diff --git a/.agents/_TOC.md b/.agents/_TOC.md deleted file mode 100644 index 4be0656b..00000000 --- a/.agents/_TOC.md +++ /dev/null @@ -1,23 +0,0 @@ -# Table of Contents - -1. [Quick Reference Card](quick-reference-card.md) -2. [JVM project requirements](jvm-project.md) โ€” language, build, and review checklist shared by all JVM repos -3. [Coding guidelines](coding-guidelines.md) -4. [Documentation & comments](documentation-guidelines.md) -5. [Documentation tasks](documentation-tasks.md) -6. [Running builds](running-builds.md) -7. [Version policy](version-policy.md) -8. [Project structure expectations](project-structure-expectations.md) -9. [Testing](testing.md) -10. [Safety rules](safety-rules.md) -11. [Advanced safety rules](advanced-safety-rules.md) -12. [Refactoring guidelines](refactoring-guidelines.md) -13. [Common tasks](common-tasks.md) -14. [Team memory](memory/MEMORY.md) -15. [Task plans](tasks/README.md) -16. [Java to Kotlin conversion](skills/java-to-kotlin/SKILL.md) -17. [Dependency update](skills/dependency-update/SKILL.md) -18. [Documentation review](skills/review-docs/SKILL.md) -19. [Pre-PR checklist](skills/pre-pr/SKILL.md) -20. [Kotlin code review](skills/kotlin-review/SKILL.md) -21. [Dependency audit](skills/dependency-audit/SKILL.md) diff --git a/.agents/advanced-safety-rules.md b/.agents/advanced-safety-rules.md deleted file mode 100644 index e4105813..00000000 --- a/.agents/advanced-safety-rules.md +++ /dev/null @@ -1,6 +0,0 @@ -# ๐Ÿšจ Advanced safety rules - -- Do **not** auto-update external dependencies without explicit request. -- Do **not** inject analytics or telemetry code. -- Flag any usage of unsafe constructs (e.g., reflection, I/O on the main thread). -- Avoid generating blocking calls inside coroutines. diff --git a/.agents/coding-guidelines.md b/.agents/coding-guidelines.md deleted file mode 100644 index 12ede97c..00000000 --- a/.agents/coding-guidelines.md +++ /dev/null @@ -1,39 +0,0 @@ -# ๐Ÿงพ Coding guidelines - -## Core principles - -- Adhere to [Spine Event Engine Documentation][spine-docs] for coding style. -- Generate code that compiles cleanly and passes static analysis. -- Respect existing architecture, naming conventions, and project structure. -- Write clear, incremental commits with descriptive messages. -- Include automated tests for any code change that alters functionality. - -## Kotlin best practices - -### โœ… Prefer -- **Kotlin idioms** over Java-style approaches: - - Extension functions - - `when` expressions - - Smart casts - - Data classes and sealed classes - - Immutable data structures -- **Simple nouns** over composite nouns (`user` > `userAccount`) -- **Generic parameters** over explicit variable types (`val list = mutableList()`) -- **Java interop annotations** only when needed (`@file:JvmName`, `@JvmStatic`) -- **Kotlin DSL** for Gradle files - -### โŒ Avoid -- Mutable data structures -- Java-style verbosity (builders with setters) -- Redundant null checks (`?.let` misuse) -- Using `!!` unless clearly justified -- Type names in variable names (`userObject`, `itemList`) -- String duplication (use constants in companion objects) -- Mixing Groovy and Kotlin DSLs in build logic -- Reflection unless specifically requested - -## Text formatting - - โœ… Replace double empty lines with a single empty line in the code. - - โœ… Remove trailing space characters in the code. - -[spine-docs]: https://github.com/SpineEventEngine/documentation/wiki diff --git a/.agents/common-tasks.md b/.agents/common-tasks.md deleted file mode 100644 index 5ee954d8..00000000 --- a/.agents/common-tasks.md +++ /dev/null @@ -1,6 +0,0 @@ -# ๐Ÿ“‹ Common tasks - -- **Adding a new dependency**: Update relevant files in `buildSrc` directory. -- **Creating a new module**: Follow existing module structure patterns. -- **Documentation**: Use KDoc style for public and internal APIs. -- **Testing**: Create comprehensive tests using Kotest assertions. diff --git a/.agents/documentation-guidelines.md b/.agents/documentation-guidelines.md deleted file mode 100644 index 6c9c1bae..00000000 --- a/.agents/documentation-guidelines.md +++ /dev/null @@ -1,14 +0,0 @@ -# Documentation & comments - -## Commenting guidelines -- Avoid inline comments in production code unless necessary. -- Inline comments are helpful in tests. -- When using TODO comments, follow the format on the [dedicated page][todo-comments]. -- File and directory names should be formatted as code. - -## Avoid widows, runts, orphans, or rivers - -Agents should **AVOID** text flow patters illustrated -on [this diagram](widow-runt-orphan.jpg). - -[todo-comments]: https://github.com/SpineEventEngine/documentation/wiki/TODO-comments diff --git a/.agents/documentation-tasks.md b/.agents/documentation-tasks.md deleted file mode 100644 index 8ac4660d..00000000 --- a/.agents/documentation-tasks.md +++ /dev/null @@ -1,20 +0,0 @@ -# ๐Ÿ“„ Documentation tasks - -1. Ensure all public and internal APIs have KDoc examples. -2. Add in-line code blocks for clarity in tests. -3. Convert inline API comments in Java to KDoc in Kotlin: - ```java - // Literal string to be inlined whenever a placeholder references a non-existent argument. - private final String missingArgumentMessage = "[MISSING ARGUMENT]"; - ``` - transforms to: - ```kotlin - /** - * Literal string to be inlined whenever a placeholder references a non-existent argument. - */ - private val missingArgumentMessage = "[MISSING ARGUMENT]" - ``` - -4. Javadoc -> KDoc conversion tasks: - - Remove `

` tags in the line with text: `"

This"` -> `"This"`. - - Replace `

` with empty line if the tag is the only text in the line. diff --git a/.agents/guidelines b/.agents/guidelines new file mode 120000 index 00000000..6f9d9663 --- /dev/null +++ b/.agents/guidelines @@ -0,0 +1 @@ +shared/guidelines \ No newline at end of file diff --git a/.agents/jvm-project.md b/.agents/jvm-project.md deleted file mode 100644 index e3c5d650..00000000 --- a/.agents/jvm-project.md +++ /dev/null @@ -1,37 +0,0 @@ -# JVM Project Requirements - -General requirements for all JVM projects in the Spine SDK organisation. -Repo-specific `project.md` files link here and add their own context. - -## Language and build - -- **Languages**: Kotlin (primary), Java (secondary). -- **Build**: Gradle with Kotlin DSL. -- **Static analysis**: detekt, ErrorProne, Checkstyle, PMD. -- **Testing**: JUnit 5, Kotest Assertions, Codecov. - -## Code review checklist - -**Correctness and safety** -- Code compiles and passes static analysis (detekt, ErrorProne, Checkstyle, PMD). -- No reflection or unsafe code unless explicitly approved in scope. -- No analytics, telemetry, or tracking code. -- No blocking calls inside coroutines. - -**Kotlin/Java style** -- Kotlin idioms preferred: extension functions, `when` expressions, data/sealed - classes, immutable data structures. -- No `!!` unless provably safe. No unchecked casts. -- No mutable state without justification. -- No string duplication โ€” use constants. - -**Tests** -- New or changed functionality must include tests. -- Use stubs, not mocks. -- Prefer [Kotest assertions][kotest-assertions] over JUnit or Google Truth. - -**Versioning** -- If the repo has `version.gradle.kts`, every PR must include a version bump. - Flag the absence as a required change. - -[kotest-assertions]: https://kotest.io/docs/assertions/assertions.html diff --git a/.agents/project-structure-expectations.md b/.agents/project-structure-expectations.md deleted file mode 100644 index 22a3ab7d..00000000 --- a/.agents/project-structure-expectations.md +++ /dev/null @@ -1,21 +0,0 @@ -# ๐Ÿ“ Project structure expectations - -```yaml -.github -buildSrc/ - - src/ - โ”œโ”€โ”€ main/ - โ”‚ โ”œโ”€โ”€ kotlin/ # Kotlin source files - โ”‚ โ””โ”€โ”€ java/ # Legacy Java code - โ”œโ”€โ”€ test/ - โ”‚ โ””โ”€โ”€ kotlin/ # Unit and integration tests - build.gradle.kts # Kotlin-based build configuration - - -build.gradle.kts # Kotlin-based build configuration -settings.gradle.kts # Project structure and settings -README.md # Project overview -AGENTS.md # Entry point for LLM agent instructions -version.gradle.kts # Declares the project version in versioned Gradle Build Tools repos. -``` diff --git a/.agents/project.md b/.agents/project.md deleted file mode 100644 index 8cf191de..00000000 --- a/.agents/project.md +++ /dev/null @@ -1,96 +0,0 @@ -# Project: spine.io - -## Overview - -This repository is the source of the public [spine.io](https://spine.io) website โ€” -the umbrella surface for the Spine SDK organisation. It serves a dual role: -a **documentation portal** (pulling the canonical content from the -[`documentation`][documentation] repository as a Hugo Module) and a -**marketing site** with landing pages, examples, and a `getting-help` page that -sells products through a Paygate-backed checkout. The site is built with -[Hugo Extended][hugo] and hosted on GitHub Pages. - -## Architecture - -**Role in the org:** application (a static website). Not a library, tool, or -Gradle plugin. The Gradle build present in the repo is a **convenience -wrapper only** โ€” no JVM compilation happens here. - -**Composition** - -- `site/` โ€” the Hugo project (config, layouts, assets, content shell). -- `config/` โ€” the only git submodule, pointing at - [`SpineEventEngine/config`][config] (shared Spine build conventions). -- `build.gradle.kts` โ€” exposes shell-backed tasks: - `:runSite`, `:buildSite`, and `:checkLinks`. - -**External pieces (consumed, not vendored)** - -- [`documentation`][documentation] โ€” pulled in as a Hugo Module; provides the - documentation content. Code snippets shown on the rendered site are - embedded *there* (via `embed-code`) before being pulled in as a module. -- [`site-commons`][site-commons] โ€” Hugo theme module shared across Spine - public sites (anchor icons, snackbars, etc.). - -**CI / deployment** (`.github/workflows/`) - -- `gh-pages.yml` โ€” builds the Hugo site and deploys to GitHub Pages on push - to the default branch. This is the deploy path; treat changes that affect - it with care. -- `check-links.yml` โ€” validates rendered-site links (mirrors the local - `checkLinks` task and the `check-links` skill). -- `gradle-wrapper-validation.yml` โ€” guards the Gradle wrapper checksum on - PRs that touch the wrapper. - -## Constraints & guardrails - -- **Hugo Extended version is pinned.** Use `v0.161.1` or higher of the - *Extended* build. A mismatched/non-extended Hugo will break the theme - pipeline and SCSS. -- **The `config/` submodule is pinned to a specific commit.** Do not bump - it outside a dedicated update task โ€” drive-by pointer changes silently - alter shared Spine build conventions. -- **Documentation content lives in the [`documentation`][documentation] - repo, not here.** Edits to documentation pages must be made there (or in - the relevant doc module). This repo only owns the site shell, landing - pages, and Spine-SDK-wide marketing surfaces. - -## Local development - -Two equivalent ways to run the site locally: - -1. Via Gradle (preferred โ€” handles working directory and Hugo flags): - ```shell - ./gradlew :runSite # build + serve on http://localhost:1313 - ./gradlew :buildSite # build only, output under site/public - ./gradlew :checkLinks # validate links against the built site - ``` -2. Directly with Hugo, from inside `site/`: - ```shell - hugo server - ``` - -If Hugo Module fetches misbehave (stale cache, theme not updating), clear -the module cache and retry: - -```shell -hugo mod clean --all -``` - -The `getting-help` page integrates with **Paygate**. In dev it points at the -staging Paygate server; test the checkout flow with -[LHV sandbox cards](https://merchant.lhv.ee/help/en/articles/12807566-test-cards). -Changes touching that page have a real third-party integration surface โ€” -verify the flow end-to-end, not just the rendered markup. - -## Authoring - -- Content authoring conventions: [`AUTHORING.md`](../AUTHORING.md). -- Embedding code samples into pages happens in the - [`documentation`][documentation] repo, not here โ€” see its - [`EMBEDDING.md`](https://github.com/SpineEventEngine/documentation/blob/master/EMBEDDING.md). - -[hugo]: https://gohugo.io/getting-started/quick-start/#step-1-install-hugo -[documentation]: https://github.com/SpineEventEngine/documentation -[site-commons]: https://github.com/SpineEventEngine/site-commons -[config]: https://github.com/SpineEventEngine/config diff --git a/.agents/project.md b/.agents/project.md new file mode 120000 index 00000000..7e0bf9bd --- /dev/null +++ b/.agents/project.md @@ -0,0 +1 @@ +../docs/project.md \ No newline at end of file diff --git a/.agents/project.template.md b/.agents/project.template.md deleted file mode 100644 index b6882e03..00000000 --- a/.agents/project.template.md +++ /dev/null @@ -1,18 +0,0 @@ - - -# Project: - -## Overview - -*One paragraph: what this repo is, what problem it solves, and its role in the -Spine SDK organisation.* - -## Architecture - -*Role in the org: library / tool / Gradle plugin / application. -Key patterns, public API boundaries, and constraints specific to this repo.* - - diff --git a/.agents/quick-reference-card.md b/.agents/quick-reference-card.md deleted file mode 100644 index 2e890e42..00000000 --- a/.agents/quick-reference-card.md +++ /dev/null @@ -1,7 +0,0 @@ -# ๐Ÿ“ Quick Reference Card - -๐Ÿšซ **Do not write to git history** (commit/push/tag/rebase/merge/cherry-pick/reset/`gh pr merge`) without explicit authorization. See -[`safety-rules.md`](safety-rules.md) โ†’ *Commits and history-writing*. -Authorization comes only from a skill's `## Commit authorization` -section or from the user's current prompt โ€” never from prior turns or -memory. diff --git a/.agents/refactoring-guidelines.md b/.agents/refactoring-guidelines.md deleted file mode 100644 index 191db49f..00000000 --- a/.agents/refactoring-guidelines.md +++ /dev/null @@ -1,3 +0,0 @@ -# โš™๏ธ Refactoring guidelines - -- Do NOT replace Kotest assertions with standard Kotlin's built-in test assertions. diff --git a/.agents/running-builds.md b/.agents/running-builds.md deleted file mode 100644 index db0338d6..00000000 --- a/.agents/running-builds.md +++ /dev/null @@ -1,18 +0,0 @@ -# Running builds - -1. When modifying code, run: - ```bash - ./gradlew build - ``` - -2. If Protobuf (`.proto`) files are modified run: - ```bash - ./gradlew clean build - ``` - -3. Documentation-only changes in Kotlin or Java sources run: - ```bash - ./gradlew dokka - ``` - -4. Documentation-only changes do not require running tests! diff --git a/.agents/safety-rules.md b/.agents/safety-rules.md deleted file mode 100644 index e7fece3c..00000000 --- a/.agents/safety-rules.md +++ /dev/null @@ -1,49 +0,0 @@ -# Safety rules - -- โœ… All code must compile and pass static analysis. -- โœ… Do not auto-update external dependencies. -- โŒ Never use reflection or unsafe code without an explicit approval. -- โŒ No analytics or telemetry code. -- โŒ No blocking calls inside coroutines. - -## Commits and history-writing - -**Default: do not write to git history.** This is a hard rule for every -agent โ€” the main thread, every subagent, every skill. It overrides any -local convenience or "the change looks done" instinct. - -The rule covers all of these operations: - -- `git commit`, `git commit-tree` -- `git push`, `git push --force` -- `git tag` -- `git rebase`, `git merge`, `git cherry-pick` against shared history -- `git reset` that discards committed work -- `gh release create`, `gh pr merge` - -Authorization to perform one of these operations exists only when **one** -of the following is true *right now*: - -1. **Skill-declared.** The currently active skill's `SKILL.md` contains - a `## Commit authorization` section that explicitly authorizes the - operation and constrains it (which files may be staged, the exact - commit subject, the maximum number of commits). The mere mention of - a commit message inside skill prose is **not** authorization โ€” the - section heading must be present. -2. **User-instructed.** The user's *current* prompt explicitly tells - the agent to perform the operation. Examples that qualify: - "commit this", "make a commit with subject X", "push the branch", - "tag this release". Authorization from previous turns, from - `CLAUDE.md`, or from any memory file does **not** carry over. - -If neither holds, the agent: - -1. Stages relevant changes with `git add` (only if helpful for review). -2. Prints the proposed commit subject (if any) and `git diff --staged`. -3. **Stops.** The user runs the commit themselves, or replies with - explicit authorization in the next prompt. - -The project's `.claude/settings.json` keeps `Bash(git commit:*)` in -`permissions.ask` as defense-in-depth, but the primary enforcement is -this rule โ€” agents must not propose commit attempts that rely on the -user clicking the prompt. diff --git a/.agents/scripts b/.agents/scripts new file mode 120000 index 00000000..96bf06e1 --- /dev/null +++ b/.agents/scripts @@ -0,0 +1 @@ +shared/scripts \ No newline at end of file diff --git a/.agents/scripts/api-discovery/.gitignore b/.agents/scripts/api-discovery/.gitignore deleted file mode 100644 index c824ff1d..00000000 --- a/.agents/scripts/api-discovery/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Per-developer override of for the api-discovery -# extraction cache. Contains an absolute path; do not commit. -.workspace-root diff --git a/.agents/scripts/api-discovery/README.md b/.agents/scripts/api-discovery/README.md deleted file mode 100644 index de4c631a..00000000 --- a/.agents/scripts/api-discovery/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# `api-discovery` scripts - -Resolve the on-disk location of a Maven artifact's source code for -agents and developers, without repeatedly `unzip`-ing JARs out of the -Gradle cache. - -The agent-facing documentation lives in -[`../../skills/api-discovery/SKILL.md`](../../skills/api-discovery/SKILL.md); -this file is the implementation reference. - -## Why - -Agents investigating library APIs used to run dozens of `find -~/.gradle/caches` + `unzip -l` + `unzip -p` calls per question. Each -`unzip` decompresses the archive from scratch โ€” slow and token-heavy. - -This package replaces that pattern with two cheap reads: - -1. **Sibling-first** โ€” every Spine artifact maps to a sibling clone - under `//`. The source tree is already on - disk; we just resolve the right submodule path. -2. **Extraction cache** โ€” non-Spine artifacts (Jackson, Guava, etc.) - have their `-sources.jar` extracted **once** to a per-workstation - cache. Subsequent queries return instantly. - -## Layout - -``` -.agents/scripts/api-discovery/ -โ”œโ”€โ”€ README.md # this file -โ”œโ”€โ”€ lib/common.sh # shared bash helpers -โ”œโ”€โ”€ discover # main entry โ€” resolve a coordinate to a path -โ”œโ”€โ”€ extract-sources # one-shot JAR extraction (race-safe) -โ”œโ”€โ”€ update-sibling # `git pull --ff-only` a stale sibling (safe-guarded) -โ””โ”€โ”€ clean-cache # prune the extraction cache -``` - -The cache itself is **not** under the repo. It lives at: - -``` -/.agents/caches/api-discovery/sources//// -``` - -`` defaults to the parent of the consumer repo (e.g. -`/Users//Projects/Spine/` when the consumer repo is -`.../Spine/config/`). To override, write the absolute path to an -alternative root into `.workspace-root` next to this README (the -script is gitignored). - -## Bootstrap - -First-time use needs the cache directory created. The scripts detect -its absence and exit `10`; the skill instructs the agent to ask the -user whether to: - -1. **Approve** the default path, -2. **Provide an alternative** workspace root, -3. **Disable** the cache for this repo (recorded in per-developer - auto-memory; sibling-first resolution still works). - -## Scripts - -### `discover` - -``` -discover :: -discover : # version pulled from buildSrc -discover # Spine-only; group + version inferred -``` - -- **stdout** โ€” absolute path to a directory you can `Grep`/`Read`. -- **stderr** โ€” `STALE` warnings when the sibling's `versionToPublish` - differs from the declared dependency version, plus other - diagnostics. Always inspect. -- **exit 0** โ€” path resolved (even if stale; the warning is on stderr). -- **exit 1** โ€” unresolvable (missing sibling AND no sources JAR). -- **exit 10** โ€” cache uninitialized; run the bootstrap flow. - -### `extract-sources` - -``` -extract-sources :: -``` - -Idempotent and race-safe. If the target directory is already populated -the script returns its path immediately. Concurrent first-time -extractions race on an atomic `mv` of a per-PID temp directory; the -loser discards its temp. - -### `update-sibling` - -``` -update-sibling # resolved under -update-sibling # acts on that path directly -``` - -Invoked by the agent (after user consent) when `discover` emits a -`STALE` warning. Safe by design: - -- Pulls **only** when the sibling is on `master` or `main` with a - clean working tree and a tracked upstream. -- On any other branch, exits `0` without touching anything โ€” the - user's "advancing multiple subprojects at once" workflow keeps - feature branches checked out as intentional staging state. -- Refuses on detached HEAD (`3`), uncommitted changes (`4`), or - missing upstream (`5`). -- Never switches branches, never `--rebase`, never `--force`. - -On success (exit `0`), the script writes a single stable token to -**stdout** that names the outcome โ€” callers should branch on the -token, not on stderr text. Failure paths produce empty stdout. - -Exit codes: - -| Code | stdout | Meaning | -|---|---|---| -| `0` | `pulled` | HEAD advanced to upstream tip | -| `0` | `up-to-date` | Already at upstream tip; nothing to do | -| `0` | `skipped-branch` | On a non-default branch; left untouched | -| `1` | _(empty)_ | Sibling not on disk | -| `2` | _(empty)_ | Not a git repository | -| `3` | _(empty)_ | Detached HEAD โ€” refused | -| `4` | _(empty)_ | Working tree dirty โ€” refused | -| `5` | _(empty)_ | No upstream tracking on default branch โ€” refused | -| `6` | _(empty)_ | `git pull --ff-only` failed (divergence, network, etc.) | -| `64` | _(empty)_ | Usage error (no/too many arguments) โ€” BSD `sysexits(3)` convention | - -### `clean-cache` - -``` -clean-cache --dry-run -clean-cache --older-than 30d [--dry-run] -clean-cache --all [--dry-run] -``` - -Manual pruning only. Nothing runs on a timer. - -## Conventions - -- **Bash 3.2 compatible** โ€” macOS ships 3.2 by default. -- **No external dependencies** beyond `bash`, coreutils, `grep`, - `sed`, `awk`, `unzip`, `find`, and `git` (used only by - `update-sibling`). -- **stdout** is always the answer; **stderr** is diagnostics. Mix - them only by piping. -- Scripts source `lib/common.sh` after setting - `SPINE_API_DISCOVERY_DIR`, so the workspace-root pointer file is - reachable. - -## Troubleshooting - -| Symptom | Likely cause | Fix | -|---|---|---| -| `cache not initialized` (exit 10) | Bootstrap not run | Follow the skill's bootstrap prompt | -| `sibling not on disk` | Spine repo not cloned | `git clone` it next to your consumer repo | -| `STALE: ...` | Sibling drifted from declared version | Run `update-sibling ` (auto-skips feature branches), or accept the warning | -| `is in the Gradle cache but publishes no -sources.jar` | Upstream artifact has no sources | Read the binary `.class` files via a different tool, or look at GitHub directly | -| `is not in the local Gradle cache` | Gradle has not fetched the dep | `./gradlew dependencies` to populate, then retry | diff --git a/.agents/scripts/api-discovery/clean-cache b/.agents/scripts/api-discovery/clean-cache deleted file mode 100755 index 30f60492..00000000 --- a/.agents/scripts/api-discovery/clean-cache +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bash -# -# Prune the workstation api-discovery extraction cache. -# -# Usage: -# clean-cache --dry-run # list what would be removed (no deletes) -# clean-cache --older-than 30d # remove entries older than 30 days -# clean-cache --older-than 7d --dry-run -# clean-cache --all # wipe the whole sources cache -# clean-cache --all --dry-run -# -# The cache is at -# `/.agents/caches/api-discovery/sources////`. -# "Age" is the directory's mtime (recorded at extraction time). -# -# Exit codes (see lib/common.sh): -# 0 โ€” succeeded (with or without removals). -# 1 โ€” bad arguments or filesystem error. -# 10 โ€” cache not initialized (nothing to clean). -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -SPINE_API_DISCOVERY_DIR="$SCRIPT_DIR" -export SPINE_API_DISCOVERY_DIR -# shellcheck source=lib/common.sh -. "$SCRIPT_DIR/lib/common.sh" - -usage() { - cat >&2 <<'EOF' -Usage: - clean-cache --dry-run - clean-cache --older-than [--dry-run] - clean-cache --all [--dry-run] - -DURATION is a `find -mtime`-style suffix: `7d`, `30d`, `90d` (days only). -EOF - exit "$EX_FAIL" -} - -mode="" -days="" -dry_run=0 - -while [ "$#" -gt 0 ]; do - case "$1" in - --dry-run) - dry_run=1 - shift - ;; - --all) - mode="all" - shift - ;; - --older-than) - mode="older-than" - shift - [ "$#" -gt 0 ] || usage - case "$1" in - *d) days="${1%d}" ;; - *) log_warn "duration must end in 'd' (days), got: $1"; usage ;; - esac - case "$days" in - ''|*[!0-9]*) log_warn "duration days must be numeric, got: $1"; usage ;; - esac - shift - ;; - -h|--help) - usage - ;; - *) - log_warn "unknown argument: $1" - usage - ;; - esac -done - -# Default to a no-op listing if no mode was given. -[ -n "$mode" ] || mode="older-than-default" - -if ! cache_initialized; then - log_warn "cache not initialized: $(cache_root)" - exit "$EX_NO_CACHE" -fi - -sources="$(sources_root)" - -# Collect targets into a temp list so we can preview and act consistently. -list_file="$(mktemp -t api-discovery-clean.XXXXXX)" -trap 'rm -f -- "$list_file"' EXIT - -case "$mode" in - all) - find "$sources" -mindepth 3 -maxdepth 3 -type d -print > "$list_file" 2>/dev/null || true - ;; - older-than) - find "$sources" -mindepth 3 -maxdepth 3 -type d -mtime "+$days" -print \ - > "$list_file" 2>/dev/null || true - ;; - older-than-default) - log_warn "no mode specified; use --all or --older-than d" - usage - ;; -esac - -count="$(wc -l < "$list_file" | tr -d '[:space:]')" - -# Prune now-empty `/` and `//` parents so the cache -# layout stays tidy. Two passes (artifact dirs first, then group dirs) so -# that emptying a group's last artifact also reclaims the group dir. -# Skipped under --dry-run so the command stays read-only. -prune_empty_parents() { - find "$sources" -mindepth 2 -maxdepth 2 -type d -empty -exec rmdir -- {} + \ - 2>/dev/null || true - find "$sources" -mindepth 1 -maxdepth 1 -type d -empty -exec rmdir -- {} + \ - 2>/dev/null || true -} - -if [ "$count" -eq 0 ]; then - log_warn "no entries match; cache untouched" - [ "$dry_run" -eq 0 ] && prune_empty_parents - exit "$EX_OK" -fi - -if [ "$dry_run" -eq 1 ]; then - log_warn "would remove $count entr$( [ "$count" -eq 1 ] && printf 'y' || printf 'ies' ):" - cat -- "$list_file" - exit "$EX_OK" -fi - -removed=0 -while IFS= read -r path; do - [ -n "$path" ] || continue - if rm -rf -- "$path"; then - removed=$((removed + 1)) - else - log_warn "failed to remove: $path" - fi -done < "$list_file" - -prune_empty_parents - -log_warn "removed $removed entr$( [ "$removed" -eq 1 ] && printf 'y' || printf 'ies' )" -exit "$EX_OK" diff --git a/.agents/scripts/api-discovery/discover b/.agents/scripts/api-discovery/discover deleted file mode 100755 index 2b69d36b..00000000 --- a/.agents/scripts/api-discovery/discover +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env bash -# -# Resolve the on-disk location of source code for a Maven artifact, so -# agents can `Grep`/`Read` it directly instead of repeatedly `unzip`ing -# sources JARs out of the Gradle cache. -# -# Strategy: -# 1. Spine artifacts (group = io.spine / io.spine.tools / etc.) are -# served from a sibling clone under `//`. The -# sibling is identified from the `github.com/SpineEventEngine/` -# URL inside the matching `buildSrc/.../dependency/local/.kt` -# file. Submodule paths are resolved by trying canonical name -# transformations (see `resolve_submodule_path`). -# 2. Anything else (and Spine fallbacks when no sibling is on disk) -# is served from the per-workstation extraction cache populated by -# `extract-sources`. -# -# Usage: -# discover :: -# discover : -# discover -# -# Output: -# stdout: absolute path to a directory containing the source tree. -# stderr: freshness warnings and explanatory diagnostics. Always -# inspect stderr before relying on the resolved path. -# -# Exit codes (see lib/common.sh): -# 0 โ€” path resolved (path on stdout). -# 1 โ€” unresolvable (sibling missing AND extraction failed). -# 10 โ€” workstation cache directory not initialized AND the query -# requires the cache (i.e. sibling-first did not succeed). -# Spine-sibling resolution never triggers EX_NO_CACHE โ€” the -# skill's "Non-cached" mode keeps working without bootstrap. -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -SPINE_API_DISCOVERY_DIR="$SCRIPT_DIR" -export SPINE_API_DISCOVERY_DIR -# shellcheck source=lib/common.sh -. "$SCRIPT_DIR/lib/common.sh" - -usage() { - cat >&2 <<'EOF' -Usage: discover - -Where is one of: - group:artifact:version e.g. io.spine:spine-base:2.0.0-SNAPSHOT.390 - group:artifact e.g. io.spine:spine-base - artifact e.g. spine-base - -Spine artifacts resolve to the local sibling clone; non-Spine -artifacts resolve to the extracted-sources cache. -EOF - exit "$EX_FAIL" -} - -[ "$#" -eq 1 ] || usage - -parse_query "$1" -group="$Q_GROUP" -artifact="$Q_ARTIFACT" -version="$Q_VERSION" - -if [ -z "$artifact" ]; then - log_warn "empty artifact in query: $1" - exit "$EX_FAIL" -fi - -# NOTE: We intentionally do NOT check `cache_initialized` here. The -# Spine-sibling path doesn't need the cache, and the skill's -# "Non-cached" bootstrap option must keep that path working without -# bootstrap. If we end up falling through to `extract-sources`, that -# script enforces the cache check on its own and raises EX_NO_CACHE. - -# Try the sibling path for Spine artifacts. If the group is empty we -# still attempt the local-deps lookup; a hit means it's a Spine artifact. -try_sibling() { - local dep_file - dep_file="$(find_local_dep_file_for_artifact "$artifact")" - [ -n "$dep_file" ] || return 1 - - local sibling_name workspace sibling_path - sibling_name="$(sibling_name_from_dep_file "$dep_file")" - workspace="$(workspace_root)" || return 1 - sibling_path="$workspace/$sibling_name" - - if [ ! -d "$sibling_path" ]; then - log_warn "sibling not on disk: $sibling_path (declared in $dep_file)" - return 1 - fi - - local module_path - module_path="$(resolve_submodule_path "$sibling_path" "$artifact")" - - if [ ! -d "$module_path" ]; then - log_warn "resolved submodule path missing: $module_path" - return 1 - fi - - # Freshness check: declared version vs sibling's published version. - local declared sibling_v - declared="${version:-$(read_declared_version "$dep_file" || true)}" - sibling_v="$(read_sibling_version "$sibling_path" 2>/dev/null || true)" - - if [ -n "$declared" ] && [ -n "$sibling_v" ] && \ - [ "$declared" != "$sibling_v" ]; then - log_warn "STALE: $artifact declared $declared in $(basename -- "$dep_file") but sibling publishes $sibling_v" - log_warn "sources at $module_path may differ from the published artifact" - fi - - printf '%s\n' "$module_path" - return 0 -} - -# Try sibling first when it could plausibly be a Spine artifact. -if [ -z "$group" ] || is_spine_group "$group"; then - if try_sibling; then - exit "$EX_OK" - fi -fi - -# Fall back to the extraction cache. This needs a full coordinate. -if [ -z "$group" ] || [ -z "$artifact" ] || [ -z "$version" ]; then - # Try to fill in the missing pieces from a local dep file. - if [ -z "$version" ]; then - dep_file="$(find_local_dep_file_for_artifact "$artifact" || true)" - if [ -n "$dep_file" ]; then - version="$(read_declared_version "$dep_file" || true)" - fi - fi - if [ -z "$group" ]; then - # The local Spine objects all use Spine.group / Spine.toolsGroup. - # Without a sibling hit we can't disambiguate; require explicit group. - log_warn "cannot resolve $artifact without a Maven group" - log_warn "retry with :[:]" - exit "$EX_FAIL" - fi - if [ -z "$version" ]; then - log_warn "cannot resolve $group:$artifact without a version" - log_warn "retry with ::" - exit "$EX_FAIL" - fi -fi - -# Delegate to extract-sources. It handles "already cached" by returning -# the path immediately, so this path is fast on repeat queries. -# The `|| exit $?` idiom propagates extract-sources' exit code under -# `set -e`; do NOT split into `target=...; status=$?` โ€” `set -e` may -# terminate the script before the status check runs. -target="$("$SCRIPT_DIR/extract-sources" "$group:$artifact:$version")" || exit $? - -printf '%s\n' "$target" diff --git a/.agents/scripts/api-discovery/extract-sources b/.agents/scripts/api-discovery/extract-sources deleted file mode 100755 index a8456680..00000000 --- a/.agents/scripts/api-discovery/extract-sources +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bash -# -# Extract a `-sources.jar` from the local Gradle cache into the workstation -# api-discovery cache. Idempotent and race-safe: a second invocation that -# observes an existing target returns immediately; concurrent first-time -# extractions race on the atomic `mv` of a per-PID temp directory. -# -# Usage: -# extract-sources :: -# -# Output: -# stdout: absolute path to the extracted source tree. -# stderr: explanatory diagnostics on cache misses or failures. -# -# Exit codes (see lib/common.sh for the shared definitions): -# 0 โ€” extraction successful (or already cached). -# 1 โ€” sources unavailable (missing JAR, no `-sources` variant, etc.). -# 10 โ€” workstation cache directory not initialized. -# -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -SPINE_API_DISCOVERY_DIR="$SCRIPT_DIR" -export SPINE_API_DISCOVERY_DIR -# shellcheck source=lib/common.sh -. "$SCRIPT_DIR/lib/common.sh" - -usage() { - cat >&2 <<'EOF' -Usage: extract-sources :: - -Extracts the matching `-sources.jar` from the local Gradle cache into -`/.agents/caches/api-discovery/sources////`. -EOF - exit "$EX_FAIL" -} - -[ "$#" -eq 1 ] || usage - -parse_query "$1" -group="$Q_GROUP" -artifact="$Q_ARTIFACT" -version="$Q_VERSION" - -if [ -z "$group" ] || [ -z "$artifact" ] || [ -z "$version" ]; then - log_warn "extract-sources requires a full coordinate ::" - exit "$EX_FAIL" -fi - -if ! cache_initialized; then - log_warn "cache not initialized: $(cache_root)" - log_warn "run the api-discovery bootstrap flow to create it" - exit "$EX_NO_CACHE" -fi - -target="$(sources_root)/$group/$artifact/$version" - -if [ -d "$target" ] && [ -n "$(ls -A -- "$target" 2>/dev/null)" ]; then - printf '%s\n' "$target" - exit "$EX_OK" -fi - -sources_jar="$(find_gradle_cache_jar "$group" "$artifact" "$version" -sources)" -if [ -z "$sources_jar" ]; then - if [ -n "$(find_gradle_cache_jar "$group" "$artifact" "$version")" ]; then - log_warn "$group:$artifact:$version is in the Gradle cache but publishes no -sources.jar" - exit "$EX_FAIL" - fi - log_warn "$group:$artifact:$version is not in the local Gradle cache" - log_warn "run './gradlew dependencies' (or rebuild) to fetch it, then retry" - exit "$EX_FAIL" -fi - -parent="$(dirname -- "$target")" -mkdir -p -- "$parent" - -# Race-safe extraction: -# 1) extract into a sibling temp dir whose name embeds our PID, -# 2) check that the final target does not yet exist (race-lost -# detection); if it does, drop our temp and use the existing tree, -# 3) otherwise `mv tmp target`. Note that on macOS/Linux, `mv` into an -# existing directory silently moves the source INSIDE it โ€” we cannot -# rely on `mv` failing when the race is lost. The pre-test catches -# the common case; step 4 catches the narrow window between test -# and mv. -# 4) post-mv, detect the rare race where `target` materialized between -# our existence test and the mv: in that case `target/` -# now exists; remove it. -tmp="${target}.tmp.$$" -trap 'rm -rf -- "$tmp"' EXIT -rm -rf -- "$tmp" -mkdir -p -- "$tmp" - -if ! unzip -q -o -- "$sources_jar" -d "$tmp"; then - log_warn "unzip failed for $sources_jar" - exit "$EX_FAIL" -fi - -if [ -e "$target" ]; then - # Another process beat us to it. Discard our temp; use theirs. - rm -rf -- "$tmp" -else - if ! mv -- "$tmp" "$target"; then - log_warn "could not move extracted sources into $target" - exit "$EX_FAIL" - fi - # Close the narrow race window: if `target` appeared between the - # existence test and the mv, `mv` silently moved `$tmp` INSIDE the - # winning extraction. Clean up the nested debris. - nested="$target/$(basename -- "$tmp")" - if [ -e "$nested" ]; then - rm -rf -- "$nested" - fi -fi -trap - EXIT - -log_warn "extracted $group:$artifact:$version -> $target" -printf '%s\n' "$target" diff --git a/.agents/scripts/api-discovery/lib/common.sh b/.agents/scripts/api-discovery/lib/common.sh deleted file mode 100644 index fa3e9c83..00000000 --- a/.agents/scripts/api-discovery/lib/common.sh +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/env bash -# -# Shared helpers for the api-discovery scripts. -# Sourced by ../discover, ../extract-sources, ../clean-cache. -# -# All functions write diagnostics to stderr; "return values" go to stdout. -# -# Conventions: -# - Bash 3.2 compatible (macOS default). -# - No external deps beyond coreutils, grep, sed, unzip. -# - `set -euo pipefail` is set by the caller, not here. - -# Exit codes used across the scripts. -readonly EX_OK=0 -readonly EX_FAIL=1 -readonly EX_NO_CACHE=10 # cache uninitialized; agent runs bootstrap - -# Resolve the consumer repository root โ€” the nearest ancestor of $PWD -# containing `buildSrc/src/main/kotlin/io/spine/dependency/`. Falls back to -# CLAUDE_PROJECT_DIR (set by Claude Code) if no such ancestor exists. -# Prints the absolute path to stdout. Exits non-zero if it cannot resolve. -consumer_repo_root() { - local dir="${1:-$PWD}" - while [ "$dir" != "/" ] && [ -n "$dir" ]; do - if [ -d "$dir/buildSrc/src/main/kotlin/io/spine/dependency" ]; then - printf '%s\n' "$dir" - return 0 - fi - dir="$(dirname -- "$dir")" - done - if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && \ - [ -d "$CLAUDE_PROJECT_DIR/buildSrc/src/main/kotlin/io/spine/dependency" ]; then - printf '%s\n' "$CLAUDE_PROJECT_DIR" - return 0 - fi - return 1 -} - -# Resolve the workspace root (parent of the consumer repo by default). -# Honors an optional pointer file at -# `/.workspace-root` containing an absolute path; used when the -# user picks "alternative root" during bootstrap. -workspace_root() { - local scripts_dir="${SPINE_API_DISCOVERY_DIR:-}" - if [ -z "$scripts_dir" ]; then - local repo - repo="$(consumer_repo_root)" || return 1 - scripts_dir="$repo/.agents/scripts/api-discovery" - fi - local pointer="$scripts_dir/.workspace-root" - if [ -f "$pointer" ]; then - # Read the first line verbatim. `IFS=` keeps internal spaces - # (paths like `/Users/me/Spine Workspace` must survive intact); - # `read -r` strips the trailing newline. Strip a stray CR for - # Windows-style line endings. - local custom="" - IFS= read -r custom < "$pointer" 2>/dev/null || true - custom="${custom%$'\r'}" - if [ -n "$custom" ] && [ -d "$custom" ]; then - printf '%s\n' "$custom" - return 0 - fi - fi - local repo - repo="$(consumer_repo_root)" || return 1 - (cd "$repo/.." && pwd) -} - -# Directory that holds the per-workstation api-discovery cache. -cache_root() { - local ws - ws="$(workspace_root)" || return 1 - printf '%s/.agents/caches/api-discovery\n' "$ws" -} - -# Subdirectory under cache_root where extracted sources live. -sources_root() { - local root - root="$(cache_root)" || return 1 - printf '%s/sources\n' "$root" -} - -# Returns 0 if the sources cache directory exists; 1 otherwise. -cache_initialized() { - local s - s="$(sources_root)" || return 1 - [ -d "$s" ] -} - -# Returns the first Gradle-cache JAR path matching the coordinates and -# optional suffix ("-sources" or empty). Empty stdout means "not found". -find_gradle_cache_jar() { - local group="$1" artifact="$2" version="$3" suffix="${4:-}" - local base="$HOME/.gradle/caches/modules-2/files-2.1/$group/$artifact/$version" - [ -d "$base" ] || return 0 - local jar - jar="$(find "$base" -maxdepth 2 -type f \ - -name "${artifact}-${version}${suffix}.jar" 2>/dev/null \ - | head -n 1)" - [ -n "$jar" ] && printf '%s\n' "$jar" - return 0 -} - -# Extract the canonical `const val version` value from a Spine local/.kt -# file. Anchors at line start (with optional access modifier) so that -# `const val version` strings inside KDoc, comments, or other quoted text -# do not match. Each local/.kt is expected to declare exactly one -# top-level `version` constant; multi-artifact files use different -# constant names (e.g. `mcVersion`) for their non-canonical versions. -read_declared_version() { - local file="$1" - [ -f "$file" ] || return 1 - sed -nE 's/^[[:space:]]*(private[[:space:]]+|internal[[:space:]]+|public[[:space:]]+|protected[[:space:]]+)?const[[:space:]]+val[[:space:]]+version[[:space:]]*=[[:space:]]*"([^"]+)".*/\2/p' \ - "$file" | head -n 1 -} - -# Read a `val [: Type] by extra("VALUE")` declaration from a file. -# Prints VALUE on stdout; empty if not found. -_read_extra_val() { - local file="$1" name="$2" - sed -nE 's/^[[:space:]]*val[[:space:]]+'"$name"'([[:space:]]*:[[:space:]]*[A-Za-z]+)?[[:space:]]+by[[:space:]]+extra\("([^"]+)"\).*/\2/p' \ - "$file" | head -n 1 -} - -# Read the sibling's "main" version from `/version.gradle.kts`. -# Tries (in order): -# 1. `versionToPublish` โ€” canonical name used by most siblings. -# 2. `Version` โ€” e.g. `mcJavaVersion` -# for sibling `mc-java`, `protoDataVersion` for `ProtoData`. -# Returns non-zero if neither is found; callers treat that as -# "freshness check unavailable". -read_sibling_version() { - local sibling="$1" - local file="$sibling/version.gradle.kts" - [ -f "$file" ] || return 1 - - local v - v="$(_read_extra_val "$file" "versionToPublish")" - if [ -n "$v" ]; then - printf '%s\n' "$v" - return 0 - fi - - local sibling_name camel - sibling_name="$(basename -- "$sibling")" - camel="$(camel_case_lower "$sibling_name")Version" - v="$(_read_extra_val "$file" "$camel")" - if [ -n "$v" ]; then - printf '%s\n' "$v" - return 0 - fi - - return 1 -} - -# Convert a PascalCase name to kebab-case. -# Examples: Base -> base; CoreJvm -> core-jvm; CoreJvmCompiler -> core-jvm-compiler. -kebab_case() { - printf '%s\n' "$1" | sed -E 's/([a-z0-9])([A-Z])/\1-\2/g; s/([A-Z]+)([A-Z][a-z])/\1-\2/g' \ - | tr '[:upper:]' '[:lower:]' -} - -# Convert a kebab-case or PascalCase name to camelCase (first letter lowercase). -# Examples: base-libraries -> baseLibraries; mc-java -> mcJava; -# core-jvm-compiler -> coreJvmCompiler; ProtoData -> protoData. -camel_case_lower() { - local input="$1" - local pascal - pascal="$(printf '%s\n' "$input" | awk -F- '{ - out="" - for (i = 1; i <= NF; i++) { - out = out toupper(substr($i, 1, 1)) substr($i, 2) - } - print out - }')" - local first rest - first="$(printf '%s' "$pascal" | cut -c1 | tr '[:upper:]' '[:lower:]')" - rest="$(printf '%s' "$pascal" | cut -c2-)" - printf '%s%s\n' "$first" "$rest" -} - -# Given a Spine local/.kt file, deduce its sibling repository name. -# Priority: -# 1. `https://github.com/SpineEventEngine/` URL inside the file. -# 2. kebab-case of the file's basename (without `.kt`). -sibling_name_from_dep_file() { - local file="$1" - [ -f "$file" ] || return 1 - local from_url - from_url="$(sed -nE 's|.*github\.com/SpineEventEngine/([A-Za-z0-9._-]+).*|\1|p' \ - "$file" | head -n 1)" - if [ -n "$from_url" ]; then - # Trim any trailing slash or punctuation. - from_url="${from_url%/}" - printf '%s\n' "$from_url" - return 0 - fi - local base - base="$(basename -- "$file" .kt)" - kebab_case "$base" -} - -# Returns 0 if the given directory contains a Kotlin/Java source set. -# Recognizes plain `src/main` and Kotlin Multiplatform names such as -# `src/commonMain`, `src/jvmMain`, `src/jsMain`, `src/nativeMain`. -has_source_set() { - local dir="$1" - [ -d "$dir/src" ] || return 1 - local candidate - for candidate in main commonMain jvmMain jsMain nativeMain; do - [ -d "$dir/src/$candidate" ] && return 0 - done - return 1 -} - -# Resolve a submodule inside a sibling that owns a given artifact. -# Tries candidate subdirectory names in order, returning the first that -# contains a recognizable source set: -# 1. Sibling root (single-module siblings such as `reflect`, `testlib`). -# 2. `/` (artifact name == submodule name). -# 3. `/` -# (`spine-base` -> `base-libraries/base`). -# 4. `/-`-stripped>` -# (`spine-protodata-backend` -> `ProtoData/backend`). -# 5. `/` -# (covers `spine-tool-base` -> `tool-base/tool-base`). -# Falls back to the sibling root when no candidate matches. -resolve_submodule_path() { - local sibling="$1" artifact="$2" - [ -d "$sibling" ] || return 1 - - if has_source_set "$sibling"; then - printf '%s\n' "$sibling" - return 0 - fi - - local sibling_name lower_sibling - sibling_name="$(basename -- "$sibling")" - lower_sibling="$(printf '%s' "$sibling_name" | tr '[:upper:]' '[:lower:]')" - - local candidates=() - candidates+=("$artifact") - - case "$artifact" in - spine-*) candidates+=("${artifact#spine-}") ;; - esac - - case "$artifact" in - spine-${lower_sibling}-*) candidates+=("${artifact#spine-${lower_sibling}-}") ;; - ${lower_sibling}-*) candidates+=("${artifact#${lower_sibling}-}") ;; - esac - - candidates+=("$sibling_name") - - local cand - for cand in "${candidates[@]}"; do - [ -n "$cand" ] || continue - if has_source_set "$sibling/$cand"; then - printf '%s/%s\n' "$sibling" "$cand" - return 0 - fi - done - - printf '%s\n' "$sibling" -} - -# Identify whether a Maven group belongs to the Spine sibling ecosystem. -# Returns 0 (true) for Spine groups, 1 (false) otherwise. -is_spine_group() { - case "$1" in - io.spine|io.spine.tools|io.spine.protodata|io.spine.validation) - return 0 - ;; - *) - return 1 - ;; - esac -} - -# Parse a query into group, artifact, version. Sets the globals -# Q_GROUP, Q_ARTIFACT, Q_VERSION. Some may be empty. -# Accepts: `group:artifact:version`, `group:artifact`, `artifact`, or a -# free-form name that we treat as either an artifact or a sibling label. -# Returns 0 always; the caller decides what an empty field means. -parse_query() { - local q="$1" - Q_GROUP=""; Q_ARTIFACT=""; Q_VERSION="" - local rest - case "$q" in - *:*:*) - Q_GROUP="${q%%:*}" - rest="${q#*:}" - Q_ARTIFACT="${rest%%:*}" - Q_VERSION="${rest#*:}" - ;; - *:*) - Q_GROUP="${q%%:*}" - Q_ARTIFACT="${q#*:}" - ;; - *) - Q_ARTIFACT="$q" - ;; - esac -} - -# Escape every non-alphanumeric character so the result is safe to embed -# in a POSIX ERE pattern. Cheap overkill โ€” Maven artifact names should -# never need most of these, but the caller's input is untrusted. -escape_ere() { - printf '%s' "$1" | sed 's/[^A-Za-z0-9]/\\&/g' -} - -# Locate the consumer repo's local/.kt file that declares a Maven artifact. -# Some local files build artifact coordinates via Kotlin string templates -# (`"$prefix-base"`, `"$group:$prefix-java:$version"`). To match those we -# expand the per-file `prefix` constant before grepping. Other template -# variables resolve to literals already present in the source, so a plain -# grep finds them. -# Returns the path of the first matching file (or empty). -find_local_dep_file_for_artifact() { - local artifact="$1" - local repo - repo="$(consumer_repo_root)" || return 1 - local local_dir="$repo/buildSrc/src/main/kotlin/io/spine/dependency/local" - [ -d "$local_dir" ] || return 0 - - # Validate the artifact name against the Maven convention before - # building a regex from it. Reject anything we cannot guarantee is - # safe; this prevents shell-quoted regex metacharacters in - # caller-supplied input from being interpreted by `grep -E`. - case "$artifact" in - ''|*[!A-Za-z0-9._-]*) - log_warn "invalid artifact name (allowed: A-Z a-z 0-9 . _ -): $artifact" - return 1 - ;; - esac - local artifact_esc - artifact_esc="$(escape_ere "$artifact")" - - local file prefix expanded - for file in "$local_dir"/*.kt; do - [ -f "$file" ] || continue - prefix="$(sed -nE 's/.*const[[:space:]]+val[[:space:]]+prefix[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p' \ - "$file" | head -1)" - if [ -n "$prefix" ]; then - expanded="$(sed -e 's|\$prefix|'"$prefix"'|g; s|\${prefix}|'"$prefix"'|g' "$file")" - else - expanded="$(cat -- "$file")" - fi - # Match the artifact as a complete coordinate component: - # delimited by `"`, `:`, or `-` on either side, never as a substring. - if printf '%s\n' "$expanded" | grep -qE "[\":-]${artifact_esc}[\":-]|[\":-]${artifact_esc}\$|^${artifact_esc}\$"; then - printf '%s\n' "$file" - return 0 - fi - done - return 0 -} - -# Emit a stderr line tagged with the scripts' identity, so the agent can -# distinguish them from unrelated noise. -log_warn() { - printf 'api-discovery: %s\n' "$*" >&2 -} diff --git a/.agents/scripts/api-discovery/update-sibling b/.agents/scripts/api-discovery/update-sibling deleted file mode 100755 index e145aaee..00000000 --- a/.agents/scripts/api-discovery/update-sibling +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash -# -# Refresh a Spine sibling repo on disk so api-discovery returns sources -# matching the most recent published version. -# -# Safe by design: -# - Pulls only when the sibling is on its default branch (master or -# main) with a clean working tree and a tracked upstream. -# - On any other branch, treats the local state as intentional (the -# user is "advancing multiple subprojects at the same time") and -# exits 0 without touching anything. -# - Refuses on detached HEAD, uncommitted changes, or missing upstream. -# - Never switches branches, never `--rebase`, never `--force`, never -# fetches a branch the user does not already track. The strictest -# action it performs is `git pull --ff-only`. -# -# This script is intended to be invoked by the api-discovery skill -# after the agent has surfaced a STALE warning to the user AND received -# explicit consent. -# -# Usage: -# update-sibling # resolved under -# update-sibling # acts on that path directly -# -# Output: -# stdout: exactly one stable token on success (`pulled`, `up-to-date`, -# or `skipped-branch` โ€” see Exit codes). Empty on any failure -# path so callers cannot misread an error as a result. -# stderr: human-facing diagnostics, including git's own pull output. -# -# Exit codes: -# 0 โ€” succeeded; stdout token names the outcome: -# `pulled` โ€” HEAD advanced to upstream tip. -# `up-to-date` โ€” already at upstream tip; nothing to do. -# `skipped-branch` โ€” on a non-default branch; left untouched. -# 1 โ€” sibling not on disk. -# 2 โ€” not a git repository. -# 3 โ€” detached HEAD; refused. -# 4 โ€” uncommitted tracked changes; refused. -# 5 โ€” default branch has no upstream tracking; refused. -# 6 โ€” `git pull --ff-only` failed (divergence, conflict, network, etc.). -# 64 โ€” usage error (no/too many args). -set -euo pipefail - -SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -SPINE_API_DISCOVERY_DIR="$SCRIPT_DIR" -export SPINE_API_DISCOVERY_DIR -# shellcheck source=lib/common.sh -. "$SCRIPT_DIR/lib/common.sh" - -# Layered on top of the shared EX_OK / EX_FAIL / EX_NO_CACHE in common.sh. -readonly EX_NOT_GIT=2 -readonly EX_DETACHED=3 -readonly EX_DIRTY=4 -readonly EX_NO_UPSTREAM=5 -readonly EX_PULL_FAILED=6 -readonly EX_USAGE=64 # BSD sysexits(3) convention. - -usage() { - cat >&2 <<'EOF' -Usage: update-sibling - -Examples: - update-sibling base-libraries - update-sibling /Users/me/Projects/Spine/validation - -Pulls only when the sibling is on its default branch (master|main) -with a clean working tree and a tracked upstream. Otherwise it leaves -the sibling alone. - -On success, prints one of: pulled | up-to-date | skipped-branch. -EOF - exit "$EX_USAGE" -} - -[ "$#" -eq 1 ] || usage -arg="$1" - -# Resolve to an absolute sibling path. Accept either a bare repo name -# (looked up under ) or an absolute path. -case "$arg" in - /*) sibling="$arg" ;; - *) - ws="$(workspace_root)" || { - log_warn "cannot resolve workspace root" - exit "$EX_FAIL" - } - sibling="$ws/$arg" - ;; -esac - -if [ ! -d "$sibling" ]; then - log_warn "sibling not on disk: $sibling" - exit "$EX_FAIL" # distinct from EX_USAGE so callers can tell - # "you passed me a bad path" apart from - # "you didn't pass me anything". -fi - -# `.git` may be a directory (normal clone) or a file (worktree pointer). -if [ ! -e "$sibling/.git" ]; then - log_warn "not a git repository: $sibling" - exit "$EX_NOT_GIT" -fi - -branch="$(git -C "$sibling" rev-parse --abbrev-ref HEAD 2>/dev/null || true)" -if [ -z "$branch" ]; then - log_warn "failed to read current branch in $sibling" - exit "$EX_FAIL" -fi -if [ "$branch" = "HEAD" ]; then - log_warn "$sibling is in detached HEAD; not pulling" - exit "$EX_DETACHED" -fi - -# Non-default branch: the user is intentionally on a feature branch. -# Use the current code as-is. -case "$branch" in - master|main) - ;; - *) - log_warn "$sibling is on '$branch' (not master/main); using local code as-is" - printf 'skipped-branch\n' - exit "$EX_OK" - ;; -esac - -# Dirty-tree guard: refuse on TRACKED modifications, since a fast-forward -# could conflict with them. Untracked files are tolerated: they don't -# conflict with HEAD movement on their own, and any genuine overwrite -# conflict (upstream adds a file whose path already exists untracked -# locally) still surfaces below as EX_PULL_FAILED via git's own check. -if [ -n "$(git -C "$sibling" status --porcelain --untracked-files=no 2>/dev/null)" ]; then - log_warn "$sibling has uncommitted tracked changes on '$branch'; not pulling" - exit "$EX_DIRTY" -fi - -# Upstream guard: --ff-only against an undefined upstream is meaningless. -if ! git -C "$sibling" rev-parse --abbrev-ref --symbolic-full-name '@{u}' \ - >/dev/null 2>&1; then - log_warn "$sibling/$branch has no upstream tracking; not pulling" - exit "$EX_NO_UPSTREAM" -fi - -# Capture HEAD before/after so we can report what changed. -before="$(git -C "$sibling" rev-parse HEAD)" -if ! git -C "$sibling" pull --ff-only >&2; then - log_warn "git pull --ff-only failed in $sibling" - exit "$EX_PULL_FAILED" -fi -after="$(git -C "$sibling" rev-parse HEAD)" - -if [ "$before" = "$after" ]; then - log_warn "$sibling already up-to-date on '$branch' ($after)" - printf 'up-to-date\n' -else - log_warn "$sibling pulled '$branch': $before -> $after" - printf 'pulled\n' -fi -exit "$EX_OK" diff --git a/.agents/scripts/pre-pr-gate.sh b/.agents/scripts/pre-pr-gate.sh deleted file mode 100755 index 88de51c2..00000000 --- a/.agents/scripts/pre-pr-gate.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -# -# PreToolUse hook: block `gh pr create` unless /pre-pr has successfully run -# for the current HEAD. The hook is intentionally unaware of the repository's -# versioning or build system; the /pre-pr skill decides which checks apply. -# -# Input: hook JSON on stdin (tool_name, tool_input.command). -# Exit: 0 to allow, 2 to block (stderr is surfaced to Claude). -# -set -eu - -command -v jq >/dev/null 2>&1 || exit 0 - -input=$(cat) -tool=$(printf '%s' "$input" | jq -r '.tool_name // empty') -[ "$tool" != "Bash" ] && exit 0 - -cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') - -# Split the command on shell separators (`;`, `&`, `|` โ€” `&&`/`||` collapse -# to repeated newlines, which is fine) and check each segment. Only block -# when a segment STARTS (after optional whitespace) with `gh pr create`. -# This avoids false positives like `echo "gh pr create"` or test fixtures -# that mention the string, while still catching `cd dir && gh pr create` -# and `cat body | gh pr create`. `tr` is used (not `sed s///`) because -# BSD `sed` on macOS does not interpret `\n` in the replacement string. -if ! printf '%s' "$cmd" \ - | tr ';&|' '\n\n\n' \ - | grep -qE '^[[:space:]]*gh[[:space:]]+pr[[:space:]]+create([[:space:]]|$)'; then - exit 0 -fi - -repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 -sentinel="$repo_root/.git/pre-pr.ok" - -block() { - cat >&2 - exit 2 -} - -if [ ! -f "$sentinel" ]; then - block </dev/null 2>&1 || exit 0 - -input=$(cat) -file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty') -command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') - -touches_version_file() { - if [ "$file" = "version.gradle.kts" ] || [ "${file%/version.gradle.kts}" != "$file" ]; then - return 0 - fi - - printf '%s\n' "$command" \ - | grep -qE '^\*\*\* (Add|Update|Delete) File: (.+/)?version\.gradle\.kts$' -} - -if touches_version_file; then - cat >&2 <<'EOF' -Direct edits to version.gradle.kts are blocked by a project hook. - -If this repository already has a root version.gradle.kts, use the bump-version -skill instead: - /bump-version [snapshot|minor|major] - -If this repository does not have a root version.gradle.kts, do not add one just -to satisfy /pre-pr; the version check is not applicable. - -See: - - .agents/version-policy.md - - .agents/skills/bump-version/SKILL.md -EOF - exit 2 -fi - -exit 0 diff --git a/.agents/scripts/publish-version-gate.sh b/.agents/scripts/publish-version-gate.sh deleted file mode 100755 index 996bd256..00000000 --- a/.agents/scripts/publish-version-gate.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash -# -# PreToolUse hook: block any `./gradlew` invocation that could publish to -# Maven Local without a version bump on the current branch. Wraps the -# Layer-1 deterministic check at `version-bumped.sh`. -# -# This is intentionally broad: it fires on `build`, `publish`, -# `publishToMavenLocal`, and any `:publish*` task. Many repos in this -# constellation chain `publishToMavenLocal` into `build` because -# integration tests consume those local artifacts, so `build` itself is -# publish-risky. False positives (blocking a pure compile) are preferable -# to overwriting a previously published snapshot that consuming repos -# rely on. -# -# Input: hook JSON on stdin (tool_name, tool_input.command). -# Exit: 0 to allow, 2 to block (stderr is surfaced to Claude). -# -set -eu - -command -v jq >/dev/null 2>&1 || exit 0 - -input=$(cat) -tool=$(printf '%s' "$input" | jq -r '.tool_name // empty') -[ "$tool" != "Bash" ] && exit 0 - -cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') - -# Split the command on shell separators (`;`, `&`, `|`) and inspect each -# segment. Only block when a segment, after optional whitespace, invokes -# `./gradlew` (or `./config/gradlew`) with a publish-risky task. Avoids -# false positives on `echo "./gradlew build"` or fixtures. -risky_segment() { - local seg="$1" - # Must start with a gradlew invocation. - printf '%s' "$seg" | grep -qE '^[[:space:]]*\.?/?(config/)?gradlew([[:space:]]|$)' || return 1 - # Must mention a publish-risky task. `build` is risky because it can - # finalize publishToMavenLocal in this config. The leading - # `(:[A-Za-z0-9_.-]+)*:?` covers qualified task paths - # (e.g. `:module:build`, `:a:b:publishToMavenLocal`) and a single - # leading-colon form (`:publishMavenJavaPublicationToMavenLocal`). - # `publish[^[:space:]]*` then catches every publish-task variant. - printf '%s' "$seg" | grep -qE '(^|[[:space:]])(:[A-Za-z0-9_.-]+)*:?(build|publish[^[:space:]]*|publishToMavenLocal|publishAllPublicationsToMavenLocal)([[:space:]]|$)' -} - -block_needed=0 -# `|| [ -n "$segment" ]` makes the loop process the final segment when the -# input has no trailing newline (which is the case for `printf '%s'`). -while IFS= read -r segment || [ -n "$segment" ]; do - if risky_segment "$segment"; then - block_needed=1 - break - fi -done < <(printf '%s' "$cmd" | tr ';&|' '\n\n\n') - -[ "$block_needed" -eq 0 ] && exit 0 - -repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 -script="$repo_root/.agents/skills/version-bumped/scripts/version-bumped.sh" - -# If the helper is missing (e.g. partial clone), don't pretend we gated. -if [ ! -x "$script" ]; then - exit 0 -fi - -# `&& rc=0 || rc=$?` captures the exit code regardless of success/failure. -# After `if cmd; then ... fi`, $? reflects the if-fi structural exit (0), -# not the failed test's exit code โ€” so we cannot use the if-fi form here. -err_file="/tmp/version-bumped.$$.err" -VERSION_BUMPED_QUIET=1 "$script" 2>"$err_file" && rc=0 || rc=$? -if [ "$rc" -eq 0 ]; then - rm -f "$err_file" - exit 0 -fi -err_payload=$(cat "$err_file" 2>/dev/null || true) -rm -f "$err_file" - -# Layer-1 returned a configuration error โ€” do not block, surface the note. -if [ "$rc" -ne 1 ]; then - printf '%s\n' "$err_payload" >&2 - exit 0 -fi - -cat >&2 </dev/null 2>&1 || exit 0 - -input=$(cat) -file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty') -command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty') - -sanitize_file() { - local path="$1" - - [ -z "$path" ] && return 0 - [ ! -f "$path" ] && return 0 - - case "$path" in - *.java|*.kt|*.kts) ;; - *) return 0 ;; - esac - - tmp=$(mktemp) - awk ' - { sub(/[ \t]+$/, "") } - /^$/ { blank++; if (blank > 1) next; print; next } - { blank = 0; print } - ' "$path" > "$tmp" && mv "$tmp" "$path" -} - -if [ -n "$file" ]; then - sanitize_file "$file" - exit 0 -fi - -printf '%s\n' "$command" \ - | sed -nE 's/^\*\*\* (Add|Update) File: (.*)$/\2/p' \ - | sort -u \ - | while IFS= read -r path; do - sanitize_file "$path" - done diff --git a/.agents/scripts/update-copyright.sh b/.agents/scripts/update-copyright.sh deleted file mode 100755 index b25282fd..00000000 --- a/.agents/scripts/update-copyright.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# -# PostToolUse hook: refresh the copyright header of source files touched by -# Edit/Write/MultiEdit. Delegates to -# .agents/skills/update-copyright/scripts/update_copyright.py, which: -# - operates only on recognized source extensions, -# - never adds a header to a file that does not already have one, -# - rewrites `today.year` to the current year per the IntelliJ profile. -# -# Input: hook JSON on stdin. Claude Code passes `tool_input.file_path`; -# Codex `apply_patch` passes the patch text in `tool_input.command`. -# Exit: 0 always (post-tool-use; never block). -# -set -u - -# Required tools โ€” silently no-op if either is missing so the hook never blocks. -command -v jq >/dev/null 2>&1 || exit 0 -command -v python3 >/dev/null 2>&1 || exit 0 - -input=$(cat) -file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true) -command=$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null || true) - -root="${CLAUDE_PROJECT_DIR:-$(pwd)}" -script="$root/.agents/skills/update-copyright/scripts/update_copyright.py" - -[ -f "$script" ] || exit 0 - -update_path() { - local path="$1" - [ -z "$path" ] && return 0 - [ ! -f "$path" ] && return 0 - python3 "$script" --root "$root" "$path" >/dev/null 2>&1 || true -} - -if [ -n "$file" ]; then - update_path "$file" - exit 0 -fi - -printf '%s\n' "$command" \ - | sed -nE 's/^\*\*\* (Add|Update) File: (.*)$/\2/p' \ - | sort -u \ - | while IFS= read -r path; do - update_path "$path" - done - -exit 0 diff --git a/.agents/shared b/.agents/shared new file mode 160000 index 00000000..f3d8ac5b --- /dev/null +++ b/.agents/shared @@ -0,0 +1 @@ +Subproject commit f3d8ac5bed56b4695dbedd8a201ad7a0b81651e6 diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 00000000..f14734dd --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +shared/skills \ No newline at end of file diff --git a/.agents/skills/api-discovery/SKILL.md b/.agents/skills/api-discovery/SKILL.md deleted file mode 100644 index b1622ffd..00000000 --- a/.agents/skills/api-discovery/SKILL.md +++ /dev/null @@ -1,288 +0,0 @@ ---- -name: api-discovery -description: > - Resolve the on-disk location of a Maven artifact's source code, - so you can `Grep`/`Read` it directly instead of running `unzip` - against JARs in the Gradle cache. Use this whenever you need to - inspect a library's API or implementation โ€” definitions of public - types, method signatures, KDoc, internal helpers, etc. ---- - -# API discovery - -Before reading library source code, run the `discover` script in -`.agents/scripts/api-discovery/`. It returns a path you can hand -straight to `Grep`, `Read`, or `Glob`. - -Do **not** run `find ~/.gradle/caches` or `unzip` against cache JARs. -Each `unzip` decompresses the archive afresh โ€” slow and token-heavy. - -## How to call it - -From the consumer repository root: - -```bash -.agents/scripts/api-discovery/discover -``` - -Where `` is one of: - -| Form | Example | Notes | -|---|---|---| -| `group:artifact:version` | `io.spine:spine-base:2.0.0-SNAPSHOT.390` | Most explicit | -| `group:artifact` | `io.spine:spine-base` | Version inferred from `buildSrc` | -| `artifact` | `spine-base` | Spine-only; group inferred from `buildSrc` | - -The script writes the absolute resolved path to **stdout**, and any -freshness/diagnostic warnings to **stderr**. Always read stderr โ€” a -silent stdout means clean resolution; a noisy stderr means caveats -the user should know about. - -## Exit codes - -| Code | Meaning | What you do | -|---|---|---| -| `0` | Path on stdout is usable. | Pass it to `Grep`/`Read`/`Glob`. If stderr is non-empty, surface the warning to the user before relying on the path. | -| `1` | Unresolvable (no sibling AND no JAR). | Report the failure. **Do not** fall back to `unzip ~/.gradle/caches/...`. | -| `10` | Cache directory not initialized. | Run the **bootstrap flow** below. | - -## Bootstrap flow (exit 10) - -On the first run in a fresh workstation the per-workstation cache -directory does not yet exist. The script exits `10` and names the -path it would create. Ask the user: - -> The shared cache directory `/.agents/caches/api-discovery/` -> does not exist yet. How would you like to proceed? -> -> 1. **Approve** โ€” create the directory at the default path. -> 2. **Alternative root** โ€” pick a different parent for the shared -> `.agents/` directory (e.g., `~/SpineWorkspace`, `/srv/spine`). -> 3. **Non-cached** โ€” skip the extraction cache. Sibling-first -> discovery still works for Spine artifacts; non-Spine deps will -> not be served by `api-discovery` in this repo. - -Then act on the user's reply: - -- **Approve** โ†’ `mkdir -p /.agents/caches/api-discovery/sources`, - then re-run the original `discover` query. -- **Alternative root** โ†’ ask for the absolute path ``, then: - ```bash - mkdir -p "/.agents/caches/api-discovery/sources" - printf '%s\n' "" \ - > .agents/scripts/api-discovery/.workspace-root - ``` - (the pointer file is gitignored). Then re-run `discover`. -- **Non-cached** โ†’ record the choice in **per-developer auto-memory** - (project memory, type `feedback`), `name: api-discovery-cache-disabled`, - describing the user's choice and giving the "How to apply" rule: - *do not invoke `extract-sources` in this repo; for non-Spine deps - fall back to other investigation tools*. Then proceed with - sibling-first only. - -Check that memory at session start. If it exists, skip cache-touching -paths entirely. - -## Workflow - -1. **Always** call `discover` before reading library source. -2. Use the returned path with `Grep`/`Read`/`Glob` directly. Do **not** - `cd` into the directory โ€” that adds path-prefix noise to tool calls - and makes line citations harder to read. -3. If stderr contains `STALE: ...`, the sibling on disk does not match - the version declared in `buildSrc`. Surface the warning AND offer - to refresh โ€” see *Refreshing a stale sibling* below. -4. If the script exits `1`, report the failure with its stderr - message and stop. Do not try `unzip` as a workaround. - -## Refreshing a stale sibling - -The user keeps siblings cloned locally as the source of truth and -sometimes works across several siblings at once with a feature branch -checked out in each. So a `STALE` line has two possible meanings, and -they require different handling: - -- **Sibling is behind `master`/`main`.** A `git pull --ff-only` will - bring it up to date. -- **Sibling is on a feature branch.** This is *intentional* โ€” the user - is staging changes across multiple subprojects. The local code is - the right code; **do not** pull. - -You cannot tell which case applies without inspecting the sibling. The -companion script `update-sibling` handles both safely: it pulls only -on the default branch with a clean tree and a tracked upstream, and -exits `0` without touching anything when on a feature branch. - -### Procedure - -When you see a `STALE: ...` line from `discover`: - -1. Surface the warning to the user. -2. Ask, in one short prompt: - > The sibling at `` is stale. Want me to try updating it? - > I'll only `git pull --ff-only` if it's on `master`/`main` with - > a clean working tree; if you have a feature branch checked out, - > I'll leave it as-is. -3. If the user agrees, run: - ```bash - .agents/scripts/api-discovery/update-sibling - ``` - `` is either the absolute path shown by - `discover` (preferred โ€” unambiguous) or just the sibling repo name - (resolved under ``). -4. Read **stdout** to decide what to do next โ€” it is a single stable - token, not free-form English: - - `pulled` โ€” HEAD advanced. Re-run `discover` so the STALE warning - clears (or, more rarely, reports a different discrepancy). - - `up-to-date` โ€” sibling was already at upstream tip. The STALE - warning is informational โ€” the declared `buildSrc` version and - the sibling's `versionToPublish` simply disagree. Proceed - without re-running `discover`. - - `skipped-branch` โ€” sibling is on a feature branch and was left - untouched. Use the local code as-is; proceed without re-running. - - stderr always carries the human-readable diagnostics; surface it - to the user, but do not parse it to drive control flow. -5. If the user declines, proceed without pulling. Do not ask again - for the same sibling in the same session unless the user revisits. - -### `update-sibling` exit codes - -Exit 0 is split into three outcomes by the **stdout token** โ€” read -that, not the stderr text. - -| Code | stdout | Meaning | What you do | -|---|---|---|---| -| `0` | `pulled` | HEAD advanced to upstream tip. | Re-run `discover` so the STALE warning clears. | -| `0` | `up-to-date` | Already at upstream tip; nothing to do. | Proceed; surface the STALE warning to the user as informational. | -| `0` | `skipped-branch` | On a non-default branch; left untouched. | Use the local code as-is; proceed without re-running. | -| `1` | _(empty)_ | Sibling not on disk. | Report the error. | -| `2` | _(empty)_ | Not a git repository. | Report the error; do not retry. | -| `3` | _(empty)_ | Detached HEAD โ€” refused. | Tell the user; do not retry. | -| `4` | _(empty)_ | Working tree dirty โ€” refused. | Tell the user; do not retry. | -| `5` | _(empty)_ | No upstream tracking on default branch โ€” refused. | Tell the user. | -| `6` | _(empty)_ | `git pull --ff-only` failed (divergence, network, etc.). | Surface the git error verbatim. | -| `64` | _(empty)_ | Usage error (no/too many arguments). | Fix the invocation; do not retry blindly. | - -Failure paths produce **empty stdout** so the agent can never misread -an error message as a result token. - -### "Don't ask me again" - -If the user says something like "stop offering" or "skip the prompt -this session", remember that for the rest of the conversation and do -not prompt on subsequent STALE warnings โ€” just surface the warning -and move on. This is **per-session** state; do not write it to -auto-memory. - -## Anti-patterns - -Stop doing these โ€” they are exactly what this skill exists to replace: - -- `find ~/.gradle/caches/modules-2/files-2.1/ -name '*-sources.jar'` -- `unzip -l ` to list classes -- `unzip -p path/in/jar` to read a file -- Any chain of `unzip` + `grep` against a Gradle-cache JAR - -If you find yourself wanting to do those, run `discover` instead. - -## Examples - -**Spine artifact, fresh sibling on disk:** - -```text -$ .agents/scripts/api-discovery/discover io.spine:spine-base -/Users//Projects/Spine/base-libraries/base -$ echo $? -0 -``` - -Tool calls then look like: - -- `Glob` pattern `**/*.kt`, path - `/Users//Projects/Spine/base-libraries/base`. -- `Grep` pattern `class Identifier`, path the same. - -**Spine artifact, stale sibling:** - -```text -$ .agents/scripts/api-discovery/discover io.spine.tools:validation-java -api-discovery: STALE: validation-java declared 2.0.0-SNAPSHOT.433 in Validation.kt but sibling publishes 2.0.0-SNAPSHOT.440 -api-discovery: sources at /Users//Projects/Spine/validation/java may differ from the published artifact -/Users//Projects/Spine/validation/java -``` - -Surface the `STALE` line, then offer to refresh โ€” see *Refreshing a -stale sibling*. After the user agrees and the pull succeeds, re-run -`discover` and the warning clears. - -**Stale sibling, refresh on master:** - -```text -$ .agents/scripts/api-discovery/update-sibling /Users//Projects/Spine/validation -Updating abc1234..def5678 -Fast-forward - ... -api-discovery: /Users//Projects/Spine/validation pulled 'master': abc1234... -> def5678... -pulled -$ echo $? -0 -``` - -Stdout is `pulled` โ€” re-run `discover` to clear the STALE warning. - -**Stale sibling, already at upstream tip:** - -```text -$ .agents/scripts/api-discovery/update-sibling /Users//Projects/Spine/validation -Already up to date. -api-discovery: /Users//Projects/Spine/validation already up-to-date on 'master' (def5678...) -up-to-date -$ echo $? -0 -``` - -Stdout is `up-to-date` โ€” the sibling is fresh; the STALE warning -reflects a declared-version vs. `versionToPublish` discrepancy that -`git pull` cannot resolve. Surface it to the user as informational. - -**Stale sibling, feature branch (no-op):** - -```text -$ .agents/scripts/api-discovery/update-sibling /Users//Projects/Spine/validation -api-discovery: /Users//Projects/Spine/validation is on 'feature/new-rule' (not master/main); using local code as-is -skipped-branch -$ echo $? -0 -``` - -Stdout is `skipped-branch` โ€” feature branch is intentional local -state. Use the code as-is. - -**Non-Spine artifact, first use (extraction):** - -```text -$ .agents/scripts/api-discovery/discover com.google.guava:guava:33.5.0-jre -api-discovery: extracted com.google.guava:guava:33.5.0-jre -> .../guava/33.5.0-jre -/Users//Projects/Spine/.agents/caches/api-discovery/sources/com.google.guava/guava/33.5.0-jre -``` - -Second call returns the same path with no stderr (already cached). - -**Unresolvable:** - -```text -$ .agents/scripts/api-discovery/discover io.spine:does-not-exist:9.9.9 -api-discovery: io.spine:does-not-exist:9.9.9 is not in the local Gradle cache -api-discovery: run './gradlew dependencies' (or rebuild) to fetch it, then retry -$ echo $? -1 -``` - -Report the failure verbatim; do not try `unzip` as a workaround. - -## Related - -- Implementation reference: `.agents/scripts/api-discovery/README.md`. -- Sibling refresh on STALE: `.agents/scripts/api-discovery/update-sibling`. -- Manual cache pruning: `.agents/scripts/api-discovery/clean-cache`. diff --git a/.agents/skills/bump-gradle/SKILL.md b/.agents/skills/bump-gradle/SKILL.md deleted file mode 100644 index 22f29578..00000000 --- a/.agents/skills/bump-gradle/SKILL.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -name: bump-gradle -description: > - Update the Gradle wrapper version used by this repository. Use when asked to - upgrade Gradle, bump the Gradle wrapper, move the project to the latest - Gradle release from the official release notes, run the Gradle build, and - commit Gradle wrapper and dependency report changes separately. ---- - -# Bump Gradle - -Use the official Gradle release notes as the source of truth for both the -latest version and the wrapper update command: - -https://docs.gradle.org/current/release-notes.html#upgrade-instructions - -Always check that page at task time. Do not rely on remembered Gradle versions. - -## Commit authorization - -This skill is authorized to run `git commit` **up to two times** per -invocation, under these constraints: - -1. **Gradle wrapper commit.** Stage only the Gradle wrapper files - (`gradle/wrapper/gradle-wrapper.properties`, - `gradle/wrapper/gradle-wrapper.jar`, `gradlew`, `gradlew.bat`, plus - files directly required by the wrapper update). Subject: - `` Bump Gradle -> `GRADLE_VERSION` `` with the actual version - substituted. Skip if no wrapper-owned file changed. - -2. **Dependency-report commit** (separate from the wrapper commit). Stage - only generated dependency-report files (`docs/dependencies/pom.xml`, - `docs/dependencies/dependencies.md`). Subject: - `Update dependency reports`. Skip if the build did not regenerate - those files. - -No `git push`, `git tag`, `git rebase`, `git commit --amend`, or any other -history-writing operation. Those require a separate authorization -(`.agents/safety-rules.md` โ†’ *Commits and history-writing*). Do not create -empty commits, and do not bundle unrelated changes into either commit. - -## Checklist - -1. Work from the target repository root. - - Confirm `./gradlew` and `gradle/wrapper/gradle-wrapper.properties` exist - before changing anything. Inspect `git status --short` and preserve unrelated - user changes. If Gradle wrapper files are already modified, inspect the diff - and continue only when those edits are part of the same requested Gradle - bump; otherwise ask before overwriting or staging them. - -2. Read the latest Gradle version from the release notes. - - Open the Upgrade instructions section at the URL above. Use the version in - the release heading and the wrapper command shown there. They should agree; - if they do not, stop and report the mismatch. - -3. Run the wrapper update command. - - Substitute the version from the release notes: - - ```bash - ./gradlew wrapper --gradle-version=GRADLE_VERSION && ./gradlew wrapper - ``` - - For example, if the release notes say Gradle `9.5.1`, run: - - ```bash - ./gradlew wrapper --gradle-version=9.5.1 && ./gradlew wrapper - ``` - -4. Run the build. - - ```bash - ./gradlew clean build - ``` - - If the wrapper update or build fails, do not commit partial changes. Report - the failing command and the relevant error output. - -5. Commit only Gradle-related files. - - Inspect `git status --short` and `git diff --name-only`. Stage only files - created or updated by the Gradle wrapper bump, normally: - - ```text - gradle/wrapper/gradle-wrapper.properties - gradle/wrapper/gradle-wrapper.jar - gradlew - gradlew.bat - ``` - - Include other Gradle-owned files only when they are directly required by the - wrapper update and are clearly part of the same change. Do not stage - dependency reports or unrelated build output in this commit. - - Commit with the exact subject, replacing `GRADLE_VERSION`: - - ```text - Bump Gradle -> `GRADLE_VERSION` - ``` - - Example: - - ```bash - git commit -m 'Bump Gradle -> `9.5.1`' - ``` - - If no Gradle-related files changed, do not create an empty commit; report - that the wrapper was already current after verification. - -6. Commit dependency reports separately when the build updates them. - - Stage only generated dependency report files. In repositories using this - config, the usual paths are: - - ```text - docs/dependencies/pom.xml - docs/dependencies/dependencies.md - ``` - - Include other changed files only when they are clearly generated dependency - reports from the build. Commit them separately with: - - ```text - Update dependency reports - ``` - -7. Verify the final branch state. - - Confirm the recent commit subjects and make sure no owned Gradle bump or - dependency report changes remain unstaged: - - ```bash - git log --format=%s -2 - git status --short - ``` - - Leave unrelated pre-existing user changes alone and mention them separately - in the final response. - -8. Ensure `version.gradle.kts` is bumped. - - Before this branch can be built or published locally, the project - version must be strictly greater than the version on the base ref. - Invoke `/version-bumped` โ€” it is a no-op if a bump has already - happened earlier on the branch, and otherwise calls `/bump-version` - to perform the increment. diff --git a/.agents/skills/bump-gradle/agents/openai.yaml b/.agents/skills/bump-gradle/agents/openai.yaml deleted file mode 100644 index 6edf9787..00000000 --- a/.agents/skills/bump-gradle/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Bump Gradle" - short_description: "Update the Gradle wrapper safely" - default_prompt: "Use $bump-gradle to update this repository to the latest Gradle wrapper version from the official release notes, build, and split Gradle/report commits." diff --git a/.agents/skills/bump-version/SKILL.md b/.agents/skills/bump-version/SKILL.md deleted file mode 100644 index 3e1d3d65..00000000 --- a/.agents/skills/bump-version/SKILL.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -name: bump-version -description: > - Bump the project version in `version.gradle.kts` following the Spine SDK - versioning policy. Use when starting a new branch, before opening a PR, or - when CI rejects a branch for a missing/insufficient version increment. Covers - locating the published version value, choosing the increment, committing the - bump, rebuilding reports, and resolving version conflicts. ---- - -# Bump the project version - -The authoritative policy is [Spine SDK Versioning][version-policy]. In this -skill's target repository, CI runs the `Version Guard` workflow, which invokes -`checkVersionIncrement` through `IncrementGuard`. The task fails if the current -project version already exists in the Maven repository. It does not compare git -branches or inspect commit subjects; the checks below are agent-side guardrails. - -## Commit authorization - -This skill is authorized to run `git commit` **exactly once** per invocation, -under these constraints: - -- Stage only `version.gradle.kts`. Any other modified files are out of scope - for this skill's commit and must remain unstaged. -- Use the exact subject `` Bump version -> `` `` (see step 4 of the - Checklist) with the actual new version value substituted. -- No `git push`, `git tag`, `git rebase`, `git commit --amend`, or any other - history-writing operation. Those require a separate authorization - (`.agents/safety-rules.md` โ†’ *Commits and history-writing*). - -If the bump cannot be performed cleanly (no diff to commit, conflicting -staged files, build failures preceding the commit), report and stop โ€” do not -create the commit. - -## Checklist - -1. Work from the target repository root. - - Confirm `version.gradle.kts` exists before editing. If it is absent, stop and - report that this skill does not apply to the current checkout. - - Inspect `git status --short` before changing files. Preserve unrelated user - changes and stage only the version/report files this workflow owns. - -2. Locate `version.gradle.kts` and update the value that feeds - `versionToPublish`. - - The published version may be a literal: - - ```kotlin - val versionToPublish: String by extra("2.0.0-SNAPSHOT.182") - ``` - - Or it may come from another variable: - - ```kotlin - val compilerVersion: String by extra("2.0.0-SNAPSHOT.043") - val versionToPublish by extra(compilerVersion) - ``` - - In the second case, update the source value (`compilerVersion` here), not - only the `versionToPublish` alias. - -3. Choose the increment. - - For the normal snapshot-line PR, increment the trailing snapshot number by - one: `2.0.0-SNAPSHOT.182` -> `2.0.0-SNAPSHOT.183`. Preserve existing - zero-padding: `2.0.0-SNAPSHOT.009` -> `2.0.0-SNAPSHOT.010`. - - For a breaking snapshot-line PR, advance to the next multiple of 10 that is - strictly greater than the current value: `.187` -> `.190`, and `.180` -> - `.190`. - - For release-line work, follow the [policy][version-policy]: urgent fixes bump `PATCH`; - feature work or significant fixes bump `MINOR` and reset `PATCH` to `0`. - -4. Commit only the `version.gradle.kts` change with this subject: - - ```text - Bump version -> `2.0.0-SNAPSHOT.183` - ``` - - Use the actual new version in the subject. Do not include unrelated files in - this commit. - -5. Run the build to verify the bump and regenerate reports: - - ```bash - ./gradlew clean build - ``` - - Repos using this config commonly finalize `generatePom` and - `mergeAllLicenseReports` after `build`, which updates - `docs/dependencies/pom.xml` and `docs/dependencies/dependencies.md` when - those reports are configured. - -6. If `docs/dependencies/pom.xml` or `docs/dependencies/dependencies.md` changed, - commit those generated files separately: - - ```text - Update dependency reports - ``` - - If the PR has the `License Reports` workflow, make sure the branch modifies - `docs/dependencies/pom.xml` and `docs/dependencies/dependencies.md`. - -7. Validate the branch state. - - ```bash - BASE=master - git fetch --quiet origin "$BASE" - RANGE="$(git merge-base HEAD origin/$BASE)..HEAD" - git log --format=%s "$RANGE" | grep '^Bump version ->' - git diff --name-only "$RANGE" -- version.gradle.kts | grep '^version.gradle.kts$' - ``` - - Use the actual merge target for `BASE` when it is not `master`. - Also confirm `git status --short` has no uncommitted changes created by the - version bump or report regeneration. - -## Conflict Rule - -When merging a base branch into a feature branch: - -- If the base branch version is lower, keep the feature branch version. -- If the base branch version is greater than or equal to the feature branch - version, set the feature branch version to `base + 1`, or apply the breaking - change rounding rule. - -Do not require a completely clean worktree if unrelated user changes are -present. Instead, make sure no uncommitted changes were created by the version -bump or report regeneration. - -[version-policy]: https://github.com/SpineEventEngine/documentation/wiki/Versioning diff --git a/.agents/skills/bump-version/agents/openai.yaml b/.agents/skills/bump-version/agents/openai.yaml deleted file mode 100644 index 12f6e4f9..00000000 --- a/.agents/skills/bump-version/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Bump Version" - short_description: "Bump Spine project versions safely" - default_prompt: "Use $bump-version to bump the project version in version.gradle.kts, commit the version change, rebuild dependency reports, and verify the branch." diff --git a/.agents/skills/check-links/SKILL.md b/.agents/skills/check-links/SKILL.md deleted file mode 100644 index a4c61a0c..00000000 --- a/.agents/skills/check-links/SKILL.md +++ /dev/null @@ -1,325 +0,0 @@ ---- -name: check-links -description: > - Validate the Hugo documentation site under `docs/` or `site/` for broken - links. Builds the site, starts the Hugo server locally, runs Lychee against - the rendered HTML using the repo's `lychee.toml`, and reports any broken URLs - grouped by source Markdown page. Use locally before pushing changes that - touch `docs/**` or `site/**`, when CI's `Check Links` job fails, or whenever - the user asks to "check doc links". Read-only with respect to the project - sources. Does **not** cover Javadoc/KDoc (out of scope for this skill). ---- - -# Check links in the Hugo docs (repo-specific) - -You are the documentation link checker for this Spine Event Engine project. -You build the site under `docs/` or `site/` (auto-detected; see step 0), serve -it locally on port `1414`, run Lychee against the rendered HTML, and report -broken URLs. You mirror what the `.github/workflows/check-links.yml` workflow -does in CI: same Hugo version, same Lychee version, same Hugo environment -(`development`), and the same `lychee.toml`. Two deliberate differences remain: -the skill serves on port `1414` (CI uses `1313`) to avoid clashing with a -developer's local `hugo server`, and the skill writes a local sentinel that CI -does not. Both differences are harmless because `--base-url` is rewritten to -match the local port and the sentinel is consumed only by the local `pre-pr` -skill. - -### Pinned versions - -`.github/workflows/check-links.yml` is the **single source of truth** for the -Hugo and Lychee pins. This file does not duplicate the current values -because duplicates inevitably drift; see the workflow's `env:` block for -the canonical `HUGO_VERSION` and `LYCHEE_VERSION_TAG`. The auto-download -step (ยง2) reads `LYCHEE_VERSION_TAG` out of the workflow at runtime, so a -workflow bump propagates automatically. Hugo is not auto-installed; the -skill uses whichever `hugo` is on `$PATH` and only warns (does not block) -if the installed version is older than the workflow's `HUGO_VERSION` โ€” -Hugo's HTML output is stable enough across minor versions that a small -skew does not invalidate link-check results. - -The authoritative shared config is `lychee.toml` at the repo root. Do not -fork its exclude list โ€” fix the source link or, if the failing URL is a known -flaky external endpoint, add it to `lychee.toml` once (the change applies to -both the skill and CI). - -## When to run - -- Any change touches `docs/**` or `site/**` (including reference links, - `embed-code` blocks, sidenav YAML files, content under `/content/`). -- A change touches `lychee.toml` itself. -- CI reported broken links and you want a fast local repro. -- The user asks to "check the doc links" or invokes `/check-links`. - -If none of the above is true, decline with a one-line note rather than -running the (~30 s) build+check. - -## Tooling - -The skill needs four binaries: - -| Tool | Purpose | Install hint | -|--------|------------------------------------------|-------------------------------| -| Hugo | Build and serve the site | `brew install hugo` (extended)| -| Node | Hugo theme dependencies (`npm ci`) | `brew install node` | -| npm | Same | bundled with Node | -| Lychee | Link checker | `brew install lychee` | - -For **Lychee**, prefer a pre-installed binary on `$PATH`. If none is found, -download the pinned release (see `LYCHEE_VERSION_TAG` in -`.github/workflows/check-links.yml` โ€” the dynamic-read pattern in step 2 below -keeps this version in lock-step with CI) into -`.agents/skills/check-links/.cache/lychee/` and use that path. The pinned -version matches what the CI workflow uses, so behavior is identical. - -`.agents/skills/check-links/.cache/` is git-ignored (see `.gitignore`). - -## Procedure - -Execute the steps in order. On the first failure, stop, write a `FAIL` -sentinel (step 8), and report the failure with the next action. - -### 0. Detect site root and work directory - -Before any other step, determine `SITE_DIR` (the Hugo site root) and `WORK_DIR` -(the directory where `npm ci` / `hugo` commands run โ€” mirrors `.github/workflows/check-links.yml`): - -```bash -SITE_DIR="" -for dir in docs site; do - for cfg in hugo.toml hugo.yaml \ - config/hugo.toml config/hugo.yaml \ - config/_default/hugo.toml config/_default/hugo.yaml; do - if [ -f "$dir/$cfg" ]; then - SITE_DIR="$dir" - break 2 - fi - done -done -if [ -z "$SITE_DIR" ]; then - echo "ERROR: No Hugo config found under docs/ or site/." >&2 - exit 1 -fi - -if [ -f "${SITE_DIR}/_preview/package-lock.json" ]; then - WORK_DIR="${SITE_DIR}/_preview" -elif [ -f "${SITE_DIR}/package-lock.json" ]; then - WORK_DIR="${SITE_DIR}" -else - echo "ERROR: No package-lock.json found under ${SITE_DIR}/_preview/ or ${SITE_DIR}/." >&2 - exit 1 -fi -``` - -Use `$SITE_DIR` for content paths and `$WORK_DIR` for build/serve operations in the steps below. - -### 1. Scope check - -Run `git diff ...HEAD --name-only` (default `` = `master` unless -the user provides another). If the change set has **no** files under -`$SITE_DIR/**` and no changes to `lychee.toml`, and the user did not -explicitly ask, decline and exit cleanly. - -### 2. Preflight binaries - -- `hugo version` โ†’ must succeed; capture the version. If missing, stop with - Must-fix: "Install Hugo extended (`brew install hugo`)." If installed but - older than the workflow's `HUGO_VERSION` (parse with - `grep -E '^[[:space:]]+HUGO_VERSION:' .github/workflows/check-links.yml | sed -E 's/.*: *"?([^"]+)"?$/\1/'`), warn but - continue. -- `node -v` and `npm -v` โ†’ must succeed. If missing, stop with Must-fix: - "Install Node (`brew install node`) at the major version pinned by - `node-version:` in `.github/workflows/check-links.yml`." -- `lychee --version` โ†’ if it succeeds, record the path and version. -- If `lychee` is missing: - 1. Read the canonical pin from the workflow file so the skill cannot drift - from CI: - ```bash - LYCHEE_VERSION_TAG=$( - grep -E '^[[:space:]]+LYCHEE_VERSION_TAG:' .github/workflows/check-links.yml \ - | sed -E 's/.*: *"?([^"]+)"?$/\1/' - ) - ``` - Expected shape: `lychee-vX.Y.Z` (the leading `lychee-` is part of the - upstream release tag, not a typo). - 2. Determine platform via `uname -s` / `uname -m`. Map to the matching - Lychee asset (recent releases โ€” `v0.24.2` and later โ€” drop the - version from the asset filename): - - `Darwin` + `arm64` โ†’ `lychee-aarch64-apple-darwin.tar.gz` - - `Darwin` + `x86_64` โ†’ `lychee-x86_64-apple-darwin.tar.gz` - - `Linux` + `x86_64` โ†’ `lychee-x86_64-unknown-linux-gnu.tar.gz` - - `Linux` + `aarch64` โ†’ `lychee-aarch64-unknown-linux-gnu.tar.gz` - - any other combination (e.g. Windows, FreeBSD, 32-bit) โ†’ stop with - Must-fix: "Unsupported platform for Lychee auto-download โ€” install - Lychee manually (`brew install lychee` / `cargo install lychee`) - and rerun." - 3. Ensure the cache directory exists *before* the download โ€” - `mkdir -p .agents/skills/check-links/.cache/lychee/` โ€” - because the path is git-ignored and absent on a fresh clone, - and `tar -xzf โ€ฆ -C

` will fail with "no such file or - directory" if the target does not exist yet. This mirrors the - `mkdir -p lychee` that `check-links.yml` does before its own - extract step. - 4. Download from - `https://github.com/lycheeverse/lychee/releases/download/${LYCHEE_VERSION_TAG}/` - into `.agents/skills/check-links/.cache/lychee/` and extract - with `tar -xzf --strip-components=1 -C .agents/skills/check-links/.cache/lychee/` - so the binary lands at - `.agents/skills/check-links/.cache/lychee/lychee`. - 5. Use `.agents/skills/check-links/.cache/lychee/lychee` for the rest of this run. - 6. Print a one-line note: "Using auto-downloaded Lychee. For faster runs, - install with `brew install lychee`." - -### 3. Install Hugo deps - -Run `( cd ${WORK_DIR} && npm ci )`. We deliberately use `npm ci` -(matching the CI workflow's `Install Dependencies` step in `check-links.yml`) -rather than `npm install`: - -- `npm ci` installs exactly the versions pinned by `package-lock.json`; - `npm install` is allowed to update the lockfile and may resolve to - different transitive versions than CI, which defeats the "render - identical HTML to CI" goal. -- If `package.json` and `package-lock.json` drift out of sync, `npm ci` - fails fast with a clear error rather than silently healing the - lockfile โ€” a divergence we want to surface, not paper over. - -### 4. Build the site - -Run `( cd ${WORK_DIR} && hugo -e development )`. -This emits `${WORK_DIR}/public/**/*.html`. The `-e development` flag matches -what CI uses in `check-links.yml` so the two builds render identical HTML. -(The helper `${SITE_DIR}/_script/hugo-build` exists for interactive use but -defaults to `production`; we invoke `hugo` directly to keep the env in -lock-step with CI.) - -### 5. Start the Hugo server in the background - -The server must survive across multiple `Bash` tool calls (steps 5 โ†’ 6 โ†’ 8 -typically run in separate shells), so we rely on `nohup` alone โ€” a `trap โ€ฆ -EXIT` would fire when *this* shell exits and kill the server before Lychee -can query it. Teardown happens explicitly in step 8. - -Before launching, kill any leftover server from a previous crashed run so a -stale process does not hold port `1414`: - -```bash -pkill -F /tmp/check-links.hugo.pid 2>/dev/null || true -rm -f /tmp/check-links.hugo.pid - -( cd ${WORK_DIR} && nohup hugo server --environment development --port 1414 \ - > /tmp/check-links.hugo.out 2>&1 & echo $! > /tmp/check-links.hugo.pid ) -sleep 5 - -# Verify the captured PID is alive before relying on it. `$!` for -# `nohup foo &` is reliable on bash but not portable across shells; the -# pgrep check turns a silent "Lychee fetches an empty port" failure into -# a clear error. -if ! pgrep -F /tmp/check-links.hugo.pid > /dev/null 2>&1; then - echo "ERROR: Hugo server failed to start. Tail of log:" >&2 - tail -20 /tmp/check-links.hugo.out >&2 || true - exit 1 -fi -``` - -Port `1414` is chosen to avoid clashing with a developer's local `hugo server` -(default `1313`). The `--environment development` flag matches CI's build env. - -### 6. Run Lychee - -```bash - --config lychee.toml --timeout 60 \ - --base-url http://localhost:1414/ \ - "${WORK_DIR}/public/**/*.html" -``` - -Capture exit code. Any non-zero exit means at least one broken link. - -### 7. Report - -Group the broken URLs from Lychee's output by source page. To reverse-map -an HTML path to its Markdown source: - -`${WORK_DIR}/public/docs/
//index.html` -โ†” `${SITE_DIR}/content/docs/
/.md` (or `/_index.md`). - -Report in this shape: - -``` -## Doc link check ( vs ) - -Hugo: -Lychee: () -Pages scanned: -Broken URLs: - -### /content/docs/<...>/.md -- โ€” -- โ€” ... - -### /content/docs/<...>/.md -- ... -``` - -If `K == 0`, report a single line: "All links OK." - -### 8. Tear down and sentinel - -- Kill the Hugo server (and clean up its pid file): - - ```bash - pkill -F /tmp/check-links.hugo.pid 2>/dev/null || true - rm -f /tmp/check-links.hugo.pid /tmp/check-links.hugo.out - ``` - - Run this even if Lychee failed โ€” leaving a server on port `1414` would - poison the next invocation. -- Write `.git/check-links.ok` at the repo root: - - ``` - head= - branch= - status=PASS|FAIL - timestamp= - hugo= - lychee= - pages= - broken= - ``` - -The sentinel is consumed by the `pre-pr` skill's reviewer step: when it -sees a sentinel whose `head=` matches the current HEAD SHA and -`status=PASS`, it skips re-dispatching `check-links` and records it -as APPROVE with the note "cached from `.git/check-links.ok`". Any -HEAD advance (commit, amend, rebase) invalidates the cache automatically. - -## Notes - -- This skill does **not** modify tracked sources. It does, however, write - several git-ignored build artifacts during a run โ€” listed here so a future - reader does not mistake them for unrelated side-effects: - - `.agents/skills/check-links/.cache/lychee/` โ€” auto-downloaded - Lychee binary, when the system Lychee was unavailable. - - `${WORK_DIR}/node_modules/` โ€” installed by `npm ci` in step 3. - - `${WORK_DIR}/public/` โ€” Hugo's rendered HTML (the corpus Lychee scans). - - `${WORK_DIR}/resources/` โ€” Hugo's asset-pipeline cache. - - `.lycheecache` at the repo root โ€” Lychee's per-URL result cache - (honoured for `max_cache_age = "3d"` per `lychee.toml`). - - `/tmp/check-links.hugo.{pid,out}` โ€” server PID file and log, both - removed in step 8's teardown. - - Every path above is matched by an existing `.gitignore` entry; none is - committed. -- The `lychee.toml` exclude list is the single source of truth for flaky - external endpoints. If a real link must be excluded, add it there and - explain why in a comment so CI and local runs stay in sync. -- The skill assumes the docs build succeeds. A Hugo build error is treated - the same as a link failure โ€” surface it and stop. -- The `include_verbatim = false` setting in `lychee.toml` skips links inside - code blocks. That is intentional today; flip it on if you specifically need - to validate examples. - -## Related skills - -- `review-docs` โ€” prose, KDoc/Javadoc, and Markdown style review. Runs in - parallel with `check-links` when invoked by `pre-pr`. -- `pre-pr` โ€” composes the above and gates `gh pr create`. diff --git a/.agents/skills/dependency-audit/SKILL.md b/.agents/skills/dependency-audit/SKILL.md deleted file mode 100644 index 010c16bc..00000000 --- a/.agents/skills/dependency-audit/SKILL.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -name: dependency-audit -description: > - Audit changes to dependency declarations under - `buildSrc/src/main/kotlin/io/spine/dependency/` โ€” catches accidental - version downgrades, BOM mismatches, missing deprecation markers when - artifacts are renamed or removed, copyright drift, and convention drift. - Use whenever a diff touches that directory, or when asked to "audit - this dependency bump". Read-only; does not run builds. ---- - -# Dependency audit (repo-specific) - -You are the dependency auditor for a Spine Event Engine repo. All managed -dependencies live under: - - buildSrc/src/main/kotlin/io/spine/dependency/ - -organized by sub-package: - -- `lib/` โ€” third-party runtime libraries (Kotlin, Guava, Protobuf, gRPC, โ€ฆ). -- `local/` โ€” Spine SDK artifacts (Base, CoreJvm, ModelCompiler, โ€ฆ). -- `test/` โ€” testing libraries (JUnit, Kotest, AssertK, Truth, Jacoco, Kover). -- `build/` โ€” static-analysis and build-time tools (Dokka, ErrorProne, Pmd, - CheckStyle, KSP, โ€ฆ). -- `kotlinx/` โ€” Kotlin-ecosystem libraries (Coroutines, Serialization, - DateTime, AtomicFu). -- `boms/` โ€” BOM declarations. - -Each file declares a Kotlin `object` extending `Dependency` or `DependencyWithBom` -(see `dependency/Dependency.kt`). The shape is: - - object Kotest { - const val version = "6.1.11" - const val group = "io.kotest" - const val assertions = "$group:kotest-assertions-core:$version" - // โ€ฆ - } - -## How to run an audit - -1. **Fetch the full diff once.** Default base is `origin/master`: - `git diff origin/master...HEAD -- 'buildSrc/src/main/kotlin/io/spine/dependency/**'` - (use `--staged` if the user is mid-commit, or a different base only if - the user names one). The unified diff already contains the old and new - lines you need for version-sanity and BOM checks โ€” do not call `--stat` - first and then re-read each file. If the diff is empty, ask the user - which files to audit. - -2. **Lean on the diff; `Read` on demand.** Version, BOM, copyright, and - deprecation deltas are all visible in the unified diff. Only `Read` a - file when (a) it is newly added, or (b) a hunk references a - `version`/`group` constant defined outside the hunk and you need - surrounding context. **Budget:** if more than 5 files changed, do not - `Read` individual files โ€” work from the diff and use targeted `Grep` - for cross-cutting questions. - -3. **Batch independent work into one turn.** Issue the version-sanity (A), - convention-drift (D), and cross-cutting (E) tool calls *in parallel* - within a single response. Collect every finding and emit the report - once โ€” **do not stop at the first failure**. - -4. **Batch greps.** For deprecation/caller checks (C) and snapshot-pin - checks (A), build one ripgrep over the union of symbols instead of one - command per symbol. Examples: - - `rg -n '\b(name1|name2|name3)\b' --type kt` to find callers of any - removed `const val`. - - `rg -L 'Copyright \(c\) 2026' ` to flag every stale - header in one call. - - `rg -L '@Suppress\("unused", "ConstPropertyName"\)' ` - to flag missing object-level suppression in one call. - - `rg -n '(lib1:oldv1|lib2:oldv2)' --type kt --type gradle` โ€” one - alternation across libraries, not one command per library. - -5. **Fast path for pure version bumps.** If every hunk only modifies an - existing `version` (or `bom`) string literal โ€” no added/removed - `const val`, no new files, no renames โ€” run only Checks A and D. - Skip B, C, and E entirely. This is the dominant `/dependency-update` - shape; do not waste tool calls re-validating naming or deprecation - discipline when nothing structural changed. - -## Checks - -### A. Version sanity -- **No silent downgrade.** Compare the old and new `version` value as semver. - A decrease (`2.0.0 -> 1.9.0`) or a snapshot regression (`-SNAPSHOT.183` -> - `.182`) is a Must-fix unless the commit message explicitly justifies it. -- **Snapshot vs. release consistency.** If `version` switches from a release - (`2.0.0`) to a snapshot (`2.0.1-SNAPSHOT.001`), confirm the consuming code - isn't pinned to the release elsewhere. Use the batched ripgrep recipe - in step 4 โ€” one alternation across all switched libraries, not one - command per library. -- **BOM โ†” component agreement.** For objects extending `DependencyWithBom`, - check that `bom` references the same version as `version` (e.g. Kotlin's - `kotlin-bom:$runtimeVersion`). - -### B. Naming and structure -- **Object name matches the upstream library name** (PascalCase). New files - must follow the convention of neighbors (e.g. `lib/Foo.kt` declares - `object Foo`). -- **No type names in property names** (`fooList`, `barObject`) โ€” this is in - `.agents/coding-guidelines.md`. -- **Module constants use `"$group::$version"`**, not hardcoded - Maven coordinates. Catch copy-paste like `"io.kotest:kotest-assertions-core:6.1.11"`. - -### C. Deprecation discipline -When an artifact is **renamed or removed**: -- The old `const val` must stay with `@Deprecated("โ€ฆ", ReplaceWith("โ€ฆ"))` - or `@Deprecated("โ€ฆ")` (see `Kotest.frameworkApi` and `Kotest.datatest` for - the established style). -- If the diff deletes one or more `const val`s outright, confirm no caller - is left behind. Use the batched ripgrep recipe in step 4 โ€” one - alternation over all removed symbol names, not one `git grep` per - name. If any caller survives, this is a Must-fix. - -### D. Convention drift -- **Copyright header year.** Every changed file should have a current-year - copyright line. If a file was edited but its copyright says `2024`, flag it - (the user can run `/update-copyright` to fix). -- **GitHub URL comment.** New `lib/` and `kotlinx/` files conventionally - start with `// https://github.com//` above the object. - Recommend it if missing. -- **`@Suppress("unused", "ConstPropertyName")` on the object.** This is the - established style for constant-heavy declarations. - -### E. Cross-cutting checks -- **`local/` deps don't leak.** Spine SDK artifacts in `local/` should not be - declared in `lib/` or `test/` (and vice versa). -- **No mixing Groovy and Kotlin DSL.** All Gradle code in `buildSrc/` must be - `.kt` or `.gradle.kts`. Catch any `.gradle` file slipping in. - -## Output format - -Three sections, in this order: - -- **Must fix** โ€” version downgrades, missing deprecation markers on removed - symbols, broken callers, BOM/version mismatches. -- **Should fix** โ€” convention drift, missing deprecation `ReplaceWith`, - missing copyright update, missing URL comment, naming oddities. -- **Nits** โ€” formatting, ordering, doc-comment polish. - -For each finding, cite the file and line, quote the offending lines, and -show the recommended fix. - -End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or -`REQUEST CHANGES`. diff --git a/.agents/skills/dependency-update/SKILL.md b/.agents/skills/dependency-update/SKILL.md deleted file mode 100644 index 7e70bc12..00000000 --- a/.agents/skills/dependency-update/SKILL.md +++ /dev/null @@ -1,283 +0,0 @@ ---- -name: dependency-update -description: > - Walk every dependency declaration under - `buildSrc/src/main/kotlin/io/spine/dependency/`, discover the latest accepted - version of each artifact from the URL hinted in its file (or from Maven - metadata if no URL is present), and update the `version` constant in place. - External dependency scopes accept only released versions; the `local` scope - also accepts snapshots and pre-releases published from sibling Spine repos. - Use when asked to refresh dependency versions, bump libraries, run a - dependency audit, or "see what's stale". ---- - -# Update dependencies - -## Goal - -Bring every dependency object under -`buildSrc/src/main/kotlin/io/spine/dependency/` to its latest accepted version. -For every scope except `local/`, that means the latest **released** version: -snapshots, release candidates, milestones, alpha/beta, EAP, and `-dev` builds -are **excluded**. - -`local/` is the deliberate exception. It holds Spine SDK dependencies published -from sibling Spine repositories, and it may move to newer snapshots or -pre-releases such as `2.0.0-SNAPSHOT.388` or `2.1.0-RC1`. - -The authoritative version source for each artifact is the web page already -referenced in its file. When the file has no URL, use the Maven metadata -fallback described below. For non-`local/` artifacts, a discovered Maven -Central URL is **added back to the file** as a line comment so the next run has -a hint. - -## Inputs - -- No arguments โ†’ scan all of `buildSrc/src/main/kotlin/io/spine/dependency/`. -- One or more paths or sub-package names (`lib`, `local`, `test`, `build`, - `kotlinx`, `boms`) โ†’ restrict the scan to those. -- `--dry-run` โ†’ discover and report, but do not edit. - -## Pre-flight - -1. Run `git status --short`. If the worktree is dirty in files this skill will - touch, stop and ask the user. Otherwise preserve unrelated changes. -2. Confirm `buildSrc/src/main/kotlin/io/spine/dependency/` exists. -3. Note the current branch โ€” every change this skill makes is a candidate for - a single `chore(deps): refresh external versions` commit at the end; the - skill itself does NOT commit. The user decides. - -## Per-file workflow - -For each `*.kt` file in scope: - -### 1. Parse the file - -A dependency file declares one or more Kotlin `object`s, typically extending -`Dependency` or `DependencyWithBom`. The shape is: - - object Kotest { - const val version = "6.1.11" - const val group = "io.kotest" - const val assertions = "$group:kotest-assertions-core:$version" - // โ€ฆ - } - -Extract: - -- `objectName` โ€” the outer `object` identifier. -- `version` โ€” the literal version string. Some files have **multiple** version - constants (`runtimeVersion`, `embeddedVersion`, `annotationsVersion`); treat - each separately. The one driving the artifact is typically `override val - version = โ€ฆ` or the `const val version = โ€ฆ` declared at the top. -- `group` โ€” the Maven group. -- `module` artifact names โ€” each `const val foo = "$group:foo:$version"` line - contributes one artifact name. Use the first one to query Maven Central if - needed for non-`local/` artifacts, or Spine SDK Maven repositories for - `local/` artifacts. -- `versionUrl` โ€” a URL hint. Look in this order: - 1. Line comments above the object: `^//\s*(https?://\S+)`. - 2. KDoc `@see โ€ฆ` inside the object's KDoc. - 3. Plain `@see https?://โ€ฆ` inside the KDoc. - 4. If none: leave `versionUrl` empty and use the Maven metadata fallback - below. - -Skip files that contain only abstract base classes or helpers (`Dependency.kt`, -`DependencyWithBom.kt`, `BomsPlugin.kt`, anything without a concrete artifact -declaration). - -### 2. Find the latest accepted version - -The discovery rule depends on the URL shape. For files under -`dependency/local/`, check the Spine SDK Maven metadata before GitHub, even -when the file has a GitHub URL; snapshots are usually visible in Maven -metadata, not in GitHub's latest-release redirect. - -**A. GitHub repository URL** (`https://github.com//`): - -- Outside `local/`, resolve - `https://github.com///releases/latest`. GitHub redirects to the - latest non-prerelease tag. Read the redirected location or the rendered HTML - to extract the tag. -- In `local/`, do **not** rely on `/releases/latest`, because it hides - pre-releases. Use GitHub releases and tags only after checking Spine SDK - Maven metadata. When you do use GitHub, include pre-release entries and keep - version-like tags that match the artifact. -- Tags often have a `v` prefix. Strip it. -- If the repo publishes per-component tags (e.g. - `kotlinx-coroutines-1.10.2`), prefer the tag whose name matches the - artifact's module identifier. Otherwise take the topmost release. - -**B. Maven Central artifact URL** -(`https://search.maven.org/artifact//` or -`https://repo1.maven.org/maven2///`): - -- Hit Maven Central's REST API: - `https://search.maven.org/solrsearch/select?q=g:+AND+a:&rows=20&core=gav` -- Outside `local/`, filter the `response.docs[].v` values by the pre-release - rule (below). -- In `local/`, keep snapshots and pre-releases in the candidate list. -- Take the highest by semver comparison. - -**C. Spine SDK Maven repositories for `local/` artifacts**: - -- For files under `dependency/local/`, query Maven metadata in the current - Spine SDK Artifact Registry repositories before falling back elsewhere: - - `https://europe-maven.pkg.dev/spine-event-engine/releases` - - `https://europe-maven.pkg.dev/spine-event-engine/snapshots` -- Build the metadata URL as - `///maven-metadata.xml`, where `groupPath` is the - Maven group after first resolving symbolic aliases used in dependency files - (for example, `Spine.group` -> `io.spine` and `Spine.toolsGroup` -> - `io.spine.tools`) and then replacing dots with slashes. -- Read `...` entries. For `local/`, do not - reject `SNAPSHOT`, RC, milestone, alpha, beta, EAP, pre, or dev versions. -- If both release and snapshot repositories have candidates, compare all of - them together and take the highest version. - -**D. Project homepage** (e.g. `https://kotest.io/`, `https://junit.org/`, -`https://www.detekt.dev/`): - -- Try to find a "latest release" or "download" link on the page. If the page - is a thin landing page with no usable version data, fall through to E. - -**E. No URL or unusable URL โ€” Maven metadata fallback**: - -- Outside `local/`, query Maven Central as in B using the file's `group` and - the first module artifact name (the part after `$group:`). -- In `local/`, query the Spine SDK Maven metadata first. Use Maven Central only - if the artifact is absent from those repositories. -- If a non-`local/` Maven Central fallback query returns results, **also insert - a line comment** - `// https://search.maven.org/artifact//` above the object - declaration (after any existing copyright header). This back-fills the URL - hint for next time. Match the existing comment style (one line, no trailing - punctuation). -- If all fallback queries have no result, leave the file untouched and add it - to the `Manual review` section of the final report. - -### 3. Filter pre-releases outside `local/` - -Apply this filter only to files outside `dependency/local/`. - -For `local/` files, snapshots and pre-releases are accepted candidates. Do not -put them in `Filtered pre-releases`; put them in the `local/` confirmation -section of the final report instead. - -Reject any version string matching, case-insensitively: - - -SNAPSHOT$ - -RC[\d\-.]*$ e.g. -RC1, -RC.2 - -M\d+$ e.g. -M3 - -alpha[\d\-.]*$ - -beta[\d\-.]*$ - -EAP[\d\-.]*$ - -pre[\d\-.]*$ - -dev[\d\-.]*$ - \.Beta\d*$ Spring-style trailing tokens - \.Alpha\d*$ - \.RC\d*$ - \.M\d+$ - -Apply the regex to the **suffix after the numeric version**. The version -`2.0.0-SNAPSHOT.182` is a snapshot and must be rejected as a target outside -`local/`, but it is valid for `local/` dependency objects. This skill only -edits dependency files, never `version.gradle.kts` (that belongs to the -`bump-version` skill). - -### 4. Compare versions - -Use semver comparison: - -- Split on `.` and `-`. -- Numeric segments compare numerically; non-numeric segments compare - lexicographically. -- A version without any pre-release suffix is greater than one with the same - numeric prefix but a pre-release suffix. - -Only update when `latest > current`. Equal or lower โ†’ no change. - -### 5. Apply the edit - -- Replace the `version` literal with the new value. Use a precise replacement - anchored on the full line (`const val version = ""` โ†’ - `const val version = ""`). Do not blindly replace the version string, - because the same string can appear in module URLs constructed via - interpolation (`"$group:โ€ฆ:$version"`) โ€” those will pick up the new value - automatically. -- If the file uses a renamed version constant (`runtimeVersion`, - `compilerVersion`, etc.) that feeds `override val version = compilerVersion`, - update the **source** constant, not the alias. -- For `DependencyWithBom` objects, verify the `bom` line still resolves - correctly. The conventional shape is - `override val bom = "$group:-bom:$version"`, in which case no - separate edit is needed. If the BOM version is hard-coded, update it too. -- Preserve indentation, comment style, and surrounding blank lines exactly. - -### 6. Watch for `local/` artifacts - -`local/` holds Spine SDK dependencies (Base, CoreJvm, ModelCompiler, โ€ฆ) that -are published from sibling Spine repos. This scope accepts snapshots and -pre-releases because these artifacts often advance through internal snapshot -builds before a stable SDK release. - -Still **flag every `local/` update in the report**, and note whether the target -is a release, snapshot, or pre-release. The user can then decide whether to -bump the SDK in lockstep with the rest of the project. Spine SDK artifacts -often need to move together; one-off bumps can cause runtime ABI mismatches. - -## Report - -When the run completes, emit a Markdown report with these sections: - -- **Updated** โ€” table of `file | objectName | old โ†’ new | source URL`. -- **Already current** โ€” file/object pairs whose version was already the - newest accepted version. -- **Skipped (no URL, metadata empty)** โ€” manual review needed. -- **Filtered pre-releases** โ€” newer versions found but rejected because they - were RC/SNAPSHOT/alpha/etc. Applies only outside `local/`. -- **`local/` bumps to confirm** โ€” every `local/` change called out separately, - including snapshot and pre-release targets. - -End with the suggested next steps: - -1. Review the diff (`git diff buildSrc/src/main/kotlin/io/spine/dependency/`). -2. Invoke `/version-bumped`. Every feature branch must advance - `version.gradle.kts` strictly above the base before any - `./gradlew build` (which may transitively `publishToMavenLocal`). The - skill is a no-op when a bump already happened earlier on the branch - and otherwise calls `/bump-version` to perform the increment. -3. Run `./gradlew build` (or `./gradlew clean build` if `.proto` files - participate). -4. Commit. Match the shape of the actual change: - - Single `local/` bump (most common): `` Bump Spine Base -> `2.0.0-SNAPSHOT.190` `` - - Coordinated external set: `Bump Protobuf and gRPC` (one commit; - mention both). - - Bulk external refresh (rare): `Refresh external dependencies`. - -## Safety - -- Do not commit. Do not push. Editing files is the limit of this skill's - authority. -- Never edit `version.gradle.kts` โ€” that's the `bump-version` skill's - responsibility. -- Never auto-resolve a Maven Central query that returns multiple matching - artifacts with different groups (e.g. a library that exists under both - `io.netty` and `io.netty.incubator`). Ask the user. -- If a discovered "latest" version is more than one **major** ahead of the - current value (e.g. `1.x` โ†’ `3.x`), flag it as a major bump in the report - and apply the edit only if the user confirms, or only when running - non-interactively with `--include-majors`. Major bumps frequently break - ABI. - -## Failure modes to expect - -- **GitHub rate limit** on the unauthenticated REST API. The `/releases/latest` - HTML page does not require auth and is the preferred fallback. -- **Per-component tags** in a monorepo. Match by artifact name, don't take the - topmost tag blindly. -- **Repositories that publish to JCenter only** โ€” JCenter is sunset; if Maven - Central is empty, the dependency may need migration. Flag it. -- **Vendor-specific version schemes** (e.g. dates: `2025.10.01`) โ€” the - semver comparator above will still order these correctly; just don't - mis-classify them as pre-releases. diff --git a/.agents/skills/dependency-update/agents/openai.yaml b/.agents/skills/dependency-update/agents/openai.yaml deleted file mode 100644 index a61198d3..00000000 --- a/.agents/skills/dependency-update/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dependency Update" - short_description: "Refresh dependency versions, allowing snapshots only for local Spine SDK artifacts" - default_prompt: "Use $dependency-update to walk every dependency object under buildSrc/src/main/kotlin/io/spine/dependency/, find the latest accepted version for each, and update the version constants in place. External scopes use released non-snapshot versions only; dependency/local/ may use snapshots and pre-releases from sibling Spine repos. Use the URL referenced in each file as the source of truth; fall back to Maven metadata and back-fill missing hints when useful." diff --git a/.agents/skills/java-to-kotlin/SKILL.md b/.agents/skills/java-to-kotlin/SKILL.md deleted file mode 100644 index b9835f8f..00000000 --- a/.agents/skills/java-to-kotlin/SKILL.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: java-to-kotlin -description: > - Convert Java code to Kotlin, including Java API comments from Javadoc to KDoc. - Use when asked to migrate Java files, classes, methods, nullability semantics, - or common Java patterns into idiomatic Kotlin while preserving behavior. ---- - -# ๐Ÿช„ Converting Java code to Kotlin - -* Java code API comments are Javadoc format. -* Kotlin code API comments are in KDoc format. - -## Javadoc to KDoc conversion - -* The wording of original Javadoc comments must be preserved. - -## Treating nullability - -* Use nullable Kotlin type only if the type in Java is annotated as `@Nullable`. - -## Efficient Conversion Workflow - -* First, analyze the entire Java file structure before beginning conversion to understand dependencies and class relationships. -* Convert Java code to Kotlin systematically: imports first, followed by class definitions, methods, and finally expressions. -* Preserve all existing functionality and behavior during conversion. -* Maintain original code structure and organization to ensure readability. - -## Common Java to Kotlin Patterns - -* Convert Java getters/setters to Kotlin properties with appropriate visibility modifiers. -* Transform Java static methods to companion object functions or top-level functions as appropriate. -* Replace Java anonymous classes with Kotlin lambda expressions when possible. -* Convert Java interfaces with default methods to Kotlin interfaces with implementations. -* Transform Java builders to Kotlin DSL patterns when appropriate. - -## Error Prevention - -* Pay special attention to Java's checked exceptions versus Kotlin's unchecked exceptions. -* Be cautious with Java wildcards (`? extends`, `? super`) conversion to Kotlin's `out` and `in` type parameters. -* Ensure proper handling of Java static initialization blocks in Kotlin companion objects. -* Verify that Java overloaded methods convert correctly with appropriate default parameter values in Kotlin. -* Remember that Kotlin has smart casts which can eliminate explicit type casting needed in Java. - -## Documentation Conversion - -* Convert `@param` to `@param` with the same description. -* Convert `@return` to `@return` with the same description. -* Convert `@throws` to `@throws` with the same description. -* Convert `{@link}` to `[name][fully.qualified.Name]` format. -* Convert `{@code}` to inline code with backticks (`). - -## Final step: ensure the version is bumped - -After the conversion is verified, invoke `/version-bumped` so the branch -carries a strictly greater `version.gradle.kts` than the base ref before -any `./gradlew build` (which may transitively `publishToMavenLocal` and -overwrite the previously published snapshot consumer repos depend on). -The skill is a no-op when a bump already happened earlier on the branch. diff --git a/.agents/skills/java-to-kotlin/agents/openai.yaml b/.agents/skills/java-to-kotlin/agents/openai.yaml deleted file mode 100644 index 252920fe..00000000 --- a/.agents/skills/java-to-kotlin/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Java to Kotlin" - short_description: "Convert Java code to idiomatic Kotlin" - default_prompt: "Use $java-to-kotlin to convert Java code to Kotlin while preserving behavior, nullability, and API documentation wording." diff --git a/.agents/skills/kotlin-review/SKILL.md b/.agents/skills/kotlin-review/SKILL.md deleted file mode 100644 index c55c8c49..00000000 --- a/.agents/skills/kotlin-review/SKILL.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: kotlin-review -description: > - Review Kotlin (and Java) changes in this repo against the Spine coding - guidelines, safety rules, and testing policy. Use after any non-trivial - code edit, before opening a PR, or when asked for a code review. - Read-only; does not run builds. ---- - -# Kotlin code review (repo-specific) - -You are the Kotlin reviewer for this repository. The authoritative standards -live in `.agents/`: - -- `.agents/coding-guidelines.md` โ€” Kotlin idioms, formatting, what to prefer/avoid. -- `.agents/safety-rules.md` and `.agents/advanced-safety-rules.md` โ€” hard constraints - (no reflection without approval, no analytics/telemetry, no blocking calls in - coroutines, no auto-updating external dependencies). -- `.agents/testing.md` โ€” Kotest assertions preferred, stubs not mocks. -- `.agents/project-structure-expectations.md` โ€” module/source-set layout. -- `.agents/version-policy.md` โ€” version bumps are required only when the - repository has a root `version.gradle.kts`. - -## Review procedure - -1. Read the diff. Use `git diff --staged` or `git diff ...HEAD` depending on - what the user describes. Do NOT review the full repo โ€” only what changed. -2. Read each affected file fully, not just the diff hunks. Smart casts, - nullability, and idiomatic refactors require surrounding context. -3. Check against `.agents/coding-guidelines.md`: - - Kotlin idioms (extension functions, `when`, smart casts, data/sealed classes). - - Immutability by default. - - No `!!` without justification. - - No type names in variable names. - - No string duplication โ€” use companion-object constants. - - No mixing Groovy/Kotlin DSL in build logic. - - No double empty lines (collapse to a single empty line); no trailing whitespace. -4. Check safety rules: reflection, telemetry, blocking-in-coroutines, dependency - bumps that weren't requested. -5. Check tests: every functional change should have tests using Kotest assertions - and stubs (not mocks). -6. Check the version gate: - - If the repository has a root `version.gradle.kts`, confirm it was - incremented when the change is user-visible. - - If root `version.gradle.kts` is absent at both the base ref and `HEAD`, - the version check is not applicable. Do not report a missing version bump - or ask for the file to be created. - -## Output format - -Return three sections, in this order: - -- **Must fix** โ€” violations of safety rules, broken builds, missing version - bump when the version gate applies, missing tests for functional changes. -- **Should fix** โ€” coding-guideline violations and clearer idiomatic alternatives. - Cite the specific guideline. -- **Nits** โ€” style and naming suggestions. - -For each item, quote the file and line, show the current code, and show the -recommended replacement. If there's nothing in a section, write "None." - -End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or `REQUEST CHANGES`. diff --git a/.agents/skills/move-files/SKILL.md b/.agents/skills/move-files/SKILL.md deleted file mode 100644 index b92b05d3..00000000 --- a/.agents/skills/move-files/SKILL.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: move-files -description: > - Move or rename any files/directories in a repo: preserve history, update all - references and build metadata, verify no stale paths remain. ---- - -# Move Files - -## Workflow - -1. Preflight. - - Run `git status --short`. - - Map each `source -> destination`. - - Classify scope: simple same-module moves stay targeted; package, module, or - cross-module moves need broader inspection. - - Ask before ambiguous mappings, destination conflicts, or unclear semantic - package/module changes. - -2. Search before moving. - - Search all old identifiers: paths, names, resource refs, doc links. - - For Gradle/module/source-set moves, check `settings.gradle.kts`, - `build.gradle.kts`, and `buildSrc`. - - For Kotlin/Java, update package declarations only when package intent - changes. - -3. Move safely. - - Always use `git mv` for tracked files in the repo. If sandboxing blocks - it, request approval; do not use delete/create as a fallback. - - Use filesystem moves only for untracked/generated/out-of-git files. - - Create parent directories first. - - For case-only renames, move through a temporary name. - -4. Repair references. - - Update all references: imports, build metadata, docs, resources, and scripts. - - Start search scope narrow: affected directory, then module, then repo-wide. - - Prefer precise edits; avoid broad replacements on generic names. - -5. Verify. - - Re-run targeted searches for old tokens. - - Run `git status --short` and confirm the delta matches the move. - - Run focused validation for moved files, or state what could not run. - -6. Ensure the version is bumped. - Invoke `/version-bumped` so the branch carries a strictly greater - `version.gradle.kts` than the base ref before any `./gradlew build` - (which can transitively `publishToMavenLocal` and overwrite - consumer-facing snapshots). The skill is a no-op if a bump already - happened earlier on the branch. - -## Repo Notes - -Follow `.agents/project-structure-expectations.md` for module/source-set/test moves. - -## Report - -Return: `Moved[]`, `UpdatedRefs[]`, `Verification[]`, `Risks[]`. diff --git a/.agents/skills/move-files/agents/openai.yaml b/.agents/skills/move-files/agents/openai.yaml deleted file mode 100644 index ba90a9f8..00000000 --- a/.agents/skills/move-files/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Move Files" - short_description: "Move files safely across a repo" - default_prompt: "Use $move-files to relocate files or directories in this repository while preserving history, updating references, and verifying the result." diff --git a/.agents/skills/pre-pr/SKILL.md b/.agents/skills/pre-pr/SKILL.md deleted file mode 100644 index e656b0b4..00000000 --- a/.agents/skills/pre-pr/SKILL.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -name: pre-pr -description: > - Run the pre-PR checklist for this repo: apply the version gate only when - the repository has a root `version.gradle.kts`, run a scope-dependent - build/check command per `.agents/running-builds.md` (docs-only โ†’ `dokka`; - code/deps โ†’ `build`; proto โ†’ `clean build`; no documented command โ†’ skipped), - and invoke the relevant reviewers (`kotlin-review`, `review-docs`, - `dependency-audit`, - `check-links`) against the branch diff. On success, write a sentinel file at - `.git/pre-pr.ok` so the `gh pr create` hook can verify the checklist ran - for the current HEAD. Use before opening a PR, or when CI rejected a - branch and you want a fast local repro. ---- - -# Pre-PR checklist (repo-specific) - -You are the pre-PR gate for this repository. You compose the existing -reviewers and the documented repository rules into a single pass that must -succeed before a pull request is opened. - -This skill supports both versioned Gradle Build Tools projects and repositories -that intentionally do not have `version.gradle.kts`. Do not create -`version.gradle.kts` just to satisfy this checklist. When the file is absent -from the project root, the version-bump check is **not applicable**. - -The authoritative standards live in `.agents/`: - -- `.agents/version-policy.md` โ€” applies only when the repository has a root - `version.gradle.kts`. -- `.agents/running-builds.md` โ€” which build/check command to run. -- `.agents/safety-rules.md` and `.agents/advanced-safety-rules.md` โ€” hard - constraints checked by the reviewers. - -## Procedure - -Run steps 1โ€“4 fully before aggregating. Collect all findings; do not stop at -the first failure. - -### 1. Determine scope and repository capabilities - -- Base ref: `master` unless the user provides a different one. -- Changed files: `git diff ...HEAD --name-only` -- Repository root: `git rev-parse --show-toplevel` -- Version gate: check only the repository-root `version.gradle.kts`. - - Absent at both sides โ†’ `not-applicable`, continue. - - Present at `HEAD` โ†’ enforce in step 2. - - Present at `` but missing at `HEAD` โ†’ fail unless the user - explicitly asked to migrate away from Gradle Build Tools versioning. -- Classify changes: - - **proto** โ€” any `*.proto` changed - - **code** โ€” any `*.kt`, `*.kts`, or `*.java` changed - - **docs** โ€” any `*.md` or doc-only source edits changed - - **deps** โ€” any file under `buildSrc/src/main/kotlin/io/spine/dependency/` changed - - **site** โ€” any file under `docs/**` or `lychee.toml` (triggers Hugo link - check; pure `README.md` or KDoc-only changes do *not* count) - -### 2. Version-bump check - -- Skip when version gate is `not-applicable`. -- Read `version.gradle.kts` at `HEAD`. Read `` only if the file exists - there; if it does not, the file is newly introduced โ€” record the introduced - version and continue. -- When both sides have the file: if the version is not strictly greater (semver - + Spine snapshot rules in `.agents/version-policy.md`): if - `.agents/skills/bump-version/` exists, **auto-fix immediately** by invoking - `/bump-version` without asking; otherwise record a Must-fix and continue. - Re-read the file after the fix. If the version is still not strictly greater, - record a Must-fix and continue. If the auto-fix succeeded, recompute the - changed-file list (`git diff ...HEAD --name-only`) before proceeding to - Step 3 โ€” the bump commit adds `version.gradle.kts` to the diff. - -### 3. Build or check - -Pick the target per `.agents/running-builds.md`: - -- **proto** changed โ†’ `./gradlew clean build` -- Else **code** changed โ†’ `./gradlew build` -- Else **docs**-only โ†’ `./gradlew dokka` - -If `./gradlew` is absent, read `.agents/running-builds.md` for the -repository-specific command. If that file is also absent, or if none is -documented for the change type, record `build_status=skipped` with the -reason and continue. - -Run the chosen command. On failure, record the first failing task and -continue to step 4 โ€” do not abort. Pass `build_status=FAIL` in the context -given to reviewers so they can discount false positives from non-compiling -code. - -### 4. Reviewers (run in parallel) - -Dispatch relevant reviewers concurrently; collect all verdicts before -aggregating. Before dispatching, check that the skill directory exists under -`.agents/skills/`; if a skill is absent, skip it with a note "not applicable -for this repo" rather than failing. - -- **code** changed โ†’ `kotlin-review` -- **docs** or KDoc changed โ†’ `review-docs` -- **deps** changed โ†’ `dependency-audit` -- **site** changed โ†’ `check-links` (unless the sentinel short-circuit below - applies) - -**`check-links` sentinel short-circuit.** Read `.git/check-links.ok` (if -present). If `head=` equals the current **full** HEAD SHA and `status=PASS`, skip -dispatch and record `APPROVE` with note "cached from `.git/check-links.ok`" -(caching its ~30 s rebuild+serve cycle; the result is deterministic for a given -HEAD). Otherwise dispatch normally. - -Pass each reviewer: base ref, changed-file list, build result, version result. -When the version check is `not-applicable`, say so explicitly so reviewers don't flag a -missing version bump. - -**Auto-fix policy for reviewer findings:** - -- Findings from `kotlin-review`, `review-docs`, or `dependency-audit` โ†’ record - as Must-fix or Should-fix; do **not** auto-apply. Surface them and wait for - user action. -- If a reviewer reports a missing version bump after Step 2 already ran, the - auto-fix did not take โ€” record a Must-fix and do not silently re-apply. -- `dependency-audit` reports a **version rollback** โ†’ do **not** auto-fix. - Surface it as a Must-fix and wait for user confirmation, because a rollback - can be intentional. - -### 5. Aggregate - -- **PASS**: version check passed or `not-applicable`, build succeeded or - `build_status=skipped` (no documented command for the change type), every - reviewer returned `APPROVE` or `APPROVE WITH CHANGES`, and no unaddressed - Must-fix items remain. -- **FAIL**: anything else. - -### 6. Sentinel - -Write `.git/pre-pr.ok` at the repo root (never under `.claude/`). The `gh pr -create` hook (`.agents/scripts/pre-pr-gate.sh`) checks `head=` and `status=`; -field names in this block are part of that contract. - -``` -head= -branch= -status=PASS|FAIL -timestamp= -build= -build_status=PASS|FAIL|skipped -reviewers= -version=new, introduced:, or "not-applicable"> -``` - -## Output format - -**On PASS** โ€” single line: - -``` -Pre-PR: PASS ( vs ) โ€” ready to `gh pr create`. -``` - -**On FAIL** โ€” header line, then only the items that need attention, each -prefixed with the source reviewer or check: - -``` -Pre-PR: FAIL ( vs ) - -Must fix: -- [kotlin-review] -- [review-docs] - -Should fix: -- [dependency-audit] -``` - -Report nothing about checks that passed. If auto-fixes were applied, list -them in one line before the verdict: `Auto-fixed: .` - -## Notes - -- This skill must NOT create the PR itself. -- This skill must NOT create `version.gradle.kts`. -- The sentinel lives under `.git/` โ€” per-clone, never committed. -- Each reviewer is the source of truth for its own checks; this skill only - orchestrates and aggregates. -- This skill may auto-fix a missing version bump by invoking `/bump-version`; - all other fixes require explicit user confirmation. diff --git a/.agents/skills/review-docs/SKILL.md b/.agents/skills/review-docs/SKILL.md deleted file mode 100644 index d936fa28..00000000 --- a/.agents/skills/review-docs/SKILL.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -name: review-docs -description: > - Review documentation changes โ€” KDoc/Javadoc inside Kotlin/Java sources and - Markdown docs (`README.md`, `docs/**`) โ€” against Spine documentation - conventions. Use when a diff touches doc comments or Markdown, before - opening a doc-affecting PR, or when asked for a documentation review. - Read-only; does not run builds. ---- - -# Review documentation (repo-specific) - -You are the documentation reviewer for a Spine Event Engine project. You -focus strictly on documentation quality โ€” prose, KDoc/Javadoc, and Markdown โ€” -and deliberately do **not** duplicate the code-review skill (which owns -Kotlin idioms, safety rules, tests, and version-gate checks). - -The authoritative standards live in `.agents/`: - -- `.agents/documentation-guidelines.md` โ€” commenting rules, TODO-comment - format, "file/dir names as code", widow/runt/orphan/river rule (with the - diagram at `.agents/widow-runt-orphan.jpg`). -- `.agents/documentation-tasks.md` โ€” KDoc-example requirement on APIs; - Javadoc โ†’ KDoc conversion rules (`

` removal, etc.). -- `.agents/skills/writer/SKILL.md` โ€” Markdown conventions (footnote-style - reference links for external URLs, typographic quotes only on actual - page/section titles, sidenav-sync rules under `docs/`). -- `.agents/running-builds.md` โ€” for doc-only Kotlin/Java changes the right - build is `./gradlew dokka` (no tests required). - -## Review procedure - -1. **Scope the diff.** Obtain the change set via `git diff --staged` or - `git diff ...HEAD` depending on what the user describes. Restrict - to files matching: - - `**/*.kt`, `**/*.kts`, `**/*.java` (for KDoc/Javadoc inside sources) - - `**/*.md` (Markdown docs) - Do **not** review the full repo โ€” only what changed. - -2. **Read each affected file fully, not just the hunks.** Prose review - requires surrounding context โ€” judging widows/runts/orphans, link - placement, and KDoc completeness needs the whole paragraph and the - surrounding declarations. - -3. **Stay in scope.** If you spot a code-quality issue (idiom, naming, - tests, version-gate applicability), note it briefly as a "for the code - reviewer" item under Nits โ€” do not expand the review. - -## Checks - -### A. KDoc / Javadoc inside sources - -- **Public and internal APIs carry KDoc.** Per `documentation-tasks.md`, - KDoc should include at least one usage example for non-trivial APIs. - Missing KDoc on a new or modified public/internal symbol is a Should-fix. -- **No Javadoc residue in Kotlin.** When converting from Java: - - `

` tags on a text line removed (`"

This"` โ†’ `"This"`). - - `

` on its own line replaced with a blank line. - - HTML entities (`&`, `<`, โ€ฆ) converted to literals where appropriate. -- **Inline comments in production code are minimized.** Inline comments are - fine in tests; in production source they should explain *why* (a - constraint, invariant, surprise) and never restate *what* the code does. -- **TODO comments follow the Spine format.** Linked from - `documentation-guidelines.md` to the wiki "TODO-comments" page. A bare - `// TODO: โ€ฆ` without owner/issue reference is a Should-fix. -- **File and directory names rendered as code.** Within KDoc/Javadoc prose, - `path/to/file.kt` and `module-name` must use backticks. - -### B. Markdown docs - -- **Footnote-style reference links** for external `https://` URLs (per the - `writer` skill). Inline `[label](https://โ€ฆ)` in body prose is a - Should-fix; inline links to local relative paths are fine. -- **Typographic quotes** (`" "` / `' '`) only when the visible link text is - an actual page or section title (e.g., the "Getting started" page). - Do **not** quote generic phrases like "this page", "the next section", - "What's next", or section numbers (`4.3`). -- **Sidenav sync.** If the diff adds/removes/renames/moves a page under - `docs/content/docs/

/`, the matching current-version - `sidenav.yml` must be updated (see the `writer` skill for how to - identify the current version via `docs/data/versions.yml`). A missing - sidenav update is a Must-fix. -- **Fenced code blocks** for commands and examples โ€” no indented code - blocks for shell snippets (they swallow `$` prompts and hurt copy/paste). -- **Heading hierarchy.** No skipped levels (`#` โ†’ `###`); exactly one `#` - per file. - -### C. Prose flow (Spine-specific) - -- **Avoid widows, runts, orphans, and rivers** โ€” the rule from - `documentation-guidelines.md` with the diagram at - `.agents/widow-runt-orphan.jpg`. Operationally: - - **Widow / runt**: a paragraph's last line containing only one short - word (or a hyphenated fragment). Reflow the prior line. - - **Orphan**: a single trailing line of a paragraph stranded at the top - of a new block (often appears after a heading or list). Reflow. - - **River**: a vertical "gap" of aligned spaces running down justified - text. Rare in Markdown but possible in tables โ€” reflow the table or - rewrite to break the alignment. - Quote the offending paragraph and propose a rewording that fixes it. - -### D. Terminology and tone - -- **Match code identifiers verbatim.** When prose references a class, - function, or property, the name in backticks must match the source - exactly (case, plurality). -- **Consistent terminology across the diff.** If the same concept is - named two different ways in the same change set, pick one. - -## Output format - -Three sections, in this order: - -- **Must fix** โ€” broken/missing KDoc on a newly-introduced public API, - missing sidenav sync, broken cross-references, Javadoc residue - (`

` tags) left in Kotlin KDoc, broken Markdown links. -- **Should fix** โ€” TODO format, inline-comment overuse in production, - inline external links that should be footnote-style, missing typographic - quotes (or unwanted ones), widow/runt/orphan/river paragraphs, - fenced-vs-indented code blocks. -- **Nits** โ€” wording, terminology drift, code-identifier capitalization - in prose, "for the code reviewer" pointers if any code issues surfaced - incidentally. - -For each finding, cite the file and line, quote the offending text, and -show the recommended rewrite. If a section is empty, write "None." - -End with a one-line verdict: `APPROVE`, `APPROVE WITH CHANGES`, or -`REQUEST CHANGES`. diff --git a/.agents/skills/update-copyright/SKILL.md b/.agents/skills/update-copyright/SKILL.md deleted file mode 100644 index 6afc4c7c..00000000 --- a/.agents/skills/update-copyright/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: update-copyright -description: > - Update source file copyright headers from the IntelliJ IDEA copyright profile, - replacing `today.year` with the current year. - Automatically apply when source files are modified in a change set. ---- - -# Copyright Update - -**Command:** `python3 .agents/skills/update-copyright/scripts/update_copyright.py` - -1. Scope: explicit files/dirs from the user, or all tracked source files if none given. -2. No explicit paths โ†’ run with `--dry-run` first, then without. -3. Relay stdout (notice source, file count, changed paths) to the user. -4. Never add a copyright header to a file that does not already have one. diff --git a/.agents/skills/update-copyright/agents/openai.yaml b/.agents/skills/update-copyright/agents/openai.yaml deleted file mode 100644 index 246dd647..00000000 --- a/.agents/skills/update-copyright/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Copyright Update" - short_description: "Refresh source copyright headers" - default_prompt: "Use $update-copyright to refresh source file copyright headers from the IntelliJ IDEA copyright profile in this repository." diff --git a/.agents/skills/update-copyright/scripts/update_copyright.py b/.agents/skills/update-copyright/scripts/update_copyright.py deleted file mode 100755 index 2dbf8bbc..00000000 --- a/.agents/skills/update-copyright/scripts/update_copyright.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/usr/bin/env python3 -"""Update source copyright headers from IntelliJ IDEA copyright profiles.""" - -from __future__ import annotations - -import argparse -import datetime as dt -import html -import re -import subprocess -import sys -from pathlib import Path -from xml.etree import ElementTree as ET - - -BLOCK_EXTENSIONS = { - ".c", - ".cc", - ".cpp", - ".cs", - ".css", - ".cxx", - ".dart", - ".go", - ".gradle", - ".groovy", - ".h", - ".hh", - ".hpp", - ".java", - ".js", - ".jsx", - ".kt", - ".kts", - ".less", - ".m", - ".mm", - ".proto", - ".rs", - ".scala", - ".scss", - ".swift", - ".ts", - ".tsx", -} -HASH_EXTENSIONS = { - ".bash", - ".bzl", - ".properties", - ".pl", - ".py", - ".rb", - ".sh", - ".toml", - ".yaml", - ".yml", - ".zsh", -} -XML_EXTENSIONS = { - ".fxml", - ".pom", - ".wsdl", - ".xml", - ".xsd", - ".xsl", - ".xslt", -} -EXCLUDED_DIRS = { - ".agents", - ".git", - ".gradle", - ".idea", - ".kotlin", - "build", - "generated", - "out", - "tmp", -} -EXCLUDED_FILES = { - "gradlew", - "gradlew.bat", -} - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description=( - "Update source copyright headers from " - ".idea/copyright/profiles_settings.xml." - ) - ) - parser.add_argument( - "paths", - nargs="*", - help="Files or directories to update. Defaults to tracked source files.", - ) - parser.add_argument( - "--root", - type=Path, - default=Path.cwd(), - help="Repository root. Defaults to the current working directory.", - ) - parser.add_argument( - "--year", - default=str(dt.date.today().year), - help="Year to substitute for today.year. Defaults to the current year.", - ) - parser.add_argument( - "--dry-run", - action="store_true", - help="Report files that would change without writing them.", - ) - parser.add_argument( - "--check", - action="store_true", - help="Exit with status 1 if any file would change; do not write files.", - ) - return parser.parse_args() - - -def profile_filename(profile_name: str) -> str: - stem = re.sub(r"[^A-Za-z0-9]+", "_", profile_name).strip("_") - if not stem: - raise ValueError("The default copyright profile name is empty.") - return f"{stem}.xml" - - -def load_notice(root: Path, year: str) -> tuple[str, Path]: - settings_path = root / ".idea" / "copyright" / "profiles_settings.xml" - if not settings_path.is_file(): - raise FileNotFoundError(f"Missing {settings_path}") - - settings_root = ET.parse(settings_path).getroot() - settings = settings_root.find(".//settings") - if settings is None: - raise ValueError(f"{settings_path} does not contain a settings tag.") - - default_profile = settings.get("default") - if not default_profile: - raise ValueError(f"{settings_path} settings tag has no default attribute.") - - profile_path = settings_path.parent / profile_filename(default_profile) - if not profile_path.is_file(): - raise FileNotFoundError( - f"Default profile {default_profile!r} resolves to missing {profile_path}" - ) - - profile_root = ET.parse(profile_path).getroot() - notice = None - for option in profile_root.findall(".//option"): - if option.get("name") == "notice": - notice = option.get("value") - break - if notice is None: - raise ValueError(f"{profile_path} has no option named 'notice'.") - - decoded = html.unescape(notice) - decoded = decoded.replace("${today.year}", year) - decoded = decoded.replace("$today.year", year) - decoded = decoded.replace("today.year", year) - return decoded.rstrip(), profile_path - - -def style_for(path: Path) -> str | None: - name = path.name - suffix = path.suffix.lower() - if name.endswith((".sh.template", ".bash.template", ".zsh.template")): - return "hash" - if suffix in BLOCK_EXTENSIONS: - return "block" - if suffix in HASH_EXTENSIONS: - return "hash" - if suffix in XML_EXTENSIONS: - return "xml" - return None - - -def is_excluded(path: Path) -> bool: - if path.name in EXCLUDED_FILES: - return True - parts = path.parts - if len(parts) >= 2 and parts[0] == "gradle" and parts[1] == "wrapper": - return True - return any(part in EXCLUDED_DIRS for part in parts) - - -def tracked_files(root: Path) -> list[Path]: - try: - result = subprocess.run( - ["git", "-C", str(root), "ls-files", "-z"], - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - except (FileNotFoundError, subprocess.CalledProcessError): - return [ - path.relative_to(root) - for path in root.rglob("*") - if path.is_file() and not is_excluded(path.relative_to(root)) - ] - - paths = [] - for item in result.stdout.decode("utf-8").split("\0"): - if not item: - continue - path = Path(item) - if (root / path).is_file(): - paths.append(path) - return paths - - -def expand_requested_paths(root: Path, requested: list[str]) -> list[Path]: - if not requested: - paths = tracked_files(root) - else: - paths = [] - for item in requested: - path = (root / item).resolve() - if not path.exists(): - raise FileNotFoundError(f"Path does not exist: {item}") - if not path.is_relative_to(root): - raise ValueError( - f"Path is outside the repository root: {item!r} " - f"(resolved to {path}, root is {root})" - ) - if path.is_dir(): - for child in path.rglob("*"): - if child.is_file(): - paths.append(child.relative_to(root)) - else: - paths.append(path.relative_to(root)) - - unique = sorted(set(paths), key=lambda p: p.as_posix()) - return [ - path - for path in unique - if style_for(path) is not None and not is_excluded(path) - ] - - -def newline_for(text: str) -> str: - return "\r\n" if "\r\n" in text else "\n" - - -def build_header(notice: str, style: str, newline: str) -> str: - lines = notice.splitlines() - if style == "block": - body = newline.join(f" * {line}" if line else " *" for line in lines) - return f"/*{newline}{body}{newline} */{newline}{newline}" - if style == "hash": - body = newline.join(f"# {line}" if line else "#" for line in lines) - return f"{body}{newline}{newline}" - if style == "xml": - body = newline.join(f" ~ {line}" if line else " ~" for line in lines) - return f"{newline}{newline}" - raise ValueError(f"Unsupported comment style: {style}") - - -def split_leading_directive(text: str, style: str, newline: str) -> tuple[str, str]: - if style == "hash" and text.startswith("#!"): - line_end = text.find("\n") - if line_end == -1: - return text + newline + newline, "" - prefix = text[: line_end + 1] + newline - return prefix, strip_leading_blank_lines(text[line_end + 1 :]) - - if style == "xml" and text.startswith("") - if close != -1: - line_end = text.find("\n", close) - if line_end == -1: - return text + newline + newline, "" - prefix = text[: line_end + 1] + newline - return prefix, strip_leading_blank_lines(text[line_end + 1 :]) - - return "", strip_leading_blank_lines(text) - - -def strip_leading_blank_lines(text: str) -> str: - return re.sub(r"^(?:[ \t]*\r?\n)+", "", text) - - -def strip_existing_header(text: str, style: str) -> tuple[str, bool]: - if style == "block" and text.startswith("/*"): - close = text.find("*/") - if close != -1: - candidate = text[: close + 2] - if is_copyright_header(candidate): - return strip_leading_blank_lines(text[close + 2 :]), True - - if style == "xml" and text.startswith("") - if close != -1: - candidate = text[: close + 3] - if is_copyright_header(candidate): - return strip_leading_blank_lines(text[close + 3 :]), True - - if style == "hash": - lines = text.splitlines(keepends=True) - end = 0 - for line in lines: - stripped = line.strip() - if stripped == "" or stripped.startswith("#"): - end += len(line) - continue - break - candidate = text[:end] - if candidate and is_copyright_header(candidate): - return strip_leading_blank_lines(text[end:]), True - - return text, False - - -def is_copyright_header(text: str) -> bool: - limited = text[:5000] - return "Copyright" in limited and ( - "Licensed under" in limited or "All rights reserved" in limited - ) - - -def updated_text(text: str, notice: str, style: str) -> str: - original = text - bom = "\ufeff" if text.startswith("\ufeff") else "" - if bom: - text = text[1:] - newline = newline_for(text) - prefix, body = split_leading_directive(text, style, newline) - body, had_header = strip_existing_header(body, style) - if not had_header: - return original - return bom + prefix + build_header(notice, style, newline) + body - - -def update_file(root: Path, path: Path, notice: str, dry_run: bool) -> bool: - absolute = root / path - style = style_for(path) - if style is None: - return False - - try: - text = absolute.read_text(encoding="utf-8") - except FileNotFoundError: - print(f"Skipping missing file: {path}", file=sys.stderr) - return False - except UnicodeDecodeError: - print(f"Skipping non-UTF-8 file: {path}", file=sys.stderr) - return False - - next_text = updated_text(text, notice, style) - if next_text == text: - return False - - if not dry_run: - with absolute.open("w", encoding="utf-8", newline="") as file: - file.write(next_text) - return True - - -def main() -> int: - args = parse_args() - root = args.root.resolve() - notice, profile_path = load_notice(root, args.year) - try: - paths = expand_requested_paths(root, args.paths) - except (FileNotFoundError, ValueError) as exc: - print(f"error: {exc}", file=sys.stderr) - return 2 - dry_run = args.dry_run or args.check - - changed = [ - path - for path in paths - if update_file(root, path, notice, dry_run=dry_run) - ] - - rel_profile = profile_path.relative_to(root) - action = "Would update" if dry_run else "Updated" - print(f"Notice source: {rel_profile}") - print(f"{action} {len(changed)} file(s).") - for path in changed: - print(path.as_posix()) - - if args.check and changed: - return 1 - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.agents/skills/update-copyright/tests/test_update_copyright.py b/.agents/skills/update-copyright/tests/test_update_copyright.py deleted file mode 100644 index 8770b327..00000000 --- a/.agents/skills/update-copyright/tests/test_update_copyright.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -import subprocess -import sys -import tempfile -import unittest -from pathlib import Path - - -SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "update_copyright.py" - - -class UpdateCopyrightTest(unittest.TestCase): - def test_default_run_leaves_plain_source_without_header_unchanged(self) -> None: - with tempfile.TemporaryDirectory() as temp_dir: - root = Path(temp_dir) - self.write_profile(root) - source = root / "Foo.java" - original = "class Foo {}\n" - source.write_text(original, encoding="utf-8") - - subprocess.run(["git", "init", "-q"], cwd=root, check=True) - subprocess.run(["git", "add", "Foo.java"], cwd=root, check=True) - - result = self.run_script(root) - - self.assertEqual(result.returncode, 0, result.stderr) - self.assertIn("Updated 0 file(s).", result.stdout) - self.assertEqual(result.stderr, "") - self.assertEqual(source.read_text(encoding="utf-8"), original) - - def test_existing_header_is_updated(self) -> None: - with tempfile.TemporaryDirectory() as temp_dir: - root = Path(temp_dir) - self.write_profile(root) - source = root / "Foo.java" - source.write_text( - "/*\n" - " * Copyright 2024 ACME\n" - " * All rights reserved\n" - " */\n" - "\n" - "class Foo {}\n", - encoding="utf-8", - ) - - result = self.run_script(root, "--year", "2026", "Foo.java") - - self.assertEqual(result.returncode, 0, result.stderr) - self.assertIn("Updated 1 file(s).", result.stdout) - self.assertIn("Foo.java", result.stdout) - self.assertEqual(result.stderr, "") - self.assertEqual( - source.read_text(encoding="utf-8"), - "/*\n" - " * Copyright 2026 ACME\n" - " * All rights reserved\n" - " */\n" - "\n" - "class Foo {}\n", - ) - - def test_default_run_skips_tracked_files_deleted_from_working_tree(self) -> None: - with tempfile.TemporaryDirectory() as temp_dir: - root = Path(temp_dir) - self.write_profile(root) - source = root / "Foo.java" - source.write_text("class Foo {}\n", encoding="utf-8") - - subprocess.run(["git", "init", "-q"], cwd=root, check=True) - subprocess.run(["git", "add", "Foo.java"], cwd=root, check=True) - source.unlink() - - result = subprocess.run( - [ - sys.executable, - str(SCRIPT), - "--root", - str(root), - "--dry-run", - ], - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - - self.assertEqual(result.returncode, 0, result.stderr) - self.assertIn("Would update 0 file(s).", result.stdout) - self.assertEqual(result.stderr, "") - - @staticmethod - def run_script(root: Path, *args: str) -> subprocess.CompletedProcess[str]: - return subprocess.run( - [ - sys.executable, - str(SCRIPT), - "--root", - str(root), - *args, - ], - check=False, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - - @staticmethod - def write_profile(root: Path) -> None: - copyright_dir = root / ".idea" / "copyright" - copyright_dir.mkdir(parents=True) - (copyright_dir / "profiles_settings.xml").write_text( - '' - '' - "\n", - encoding="utf-8", - ) - (copyright_dir / "Default.xml").write_text( - '' - "" - '" - "\n", - encoding="utf-8", - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/.agents/skills/version-bumped/SKILL.md b/.agents/skills/version-bumped/SKILL.md deleted file mode 100644 index 86ca53df..00000000 --- a/.agents/skills/version-bumped/SKILL.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -name: version-bumped -description: > - Verify the current branch has bumped `version.gradle.kts` strictly above - the base ref; invoke `/bump-version` to auto-recover if not. Composable: - other modifying skills (`dependency-update`, `bump-gradle`, - `java-to-kotlin`, `move-files`) call this as their final step so a - `./gradlew build` or `publishToMavenLocal` can never overwrite a - previously published Maven Local artifact that integration tests in - consumer repos depend on. ---- - -# Ensure version is bumped - -This skill is the agent-facing wrapper around -`.agents/skills/version-bumped/scripts/version-bumped.sh`. The script is the source of truth for -"has this branch advanced the version vs base?"; this skill just runs it -and, if it fails, invokes `/bump-version` and re-runs to confirm. - -The same logic is enforced as a hook -(`.agents/scripts/publish-version-gate.sh`) that fires before any -`./gradlew โ€ฆ (build|publish|publishToMavenLocal)` invocation, so even -direct gradle calls cannot bypass it. This skill exists for the -cooperative path โ€” other skills calling it before they finish, so the -user is never surprised by a blocked gradle command later. - -The premise is simple: any feature branch is a candidate for publishing, -even when the only change is the version bump itself (sometimes the bump -is the entire change, used to retry a publish that failed because Maven -repositories were overloaded). So if the branch differs from base at all, -the version must advance. - -## When to use - -- Automatically: as the final step of any skill that may change files on - the branch. -- Manually (`/version-bumped`): before running `./gradlew build` or - `./gradlew publishToMavenLocal` on a feature branch when you are not - sure whether the version has already been bumped. - -## Procedure - -1. Run the deterministic check: - - ```bash - .agents/skills/version-bumped/scripts/version-bumped.sh - ``` - - Honor `VERSION_BUMPED_BASE` if the user has set a non-default base ref - (e.g. `origin/master`, or a release branch). - -2. Interpret the exit code: - - - **0** โ€” Done. Either the repository has no root `version.gradle.kts` - (the version check is `N/A`), the branch has no diff vs base, or the - version is already strictly greater. Report a one-line confirmation - and stop. - - **1** โ€” Block. The script's stderr explains which check failed. - Proceed to step 3. - - **2** โ€” Configuration error (no merge-base, parse failure on - `version.gradle.kts`). Do **not** invoke `/bump-version` - automatically. Surface the script's stderr to the user and stop. - -3. On exit 1, invoke `/bump-version` to perform the actual bump. That - skill owns the policy (snapshot numbering, the commit subject, the - rebuild, dependency-report regeneration, and the conflict rule). Do - not duplicate its work here. - -4. After `/bump-version` finishes, re-run the deterministic check. If it - now passes, report the new version on the branch. If it still fails, - surface the stderr unchanged and stop โ€” do not loop. - -## Why this skill is separate from `/bump-version` - -`/bump-version` is the **action** (it edits `version.gradle.kts`, -commits, rebuilds, may commit reports). `/version-bumped` is the -**guard** (read-only check, optional auto-recovery). Skills that want to -say "make sure the branch has a bumped version" should call -`/version-bumped`, not `/bump-version`, because the guard is a no-op when -the bump is already done โ€” calling `/bump-version` unconditionally would -double-bump on every chained skill invocation. - -## Relationship to `checkVersionIncrement` - -The Gradle task `checkVersionIncrement` (in `buildSrc/.../publish/`) -asks a different question: *"is this version already in the remote -Maven metadata?"* It runs on GitHub Actions feature-branch pushes and -fetches the Spine SDK Artifact Registry. The two checks are -complementary โ€” neither subsumes the other. - -## See also - -- `.agents/version-policy.md` โ€” when the version gate applies. -- `.agents/skills/bump-version/SKILL.md` โ€” the bump procedure itself. -- `.agents/skills/pre-pr/SKILL.md` โ€” uses the same check at PR time - (step 2). -- `.agents/skills/version-bumped/scripts/version-bumped.sh` โ€” the deterministic check. -- `.agents/scripts/publish-version-gate.sh` โ€” the hook that enforces the - rule on `./gradlew` invocations. diff --git a/.agents/skills/version-bumped/scripts/version-bumped.sh b/.agents/skills/version-bumped/scripts/version-bumped.sh deleted file mode 100755 index f050a5b7..00000000 --- a/.agents/skills/version-bumped/scripts/version-bumped.sh +++ /dev/null @@ -1,276 +0,0 @@ -#!/usr/bin/env bash -# -# Verifies that a feature branch which differs from the base ref also -# bumps `version.gradle.kts` strictly above the base version. Mirrors the -# universal "every branch advances the version" policy: a branch with any -# changes is a candidate for publishing โ€” sometimes the only change is the -# bump itself, used to retry a publish that failed because Maven -# repositories were overloaded. -# -# Exit codes: -# 0 โ€” OK: repo has no root `version.gradle.kts`, OR branch has no diff -# vs base, OR working-tree version is strictly greater than base -# version. -# 1 โ€” Block: branch differs from base but version is unchanged or -# decreased. Stderr points to `/bump-version`. -# 2 โ€” Configuration error (bad base ref, parse failure). Stderr explains. -# -# Inputs (env, all optional): -# VERSION_BUMPED_BASE Base ref to compare against. Default: master, -# then main if master is absent. -# VERSION_BUMPED_KEY Name of the `extra` property holding the -# publishing version (e.g. `versionToPublish`, -# `validationVersion`, `bootstrapVersion`). When -# set, bypasses auto-discovery. Useful for repos -# that don't follow the `version = extra["โ€ฆ"]` -# pattern in `build.gradle.kts`. -# VERSION_BUMPED_QUIET When `1`, suppress the "OK" line on stdout. -# The publish-version-gate hook sets this. -# -# Publishing-key discovery: -# The publishing version's variable name varies across Spine repos -# (`versionToPublish`, `validationVersion`, `compilerVersion`, โ€ฆ). -# `version.gradle.kts` may also declare other `val xxxVersion by extra(...)` entries -# that are *dependency* versions of other Spine modules โ€” not this -# project's own publishing version โ€” so the key cannot be picked by -# inspecting `version.gradle.kts` alone. -# -# The canonical source is `build.gradle.kts`, which assigns -# `version = extra["KEY"]!!`. This script scans for that pattern, -# picks the unique key, and parses its value from `version.gradle.kts`. -# If `build.gradle.kts` does not contain such a line, the script falls -# back to `versionToPublish`. Set `VERSION_BUMPED_KEY` to override. -# -# Notes: -# * Companion to the Gradle task `checkVersionIncrement` (see -# `buildSrc/.../publish/CheckVersionIncrement.kt`). The Gradle task -# asks "is this version already in remote Maven metadata?" โ€” this -# script asks the simpler local question "has this branch advanced -# the version vs base?". The two checks are complementary; neither -# subsumes the other. -# * The working tree is included in the change-detection so the gate -# reflects what `./gradlew build` would actually publish. -# -set -eu - -repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || { - echo "version-bumped: not inside a git repository" >&2 - exit 2 -} -cd "$repo_root" - -version_file="version.gradle.kts" - -# --- N/A: not a versioned project ---------------------------------------- -if [ ! -f "$version_file" ]; then - [ "${VERSION_BUMPED_QUIET:-0}" = "1" ] || echo "version-bumped: N/A (no root version.gradle.kts)" - exit 0 -fi - -# --- Resolve base ref ---------------------------------------------------- -base="${VERSION_BUMPED_BASE:-}" -if [ -z "$base" ]; then - if git show-ref --verify --quiet refs/heads/master; then - base=master - elif git show-ref --verify --quiet refs/heads/main; then - base=main - else - echo "version-bumped: no master or main branch found; set VERSION_BUMPED_BASE" >&2 - exit 2 - fi -fi - -if ! git rev-parse --verify --quiet "$base" >/dev/null; then - echo "version-bumped: base ref '$base' does not resolve" >&2 - exit 2 -fi - -# When we are on the base branch itself, there is nothing to gate. -current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") -if [ "$current_branch" = "$base" ] || [ "$current_branch" = "${base##*/}" ]; then - [ "${VERSION_BUMPED_QUIET:-0}" = "1" ] || echo "version-bumped: on base branch ($current_branch); nothing to gate" - exit 0 -fi - -merge_base=$(git merge-base HEAD "$base" 2>/dev/null) || { - echo "version-bumped: cannot find merge-base of HEAD and '$base'" >&2 - exit 2 -} - -# --- Detect any branch divergence vs base (committed/worktree/untracked) - -committed=$(git diff --name-only "$merge_base"..HEAD 2>/dev/null || true) -worktree=$(git diff --name-only HEAD 2>/dev/null || true) -untracked=$(git ls-files --others --exclude-standard 2>/dev/null || true) - -if [ -z "$committed" ] && [ -z "$worktree" ] && [ -z "$untracked" ]; then - [ "${VERSION_BUMPED_QUIET:-0}" = "1" ] || echo "version-bumped: no changes vs $base" - exit 0 -fi - -# --- Discover the publishing-version key --------------------------------- -# Source of truth is `build.gradle.kts` (or `build.gradle`). Two shapes are -# recognised, in order: -# -# a) version = extra["KEY"] -# b) version = IDENTIFIER (with `val IDENTIFIER ... by extra` nearby) -# -# Single or double quotes are accepted in shape (a). If multiple distinct -# keys appear across shapes, the script refuses to guess and asks the user -# to set VERSION_BUMPED_KEY. -# -# Return codes: -# 0 โ€” printed a unique key on stdout -# 1 โ€” no candidates found (caller should fall back) -# 2 โ€” ambiguous; diagnostic already on stderr -discover_key() { - local files keys_a keys_b keys count - files="" - [ -f build.gradle.kts ] && files="build.gradle.kts" - [ -f build.gradle ] && files="$files build.gradle" - [ -z "$files" ] && return 1 - # Shape (a): version = extra["KEY"] - # Anchored to start-of-line (modulo leading whitespace) so that comments - # like `// version = extra["x"]` and identifiers like `fooversion = ...` - # don't produce false matches. - # shellcheck disable=SC2086 - keys_a=$(grep -hE '^[[:space:]]*version[[:space:]]*=[[:space:]]*extra[[:space:]]*\[[[:space:]]*["'"'"'][^"'"'"']+["'"'"']' $files 2>/dev/null \ - | sed -nE 's/.*extra[[:space:]]*\[[[:space:]]*["'"'"']([^"'"'"']+)["'"'"'].*/\1/p') - # Shape (b): version = IDENTIFIER (bare Kotlin identifier, no '[' or '"'). - # Only accept the identifier if the same file also declares - # `val IDENTIFIER[: String]? by extra` โ€” otherwise it's a plain local - # variable (common in Groovy `build.gradle`), not an `extra` property we - # can resolve in `version.gradle.kts`. - local candidates_b cand - # shellcheck disable=SC2086 - candidates_b=$(grep -hE '^[[:space:]]*version[[:space:]]*=[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*$' $files 2>/dev/null \ - | sed -nE 's/^[[:space:]]*version[[:space:]]*=[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*$/\1/p') - keys_b="" - for cand in $candidates_b; do - # shellcheck disable=SC2086 - if grep -hE "^[[:space:]]*val[[:space:]]+${cand}([[:space:]]*:[[:space:]]*String)?[[:space:]]+by[[:space:]]+extra([^A-Za-z0-9_]|\$)" $files >/dev/null 2>&1; then - keys_b="${keys_b}${cand} -" - fi - done - keys=$(printf '%s\n%s' "$keys_a" "$keys_b" | sed '/^$/d' | sort -u) - [ -z "$keys" ] && return 1 - count=$(printf '%s\n' "$keys" | wc -l | tr -d ' ') - if [ "$count" -gt 1 ]; then - { - echo "version-bumped: ambiguous publishing key in build scripts:" - while IFS= read -r k; do printf ' %s\n' "$k"; done <<< "$keys" - echo " Set VERSION_BUMPED_KEY to disambiguate." - } >&2 - return 2 - fi - printf '%s' "$keys" -} - -key="${VERSION_BUMPED_KEY:-}" -if [ -z "$key" ]; then - set +e - key=$(discover_key) - rc=$? - set -e - if [ "$rc" = "2" ]; then - exit 2 - fi - if [ "$rc" != "0" ] || [ -z "$key" ]; then - key="versionToPublish" - fi -fi - -# --- Parse a `val KEY by extra(...)` from a Gradle file content ---------- -# Handles three shapes (per .agents/skills/bump-version/SKILL.md step 2): -# 1. val KEY[: String]? by extra("X") โ€” literal extra -# 2. val SRC[: String]? by extra("X") โ€” alias chain via extra -# val KEY[: String]? by extra(SRC) -# 3. val SRC[: String]? = "X" โ€” alias chain via plain val -# val KEY[: String]? by extra(SRC) -# The key name is parameterized so that any project-specific name works -# (versionToPublish, validationVersion, bootstrapVersion, botVersion, โ€ฆ). -parse_version() { - local content="$1" name="$2" - local v varName - # Shape 1: literal. - v=$(printf '%s' "$content" \ - | grep -E "val[[:space:]]+${name}([[:space:]]*:[[:space:]]*String)?[[:space:]]+by[[:space:]]+extra\(\"" \ - | head -n1 \ - | sed -nE 's/.*extra\("([^"]+)".*/\1/p') - if [ -n "$v" ]; then - printf '%s' "$v" - return 0 - fi - # Shapes 2 & 3: extract the alias source identifier. - varName=$(printf '%s' "$content" \ - | grep -E "val[[:space:]]+${name}([[:space:]]*:[[:space:]]*String)?[[:space:]]+by[[:space:]]+extra\(" \ - | head -n1 \ - | sed -nE 's/.*extra\(([A-Za-z_][A-Za-z0-9_]*)\).*/\1/p') - if [ -n "$varName" ]; then - # Shape 2: source is `val SRC ... by extra("X")`. - v=$(printf '%s' "$content" \ - | grep -E "val[[:space:]]+${varName}([[:space:]]*:[[:space:]]*String)?[[:space:]]+by[[:space:]]+extra\(\"" \ - | head -n1 \ - | sed -nE 's/.*extra\("([^"]+)".*/\1/p') - if [ -n "$v" ]; then - printf '%s' "$v" - return 0 - fi - # Shape 3: source is `val SRC[: String]? = "X"`. - v=$(printf '%s' "$content" \ - | grep -E "val[[:space:]]+${varName}([[:space:]]*:[[:space:]]*String)?[[:space:]]*=[[:space:]]*\"" \ - | head -n1 \ - | sed -nE 's/.*=[[:space:]]*"([^"]+)".*/\1/p') - if [ -n "$v" ]; then - printf '%s' "$v" - return 0 - fi - fi - return 1 -} - -head_content=$(cat "$version_file" 2>/dev/null || true) -head_version=$(parse_version "$head_content" "$key" || true) -if [ -z "$head_version" ]; then - echo "version-bumped: cannot parse '$key' from working-tree $version_file" >&2 - exit 2 -fi - -# Base content may legitimately not exist (file newly introduced). -base_content=$(git show "$base:$version_file" 2>/dev/null || true) -if [ -z "$base_content" ]; then - [ "${VERSION_BUMPED_QUIET:-0}" = "1" ] || echo "version-bumped: $version_file newly introduced at $head_version; treating as bumped" - exit 0 -fi - -base_version=$(parse_version "$base_content" "$key" || true) -if [ -z "$base_version" ]; then - echo "version-bumped: cannot parse '$key' from $base:$version_file" >&2 - exit 2 -fi - -# --- Strict-greater comparison via `sort -V` ----------------------------- -if [ "$head_version" = "$base_version" ]; then - cmp="equal" -elif [ "$(printf '%s\n%s\n' "$base_version" "$head_version" | sort -V | tail -n1)" = "$head_version" ]; then - cmp="greater" -else - cmp="lesser" -fi - -if [ "$cmp" = "greater" ]; then - [ "${VERSION_BUMPED_QUIET:-0}" = "1" ] || echo "version-bumped: OK ($key: $base_version -> $head_version)" - exit 0 -fi - -cat >&2 < - Write, edit, and restructure user-facing and developer-facing documentation. - Use when asked to create/update docs such as `README.md`, `docs/**`, and - other Markdown documentation, including keeping docs navigation data in sync; - when drafting tutorials, guides, troubleshooting pages, or migration notes; and - when improving inline API documentation (KDoc) and examples. ---- - -# Write documentation (repo-specific) - -## Decide the target and audience - -- Identify the target reader: end user, contributor, maintainer, or tooling/automation. -- Identify the task type: new doc, update, restructure, or documentation audit. -- Identify the acceptance criteria: โ€œwhat is correct when the reader is done?โ€ - -## Choose where the content should live - -- Prefer updating an existing doc over creating a new one. -- Place content in the most discoverable location: - - `README.md`: project entry point and โ€œwhat is this?โ€. - - `docs/`: longer-form docs (follow existing conventions in that tree). - - Source KDoc: API usage, examples, and semantics that belong with the code. - -## Keep docs navigation in sync - -- When adding, removing, moving, or renaming a page under - `docs/content/docs/

/`, keep the current version's matching - `sidenav.yml` in sync. -- Use `docs/data/versions.yml` to identify the current documentation version for - that section. The current version is the entry with `is_main: true`; its - `version_id` maps to `docs/data/docs/
//sidenav.yml`. -- Do not update historical version entries or their navigation files unless the - user explicitly asks to edit that historical version. -- Map page files to `file_path` values relative to the current version's - `content_path`, without `.md`; `_index.md` maps to its directory path, such as - `01-getting-started/_index.md` -> `01-getting-started`. -- Keep each `page` label aligned with the page frontmatter `title` unless the - existing navigation intentionally uses a shorter reader-facing label. -- Preserve the existing ordering, nesting, keys, comments, and YAML quoting - style. Remove nav entries for deleted pages and update `file_path` values for - moved pages. -- If a docs content change should not appear in navigation, say so explicitly in - the final response. - -## Follow local documentation conventions - -- Follow `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. -- Use fenced code blocks for commands and examples; format file/dir names as code. -- When referencing a documentation page or section in body prose, use typographic - double quotation marks only if the visible reference text is the actual page or - section title, such as the โ€œGetting startedโ€ page or the โ€œTroubleshootingโ€ - section. The title normally starts with a capital letter. Do not add these - quotes around generic or descriptive links such as โ€œthis pageโ€, โ€œthe next - sectionโ€, โ€œdeclaring constraintsโ€, or `4.3`, even if they point to a page or - section. Do not add these quotes in โ€œWhatโ€™s nextโ€ sections or navigation - elements. Keep file paths, identifiers, frontmatter values, navigation labels, - and Markdown link labels in their expected syntax. -- In Markdown files, prefer footnote-style reference links for external `https://` - targets instead of inline links. Write readable body text like - `[label][short-id]`, then place the URL definition near the end of the file, - such as `[short-id]: https://example.com/long/path`. Keep reference IDs short - and descriptive. Inline links are still fine for local relative paths. -- Avoid widows, runts, orphans, and rivers by reflowing paragraphs when needed. - -## Make docs actionable - -- Prefer steps the reader can execute (commands + expected outcome). -- Prefer concrete examples over abstract descriptions. -- Include prerequisites (versions, OS, environment) when they are easy to miss. -- Use consistent terminology (match code identifiers and existing docs). - -## KDoc-specific guidance - -- For public/internal APIs, include at least one example snippet demonstrating common usage. -- When converting from Javadoc/inline comments to KDoc: - - Remove HTML like `

` and preserve meaning. - - Prefer short paragraphs and blank lines over HTML formatting. - -## Validate changes - -- For code changes, follow `.agents/running-builds.md`. -- For documentation-only changes in Kotlin/Java sources, prefer `./gradlew dokka`. diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml deleted file mode 100644 index 44eaa4e2..00000000 --- a/.agents/skills/writer/agents/openai.yaml +++ /dev/null @@ -1,5 +0,0 @@ -interface: - display_name: "Writer" - short_description: "Write and update user/developer docs" - default_prompt: "Write or revise documentation in this repository (for example: README.md, docs/**, CONTRIBUTING.md, and API documentation/KDoc). Follow local documentation guidelines in .agents/*.md, keep changes concise and actionable, and include concrete examples and commands where appropriate." - diff --git a/.agents/skills/writer/assets/templates/doc-page.md b/.agents/skills/writer/assets/templates/doc-page.md deleted file mode 100644 index f405b71e..00000000 --- a/.agents/skills/writer/assets/templates/doc-page.md +++ /dev/null @@ -1,23 +0,0 @@ -# Title - -## Goal - -State what the reader will accomplish. - -## Prerequisites - -- List versions/tools the reader needs. - -## Steps - -1. Do the first thing. -2. Do the next thing. - -## Verify - -Show how the reader can confirm success. - -## Troubleshooting - -- Common failure: likely cause โ†’ fix. - diff --git a/.agents/skills/writer/assets/templates/kdoc-example.md b/.agents/skills/writer/assets/templates/kdoc-example.md deleted file mode 100644 index fdbd9b6a..00000000 --- a/.agents/skills/writer/assets/templates/kdoc-example.md +++ /dev/null @@ -1,11 +0,0 @@ -````kotlin -/** - * Explain what this API does in one sentence. - * - * ## Example - * ```kotlin - * // Show the typical usage pattern. - * val result = doThing() - * ``` - */ -```` diff --git a/.agents/skills/writer/assets/templates/kotlin-java-example.md b/.agents/skills/writer/assets/templates/kotlin-java-example.md deleted file mode 100644 index 5517516f..00000000 --- a/.agents/skills/writer/assets/templates/kotlin-java-example.md +++ /dev/null @@ -1,13 +0,0 @@ -{{< code-tabs langs="Kotlin, Java">}} - -{{< code-tab lang="Kotlin" >}} -```kotlin -``` -{{< /code-tab >}} - -{{< code-tab lang="Java" >}} -```java -``` -{{< /code-tab >}} - -{{< /code-tabs >}} diff --git a/.agents/testing.md b/.agents/testing.md deleted file mode 100644 index f81bdbf3..00000000 --- a/.agents/testing.md +++ /dev/null @@ -1,8 +0,0 @@ -# ๐Ÿงช Testing - -- Do not use mocks, use stubs. -- Prefer [Kotest assertions][kotest-assertions] over assertions from JUnit or Google Truth. -- Generate unit tests for APIs (handles edge cases/scenarios). -- Supply scaffolds for typical Kotlin patterns (`when`, sealed classes). - -[kotest-assertions]: https://kotest.io/docs/assertions/assertions.html diff --git a/.agents/version-policy.md b/.agents/version-policy.md deleted file mode 100644 index 3e8abd54..00000000 --- a/.agents/version-policy.md +++ /dev/null @@ -1,19 +0,0 @@ -# Version policy - -When a repository has `version.gradle.kts` at the project root, it follows the -[Spine SDK Versioning policy][wiki-versioning]. The version is kept in that -file and follows [Semantic Versioning 2.0.0][semver] with Spine-specific -extensions (snapshot `NUMBER`, patch, and flavor suffixes). - -For repositories with root `version.gradle.kts`, PRs without a version bump -fail CI. Repositories without that file are not versioned Gradle Build Tools -projects; their version check is not applicable, and agents must not create -`version.gradle.kts` just to satisfy `/pre-pr`. - -For the bump procedure in repositories that have root `version.gradle.kts` โ€” -version-number selection, the commit-message convention, the rebuild, -dependency-report updates, and conflict resolution โ€” use the -[`bump-version`](skills/bump-version/SKILL.md) skill. - -[semver]: https://semver.org/ -[wiki-versioning]: https://github.com/SpineEventEngine/documentation/wiki/Versioning diff --git a/.agents/widow-runt-orphan.jpg b/.agents/widow-runt-orphan.jpg deleted file mode 100644 index 284b02a4..00000000 Binary files a/.agents/widow-runt-orphan.jpg and /dev/null differ diff --git a/.claude/agents b/.claude/agents new file mode 120000 index 00000000..18e96c95 --- /dev/null +++ b/.claude/agents @@ -0,0 +1 @@ +../.agents/shared/claude/agents \ No newline at end of file diff --git a/.claude/agents/dependency-audit.md b/.claude/agents/dependency-audit.md deleted file mode 100644 index 9db010fe..00000000 --- a/.claude/agents/dependency-audit.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: dependency-audit -description: Audits changes to dependency declarations under `buildSrc/src/main/kotlin/io/spine/dependency/` โ€” catches accidental version downgrades, BOM mismatches, missing deprecation markers, copyright drift, and convention drift. Use proactively whenever a diff touches that directory, or when the user asks "audit this dependency bump". Read-only; does not run builds. -tools: Read, Grep, Glob, Bash -model: claude-haiku-4-5-20251001 ---- - -Follow the `dependency-audit` skill exactly: - -- Skill: `.agents/skills/dependency-audit/SKILL.md` -- The skill owns the per-area checks (version sanity, naming and structure, - deprecation discipline, convention drift, cross-cutting) and the output - format (Must fix / Should fix / Nits + one-line verdict). -- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff`, - `git grep`, and related read-only inspection. Do not run builds. -- **Be fast.** Fetch the full unified diff once, work from it, and `Read` - individual files only when the skill's step 2 budget allows. Issue - independent `Grep`/`Bash` calls in parallel within a single response; - do not halt at the first failure โ€” collect all findings and report once. diff --git a/.claude/agents/kotlin-review.md b/.claude/agents/kotlin-review.md deleted file mode 100644 index 74583aa3..00000000 --- a/.claude/agents/kotlin-review.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: kotlin-review -description: Reviews Kotlin (and Java) changes against Spine coding guidelines, safety rules, and testing policy. Use proactively after any non-trivial code edit, before opening a PR, or when the user asks for a code review. Read-only; does not run builds. -tools: Read, Grep, Glob, Bash -model: inherit ---- - -Follow the `kotlin-review` skill exactly: - -- Skill: `.agents/skills/kotlin-review/SKILL.md` -- The skill owns the procedure, the checks (Kotlin idioms, safety rules, - testing policy, version-gate applicability), and the output format - (Must fix / Should fix / Nits + one-line verdict). -- Stay in scope: code only. If a documentation issue surfaces, note it - briefly as a Nit pointing at the `review-docs` agent. -- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff` - and related read-only inspection. Do not run builds. diff --git a/.claude/agents/review-docs.md b/.claude/agents/review-docs.md deleted file mode 100644 index 0481b240..00000000 --- a/.claude/agents/review-docs.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: review-docs -description: Reviews documentation changes โ€” KDoc/Javadoc inside Kotlin/Java sources and Markdown docs (`README.md`, `docs/**`) โ€” against Spine documentation conventions. Use proactively when a diff touches doc comments or Markdown, before opening a doc-affecting PR, or when the user asks for a documentation review. Read-only; does not run builds. -tools: Read, Grep, Glob, Bash -model: inherit ---- - -Follow the `review-docs` skill exactly: - -- Skill: `.agents/skills/review-docs/SKILL.md` -- The skill owns the review procedure, the per-area checks (KDoc/Javadoc, - Markdown, prose flow, terminology), and the output format - (Must fix / Should fix / Nits + one-line verdict). -- Scope yourself to documentation only. If you spot a code-quality issue, - surface it briefly as a Nit pointing at the `kotlin-review` agent โ€” - do not expand the review. -- Read-only: use `Read`, `Grep`, `Glob`, and `Bash` solely for `git diff` - and related read-only inspection. Do not run builds. diff --git a/.claude/commands b/.claude/commands new file mode 120000 index 00000000..ad85cd80 --- /dev/null +++ b/.claude/commands @@ -0,0 +1 @@ +../.agents/shared/claude/commands \ No newline at end of file diff --git a/.claude/commands/bump-gradle.md b/.claude/commands/bump-gradle.md deleted file mode 100644 index f9078802..00000000 --- a/.claude/commands/bump-gradle.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: Upgrade the Gradle wrapper to the latest release. -argument-hint: "[gradle-version]" -allowed-tools: Read, Edit, Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*), WebFetch ---- - -Follow the `bump-gradle` skill exactly: - -- Skill: `.agents/skills/bump-gradle/SKILL.md` -- Read the skill first. -- Use https://docs.gradle.org/current/release-notes.html as the source of truth for the latest version. Do NOT rely on remembered Gradle versions. -- If the user supplied a version: $ARGUMENTS, use it; otherwise read it from the release notes. -- Commit the wrapper change and dependency report change in separate commits per the skill. diff --git a/.claude/commands/bump-version.md b/.claude/commands/bump-version.md deleted file mode 100644 index 82e18b59..00000000 --- a/.claude/commands/bump-version.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: Bump the project version in version.gradle.kts per Spine SDK versioning policy. -argument-hint: "[snapshot|minor|major]" -allowed-tools: Read, Edit, Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(./gradlew:*) ---- - -Follow the `bump-version` skill exactly: - -- Skill: `.agents/skills/bump-version/SKILL.md` -- Read the skill first; it owns the policy (snapshot numbering, version conflicts, rebuilding reports). -- Increment requested by the user: $ARGUMENTS (treat as "snapshot" if empty). -- Inspect `git status --short` before editing; preserve unrelated user changes. -- Stop and ask the user before committing. diff --git a/.claude/commands/dependency-update.md b/.claude/commands/dependency-update.md deleted file mode 100644 index 9c54da14..00000000 --- a/.claude/commands/dependency-update.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Refresh external dependency versions in buildSrc to their latest non-snapshot release. -argument-hint: "[--dry-run] [paths...]" -allowed-tools: Read, Edit, Write, Grep, Glob, WebFetch, Bash(git status:*), Bash(git diff:*), Bash(./gradlew build:*), Bash(./gradlew clean build:*) ---- - -Follow the `dependency-update` skill exactly: - -- Skill: `.agents/skills/dependency-update/SKILL.md` -- Scope / flags: $ARGUMENTS -- Walk every dependency object under `buildSrc/src/main/kotlin/io/spine/dependency/`. -- Source of truth per artifact: the URL in the file's comment (line `// https://...` or KDoc `@see`). If no URL, fall back to Maven Central AND back-fill the URL comment. -- Filter out snapshots, RCs, alphas, betas, milestones, EAPs, and `-dev` builds. -- Apply the edit. Do NOT commit; emit the report described in the skill. -- Flag `local/` (Spine SDK) updates separately so the user can decide whether to bump in lockstep. -- After the run, suggest the user review the diff and run `./gradlew build` (or `./gradlew clean build` if proto files participate). For `local/` bumps, suggest `./gradlew buildDependants`. diff --git a/.claude/commands/java-to-kotlin.md b/.claude/commands/java-to-kotlin.md deleted file mode 100644 index 6f2c072f..00000000 --- a/.claude/commands/java-to-kotlin.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: Convert Java files to idiomatic Kotlin, including Javadoc -> KDoc. -argument-hint: "" -allowed-tools: Read, Edit, Write, Bash(./gradlew:*), Bash(git status:*), Grep, Glob ---- - -Follow the `java-to-kotlin` skill exactly: - -- Skill: `.agents/skills/java-to-kotlin/SKILL.md` -- Target: $ARGUMENTS -- Preserve behavior. Convert Javadoc to KDoc, `@Nullable` to nullable Kotlin types, getters/setters to properties, static methods to companion objects or top-level functions. -- After each file, run `./gradlew compileKotlin` (or the relevant module's compile task) to verify. -- Honor `.agents/coding-guidelines.md` for Kotlin idioms. diff --git a/.claude/commands/move-files.md b/.claude/commands/move-files.md deleted file mode 100644 index 25885f9d..00000000 --- a/.claude/commands/move-files.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Move or rename files/directories, updating all references and build metadata. -argument-hint: " " -allowed-tools: Read, Edit, Bash(git mv:*), Bash(git status:*), Bash(git ls-files:*), Grep, Glob ---- - -Follow the `move-files` skill exactly: - -- Skill: `.agents/skills/move-files/SKILL.md` -- Operation: $ARGUMENTS -- Preflight (run `git status --short`, classify scope) -> Search for all old identifiers -> Move with `git mv` -> Repair references (imports, build metadata, docs) -> Verify. -- Report: Moved[], UpdatedRefs[], Verification[], Risks[]. diff --git a/.claude/commands/pre-pr.md b/.claude/commands/pre-pr.md deleted file mode 100644 index 24499cc5..00000000 --- a/.claude/commands/pre-pr.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -description: Run the applicable pre-PR checklist (version gate, build/check, reviewers) and write a sentinel so `gh pr create` is unblocked. -argument-hint: "[base-ref]" -allowed-tools: Read, Write, Grep, Glob, Agent, Bash ---- - -Follow the `pre-pr` skill exactly: - -- Skill: `.agents/skills/pre-pr/SKILL.md` -- Base ref: $ARGUMENTS (treat empty as `master`). -- Detect whether the repository-root `version.gradle.kts` exists. If it is - absent at both the base ref and `HEAD`, the version check is `N/A`; do not - create the file and do not ask for `/bump-version`. -- Run the build/check command selected by the skill and - `.agents/running-builds.md`. The command may be Gradle or non-Gradle. -- Dispatch the reviewers as Claude subagents in parallel โ€” send a single - message with multiple Agent tool uses: - - `kotlin-review` when `.kt|.kts|.java` files changed. - - `review-docs` when `.md` files or KDoc inside sources changed. - - `dependency-audit` when any file under - `buildSrc/src/main/kotlin/io/spine/dependency/` changed. -- Pass the version-check status to reviewers. If it is `N/A`, tell them: - "This repository has no root `version.gradle.kts`; a version bump is not - applicable and must not be reported as missing." -- Each reviewer is read-only; do not pass it edit tools. -- On any reviewer returning `REQUEST CHANGES`, treat the overall result - as `FAIL` and stop before writing the sentinel as `PASS`. -- Sentinel location: `$(git rev-parse --show-toplevel)/.git/pre-pr.ok`, - format per the skill (`head=`, `branch=`, `status=`, `timestamp=`, - `build=`, `reviewers=`, `version=`). Use `git rev-parse HEAD` for the - SHA and `date -u +%Y-%m-%dT%H:%M:%SZ` for the timestamp. -- Do NOT run `gh pr create`. That is the user's next step. diff --git a/.claude/commands/review-docs.md b/.claude/commands/review-docs.md deleted file mode 100644 index f8043f0e..00000000 --- a/.claude/commands/review-docs.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: Review documentation changes (KDoc/Javadoc and Markdown) against Spine documentation conventions. -argument-hint: "[base-ref | --staged | paths...]" -allowed-tools: Read, Grep, Glob, Bash(git diff:*), Bash(git log:*), Bash(git status:*), Bash(git rev-parse:*), Bash(git ls-files:*) ---- - -Follow the `review-docs` skill exactly: - -- Skill: `.agents/skills/review-docs/SKILL.md` -- Scope / flags: $ARGUMENTS - - Empty: review the current branch's diff against `master` (`git diff master...HEAD`). - - `--staged`: review staged changes only (`git diff --staged`). - - A base ref (e.g. `master`, `origin/master`, a commit SHA): review `git diff ...HEAD`. - - Explicit paths: limit the review to those paths in addition to the diff scope. -- The skill owns the procedure, the per-area checks (KDoc/Javadoc, Markdown, - prose flow, terminology), and the output format (Must fix / Should fix / - Nits + one-line verdict). -- Stay in scope: documentation only. If a code-quality issue surfaces, - note it briefly as a Nit pointing at `/review` (or the `kotlin-review` - agent) โ€” do not expand the review. -- Read-only: do not edit files, do not run builds. diff --git a/.claude/commands/run-build.md b/.claude/commands/run-build.md deleted file mode 100644 index 8a8d84ca..00000000 --- a/.claude/commands/run-build.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Build the project the right way based on what changed (proto vs. Kotlin/Java vs. docs). -allowed-tools: Bash(./gradlew:*), Bash(git status:*), Bash(git diff:*) ---- - -Decide which build to run by looking at `git status --short` and `git diff --name-only`: - -- If any `.proto` files changed: `./gradlew clean build` -- Else if Kotlin or Java source changed: `./gradlew build` -- Else if only docs/comments changed (KDoc / Javadoc / Markdown): `./gradlew dokka`. Tests are NOT required for doc-only changes. - -Report the chosen command and its result. See `.agents/running-builds.md`. diff --git a/.claude/commands/update-copyright.md b/.claude/commands/update-copyright.md deleted file mode 100644 index 076fb613..00000000 --- a/.claude/commands/update-copyright.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Refresh copyright headers from the IntelliJ profile, replacing today.year with the current year. -argument-hint: "[paths...]" -allowed-tools: Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*), Read ---- - -Follow the `update-copyright` skill exactly: - -- Skill: `.agents/skills/update-copyright/SKILL.md` -- Run: `python3 .agents/skills/update-copyright/scripts/update_copyright.py $ARGUMENTS` -- If $ARGUMENTS is empty, run once with `--dry-run`, show the output to the user, then run without `--dry-run`. -- Never add a header to a file that doesn't already have one. diff --git a/.claude/commands/write-docs.md b/.claude/commands/write-docs.md deleted file mode 100644 index b9b9a742..00000000 --- a/.claude/commands/write-docs.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: Write or update Markdown / KDoc documentation per Spine documentation conventions. -argument-hint: "" -allowed-tools: Read, Edit, Write, Grep, Glob ---- - -Follow the `writer` skill exactly: - -- Skill: `.agents/skills/writer/SKILL.md` -- Topic / target: $ARGUMENTS -- Decide audience first (end user, contributor, maintainer, tooling). -- Prefer updating an existing doc over creating a new one. -- Keep `docs/data/docs/

//sidenav.yml` in sync when adding, removing, moving, or renaming pages under `docs/content/docs/
/`. -- Honor `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`. diff --git a/.claude/settings.json b/.claude/settings.json index f7bbfb98..337c904b 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,9 +1,8 @@ { "$schema": "https://json.schemastore.org/claude-code-settings.json", + "plansDirectory": ".claude/plans", "permissions": { "allow": [ - "Bash(./gradlew:*)", - "Bash(./config/gradlew:*)", "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", @@ -15,11 +14,11 @@ "Bash(git restore:*)", "Bash(git stash:*)", "Bash(git fetch:*)", - "Bash(git push:*)", "Bash(git rev-parse:*)", "Bash(git ls-files:*)", "Bash(git mv:*)", "Bash(git submodule status:*)", + "Bash(hugo:*)", "Bash(ls:*)", "Bash(cat:*)", "Bash(head:*)", @@ -32,9 +31,12 @@ "Bash(touch:*)", "Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*)", "Bash(./config/pull)", - "Bash(./config/migrate)" + "Bash(./config/migrate)", + "Skill(pre-pr)", + "Skill(pre-pr:*)" ], "deny": [ + "Bash(git push:*)", "Bash(git reset --hard:*)", "Bash(git clean -fdx:*)", "Bash(rm -rf /:*)", @@ -46,33 +48,27 @@ "Bash(git commit:*)", "Bash(git rebase:*)", "Bash(git merge:*)", - "Bash(git cherry-pick:*)", - "Bash(./gradlew publish:*)", - "Bash(./gradlew uploadArtifacts:*)", - "Bash(./gradlew clean:*)" + "Bash(git cherry-pick:*)" ] }, "hooks": { - "PreToolUse": [ + "SessionStart": [ { - "matcher": "Edit|Write|MultiEdit", "hooks": [ { "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/protect-version-file.sh" + "command": "$CLAUDE_PROJECT_DIR/init-submodules" } ] - }, + } + ], + "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/pre-pr-gate.sh" - }, - { - "type": "command", - "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/publish-version-gate.sh" + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/secret-scan-gate.sh" } ] } diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 81c8d500..039657be 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -10,8 +10,27 @@ repository root โ€” read it first. If `.agents/project.md` exists, read it before reviewing. It provides the language, architecture, role, and code review checklist for this specific repo. -Additional guidelines are in `.agents/` โ€” see `.agents/_TOC.md` for the index -(if present; Hugo repos do not include this file). +Additional guidelines are in `.agents/guidelines/` โ€” see +`.agents/guidelines/_TOC.md` for the index. + +## Do not review + +Never review `gradlew` or `gradlew.bat` in any repository, including `config`. +These files are provided by Gradle and are not edited manually. + +If the current repository is `config`, review its files normally unless noted +above: they are authoritative there. In other repositories, the following files are managed by +the `config` submodule and must be reviewed in the `config` repository, not +here. In those consumer repositories, skip them without comment: + +- `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` +- `.agents/**` (except `.agents/project.md`) +- `.claude/**`, `.idea/**`, `.junie/**` +- `.github/copilot-instructions.md` +- `buildSrc/**` (except `buildSrc/src/main/kotlin/module.gradle.kts`) +- `gradle/`, `gradlew`, `gradlew.bat` +- `.codecov.yml`, `.gitignore`, `gradle.properties`, `lychee.toml` +- `.github/workflows/` โ€” unless the workflow was introduced by this repo ## Universal rules diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index a6779543..6755ff91 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -33,15 +33,15 @@ jobs: cancel-in-progress: true steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 # Detect the Hugo site root (`docs/` or `site/`) by looking for a Hugo # config file. Hugo config may live directly in the site root or in a # `config/` or `config/_default/` subdirectory (both layouts are valid). - # Outputs `present=true|false`, `site_dir=docs|site`, and `work_dir` - # (the directory where `npm ci` / `hugo` commands should run โ€” either - # `$site_dir/_preview` for repos that use a separate preview sub-tree, - # or `$site_dir` for repos whose Node/Hugo setup lives at the root). + # Outputs `present=true|false` and `work_dir` (the directory where + # `npm ci` / `hugo` commands should run โ€” either `$dir/_preview` for + # repos that use a separate preview sub-tree, or `$dir` for repos whose + # Node/Hugo setup lives at the site root). # When neither directory has a Hugo config, the job short-circuits to a # success so that this shared workflow stays green on repos that do not # host a Hugo site at all. @@ -49,9 +49,10 @@ jobs: id: docs run: | for dir in docs site; do - for cfg in hugo.toml hugo.yaml config/hugo.toml config/_default/hugo.toml; do + for cfg in hugo.toml hugo.yaml \ + config/hugo.toml config/hugo.yaml \ + config/_default/hugo.toml config/_default/hugo.yaml; do if [ -f "$dir/$cfg" ]; then - echo "site_dir=$dir" >> "$GITHUB_OUTPUT" if [ -f "$dir/_preview/package-lock.json" ]; then echo "work_dir=$dir/_preview" >> "$GITHUB_OUTPUT" echo "present=true" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 469ca37f..96196126 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -5,12 +5,17 @@ on: branches: - master - staging + # Rebuilds the site on demand, e.g. to publish a change to `SECURITY.md` in + # `SpineEventEngine/.github`: the Security policy page fetches that file at build time. + workflow_dispatch: env: HUGO_VERSION: 0.161.1 jobs: deploy: + # A manual run deploys only when started on `master`; on other branches, the job is skipped. + if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master' runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 00000000..6f0130e2 --- /dev/null +++ b/.github/workflows/secret-scan.yml @@ -0,0 +1,70 @@ +name: Secret scan + +# Defense-in-depth behind the local `secret-scan` pre-commit hook and the +# `.gitignore` secret patterns: if a credential is committed despite those, this +# fails the pull request before it can merge. Distributed to every Spine repo by +# `./config/pull`. + +on: + pull_request: + push: + branches: + - master + - main + +permissions: + contents: read + +jobs: + gitleaks: + name: gitleaks + runs-on: ubuntu-latest + env: + # Pinned gitleaks version โ€” bump through the usual dependency-update process. + GITLEAKS_VERSION: "8.21.2" + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + # Full history so a pull request's commit range can be scanned. + fetch-depth: 0 + + - name: Install gitleaks + # Run gitleaks as the runner user against the checkout it owns โ€” no + # container, so no "dubious ownership" git error and no GitHub Action + # org-licence requirement. + run: | + curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ + | tar -xzf - gitleaks + ./gitleaks version + + - name: Scan + env: + EVENT: ${{ github.event_name }} + BASE: ${{ github.event.pull_request.base.sha }} + HEAD: ${{ github.event.pull_request.head.sha }} + BEFORE: ${{ github.event.before }} + AFTER: ${{ github.sha }} + run: | + if [ "$EVENT" = pull_request ]; then + # Scan the PR's own commit RANGE: a secret added in one commit and + # deleted in a later commit of the same PR is still caught (a + # working-tree scan would miss it, yet merging keeps the secret-bearing + # commit reachable), while already-rotated secrets in older history + # outside base..head are not re-flagged. + ./gitleaks git --log-opts="$BASE..$HEAD" --redact --verbose --exit-code=1 . + else + # Push to a default branch: scan the pushed commit RANGE (before..after) + # so an add-then-remove batch is caught here too, not only on PRs โ€” the + # leaked commit would otherwise stay reachable on the default branch. A + # branch's first push reports an all-zero `before` (no range); fall back + # to a working-tree scan then. + if [ -n "$BEFORE" ] && [ "$BEFORE" != "0000000000000000000000000000000000000000" ]; then + ./gitleaks git --log-opts="$BEFORE..$AFTER" --redact --verbose --exit-code=1 . + else + # Branch's first push (all-zero `before`): no range to diff against, so + # scan the whole history reachable from the pushed tip as the initial + # import โ€” an add-then-remove within those commits is still caught. + ./gitleaks git --log-opts="$AFTER" --redact --verbose --exit-code=1 . + fi + fi diff --git a/.gitignore b/.gitignore index 19c333c0..96972ef1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,44 +1,15 @@ +# >>> shared config (managed by ./config/pull -- do not edit inside this block) >>> +# Copyright 2026 CodeMatters, Lda. # -# Copyright 2025, TeamDev. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # -# Redistribution and use in source and/or binary forms, with or without -# modification, must retain the above copyright notice and the following -# disclaimer. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -# Hugo cache files -/site/resources -public - -# Cache folder for Hugo Modules -/site/_vendor - -# Node modules -node_modules - -# Used to control concurrency between multiple Hugo instances -/site/.hugo_build.lock - -# Needed for navigation help inside code editors -jsconfig.json +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. # # This file is used for two purposes: @@ -69,14 +40,20 @@ jsconfig.json .idea/modules .idea/shelf +# `.idea/misc.xml` is intentionally NOT re-included below. It is project-local โ€” +# it holds the per-project JDK name and IDEA's own churn (entry-point list +# indices, external-storage toggles) โ€” so `.idea/*.xml` above keeps it ignored. +# `./config/pull` (via `migrate`) untracks any copy an earlier pull committed. + +# `.idea/kotlinc.xml` is likewise NOT re-included. It records the Kotlin compiler +# settings IDEA rewrites on its own (JVM target, bundled plugin version), so it +# churns per machine and per IDE build โ€” `.idea/*.xml` above keeps it ignored. + # Do not ignore the following IDEA settings -!.idea/misc.xml !.idea/codeStyleSettings.xml !.idea/codeStyles/ !.idea/copyright/ -.DS_Store - # Ignore IDEA config files under `tests` /tests/.idea/** @@ -121,14 +98,48 @@ gradle-app.setting # Spine internal directory for storing intermediate artifacts **/.spine/** -# Login details to Maven repository. -# Each workstation should have developer's login defined in this file. +# --------------------------------------------------------------------------- +# Secrets โ€” NEVER commit these. +# +# Encrypted credentials live under `.github/keys/*.gpg` and ARE committed. +# `config/scripts/decrypt.sh` turns each into its PLAINTEXT twin at build / CI / +# publish time (e.g. `spine-dev-framework-ci.json.gpg` -> `spine-dev.json`). The +# decrypted twins below โ€” and any private key or service-account file โ€” must stay +# out of Git. The shared `secret-scan` pre-commit hook is the backstop if one ever +# slips past these patterns. +# --------------------------------------------------------------------------- + +# Maven repository login details; each workstation defines its own. credentials.tar credentials.properties cloudrepo.properties deploy_key_rsa gcs-auth-key.json +# Decrypted Google / GCP service-account keys (plaintext twins of *.gpg). +spine-dev.json +spine-dev-*.json +maven-publisher.json +firebase-sa.json +*-sa.json +*service-account*.json + +# Decrypted credential property files and portal / publisher secrets. +*.secret.properties + +# Private SSH keys (public keys are *.pub and remain committable). +*_rsa +*_dsa +*_ecdsa +*_ed25519 +id_rsa +id_dsa +id_ecdsa +id_ed25519 + +# ...but always keep the committed ENCRYPTED forms. +!*.gpg + # Log files *.log @@ -148,18 +159,24 @@ pubspec.lock # Ignore the `tmp` directory used for building dependant repositories. /tmp +.gradle-test-kit/ + # Python cache __pycache__/ *.pyc # Claude working files /.claude/worktrees/ +# Ephemeral plan-mode scratch (durable task docs live in `.agents/tasks/`). +/.claude/plans/ + +# Personal, per-developer Claude Code settings overrides (never committed; +# the distributed `.claude/settings.json` is the shared, committed layer). +/.claude/settings.local.json # Auto-downloaded Lychee binary used by the `check-links` skill. /.agents/skills/check-links/.cache/ -/.sass-cache/ - # Lychee link-checker cache (created by the `check-links` skill and # the `Check Links` workflow when run locally). .lycheecache @@ -170,3 +187,68 @@ __pycache__/ docs/_preview/node_modules/ docs/_preview/public/ docs/_preview/resources/ +# <<< shared config <<< + +# >>> repo-local entries (preserved across ./config/pull) >>> +# Copyright 2025, TeamDev. All rights reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# Redistribution and use in source and/or binary forms, with or without +# modification, must retain the above copyright notice and the following +# disclaimer. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# Hugo cache files +/site/resources +public +# Cache folder for Hugo Modules +/site/_vendor +# Node modules +node_modules +# Used to control concurrency between multiple Hugo instances +/site/.hugo_build.lock +# Needed for navigation help inside code editors +jsconfig.json +!.idea/codeStyleSettings.xml +!.idea/codeStyles/ +!.idea/copyright/ +.DS_Store +!**/src/**/build/** +!gradle-wrapper.jar +# Login details to Maven repository. +# Each workstation should have developer's login defined in this file. +/.sass-cache/ +# <<< repo-local entries <<< + +# >>> secret ignores re-asserted last (managed by ./config/pull -- do not edit) >>> +credentials.tar +credentials.properties +cloudrepo.properties +deploy_key_rsa +gcs-auth-key.json +spine-dev.json +spine-dev-*.json +maven-publisher.json +firebase-sa.json +*-sa.json +*service-account*.json +*.secret.properties +*_rsa +*_dsa +*_ecdsa +*_ed25519 +id_rsa +id_dsa +id_ecdsa +id_ed25519 +# <<< secret ignores <<< diff --git a/.gitmodules b/.gitmodules index 94e8664d..1978333b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "config"] path = config url = https://github.com/SpineEventEngine/config +[submodule ".agents/shared"] + path = .agents/shared + url = https://github.com/SpineEventEngine/agents.git + branch = master + update = merge + ignore = all diff --git a/.idea/copyright/CodeMatters_Open_Source.xml b/.idea/copyright/CodeMatters_Open_Source.xml new file mode 100644 index 00000000..cf5d7135 --- /dev/null +++ b/.idea/copyright/CodeMatters_Open_Source.xml @@ -0,0 +1,6 @@ + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index 0b8f9a19..4f463a81 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,7 +1,7 @@ - + - + \ No newline at end of file diff --git a/.idea/live-templates/README.md b/.idea/live-templates/README.md index 58394ba0..267008ad 100644 --- a/.idea/live-templates/README.md +++ b/.idea/live-templates/README.md @@ -10,7 +10,7 @@ This directory contains two live template groups: Live templates are not picked up by IDEA automatically. They should be added manually. In order to add these templates, perform the following steps: -1. Copy `*.xml` files from this directory to `templates` directory in the IntelliJ IDEA +1. Copy `*.xml` files from this directory to `templates` directory in the IntelliJ IDEA [settings folder][settings_folder]. 2. Restart IntelliJ IDEA: `File -> Invalidate Caches -> Just restart`. 3. Go to `Preferences -> Editor -> Live Templates`. @@ -22,6 +22,6 @@ In order to add these templates, perform the following steps: 1. Open the corresponding template: `Preferences -> Editor -> Live Templates -> User.todo`. 2. Click on `Edit variables`. -3. Set `USER` variable to your domain email address without `@teamdev.com` ending. For example, +3. Set `USER` variable to your domain email address without `@teamdev.com` ending. For example, for `jack.sparrow@teamdev.com` use the following expression `"jack.sparrow"`. 4. Verify that the template generates expected comments: `// TODO:2022-11-03:jack.sparrow: <...>`. diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index d4de1cb4..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.junie/guidelines.md b/.junie/guidelines.md index 5160f499..7c1f8665 100644 --- a/.junie/guidelines.md +++ b/.junie/guidelines.md @@ -1,6 +1,6 @@ # Guidelines for Junie and AI Agent from JetBrains -Read the `../.agents/_TOC.md` file to understand: +Read the `../.agents/guidelines/_TOC.md` file to understand: - the agent responsibilities, - project overview, - coding guidelines, diff --git a/AGENTS.md b/AGENTS.md index 1268e51a..8c5f6198 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,16 +4,34 @@ If `.agents/project.md` exists in this repository, read it first โ€” it describes the language, architecture, and role of this specific repo within the Spine SDK -organisation. To create one, copy `.agents/project.template.md` (or the -relevant language template) and fill it in. If `project.md` links to a shared -requirements file (e.g. `jvm-project.md`), read that too. +organisation. It is a symlink to `docs/project.md`; to create one, copy +`.agents/guidelines/project.template.md` to `docs/project.md` and fill it in. If it +links to a shared requirements file (e.g. `jvm-project.md`), read that too. -- Start every session by reading `.agents/quick-reference-card.md` (if present). +- Start every session by reading `.agents/guidelines/quick-reference-card.md` (if present). - For specific tasks (code review, PR prep, dependency updates, docs, etc.), prefer the matching skill from `.agents/skills/`. -- Full standards reference: `.agents/_TOC.md` (if present) โ€” consult when a +- Full standards reference: `.agents/guidelines/_TOC.md` (if present) โ€” consult when a skill doesn't cover the needed context. +Shared skills, scripts, and guidelines come from the `.agents/shared` submodule (the +[`agents`][agents-repo] repository) exposed via symlinks. +`./config/pull` initializes and floats them automatically. But a fresh `git worktree` +(and some shallow clones / cloud checkouts) start with NO submodules checked out, so +those symlinks dangle and no skills are found. Bootstrap such a tree with +**`./init-submodules`** โ€” a root script that materializes the missing +*config-managed* submodules at their pinned commits: `config` itself, plus every +submodule that declares a tracked `branch` in `.gitmodules` (`.agents/shared`, and +any shared submodule added later) โ€” the same rule `./config/pull` uses to decide +what it floats. Submodules the consumer owns (a Hugo theme, a vendored library, +doc-example submodules, โ€ฆ) declare no tracked branch and are left untouched, so the +automatic `SessionStart` run never tries to clone โ€” or fail on credentials for โ€” a +submodule this project does not manage. It depends on no pre-existing `config` +submodule, so it works before `./config/pull` (which lives inside the `config` +submodule) can. Claude Code runs it automatically via a `SessionStart` hook; other +agents and humans run it by hand, then `./config/pull` to float the shared submodules +to their branch tips. + ## Commit and history safety **Do not commit, push, tag, rebase, merge, cherry-pick, or otherwise write to git history** @@ -26,7 +44,7 @@ unless one of the following is true *right now*: Authorization does not carry over between turns or sessions. When in doubt: stage changes, show the diff, and stop โ€” let the user commit. -See [`.agents/safety-rules.md`](.agents/safety-rules.md) โ†’ *Commits and history-writing*. +See [`.agents/guidelines/safety-rules.md`](.agents/guidelines/safety-rules.md) โ†’ *Commits and history-writing*. ## Other safety rules @@ -35,7 +53,7 @@ See [`.agents/safety-rules.md`](.agents/safety-rules.md) โ†’ *Commits and histor - No analytics, telemetry, or tracking code. - No reflection or unsafe code without explicit approval. -See [`.agents/safety-rules.md`](.agents/safety-rules.md) for the full list. +See [`.agents/guidelines/safety-rules.md`](.agents/guidelines/safety-rules.md) for the full list. ## Moving files @@ -55,6 +73,17 @@ See `.agents/memory/README.md` for layout and write protocol. Review `.agents/memory/MEMORY.md` at the start of every session. Ruthlessly iterate until mistakes stop repeating. +## Asking questions + +- Ask at most one question per message. If a decision has a small set of + options, include those options as part of that one question. +- Do not bundle unrelated clarification questions. Ask the next question only + after the user answers the previous one. +- Apply this rule both when the agent needs clarification and when the user's + prompt means "ask questions". +- Prefer a reasonable assumption over another question when the answer would not + materially change the next step. + ## Verification & Quality - Never mark a task done without proof (tests, logs, diff vs main). @@ -77,3 +106,30 @@ Ruthlessly iterate until mistakes stop repeating. - Verify changes before marking a task done. - Update memory if lessons emerged. - Delete the task file on merge to master. + +## Code review + +Never review `gradlew` or `gradlew.bat` in any repository, including `config`. +These files are provided by Gradle and are not edited manually. + +When reviewing a pull request or diff in a consumer repository, skip any +file that the `config` module distributes. Those files belong in a review +of the `config` repo, not the consumer repo โ€” reviewing them there adds +noise without value. + +Do **not** apply this skip rule when reviewing the `config` repository +itself. In `config`, these files are source files owned by the current +repo and must be reviewed normally, except `gradlew` and `gradlew.bat`. + +In consumer repositories, skip without comment any path matching: + +- `AGENTS.md`, `CLAUDE.md`, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md` +- `.agents/**` (except `.agents/project.md`) +- `.claude/**`, `.idea/**`, `.junie/**` +- `.github/copilot-instructions.md` +- `buildSrc/**` (except `buildSrc/src/main/kotlin/module.gradle.kts`) +- `gradle/`, `gradlew`, `gradlew.bat`, `init-submodules` +- `.codecov.yml`, `.gitignore`, `gradle.properties`, `lychee.toml` +- `.github/workflows/` โ€” unless the workflow was introduced by this repo + +[agents-repo]: https://github.com/SpineEventEngine/agents diff --git a/build.gradle.kts b/build.gradle.kts index 5f2f24d4..4dc7a07f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ tasks.register("runSite") { diff --git a/config b/config index 56b5c907..3210ca6c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 56b5c9070ad0efcadc3a96256e4c4937b0528e4e +Subproject commit 3210ca6cc05c77ee59961721cf5c1c08327aa86b diff --git a/docs/project.md b/docs/project.md new file mode 100644 index 00000000..8cf191de --- /dev/null +++ b/docs/project.md @@ -0,0 +1,96 @@ +# Project: spine.io + +## Overview + +This repository is the source of the public [spine.io](https://spine.io) website โ€” +the umbrella surface for the Spine SDK organisation. It serves a dual role: +a **documentation portal** (pulling the canonical content from the +[`documentation`][documentation] repository as a Hugo Module) and a +**marketing site** with landing pages, examples, and a `getting-help` page that +sells products through a Paygate-backed checkout. The site is built with +[Hugo Extended][hugo] and hosted on GitHub Pages. + +## Architecture + +**Role in the org:** application (a static website). Not a library, tool, or +Gradle plugin. The Gradle build present in the repo is a **convenience +wrapper only** โ€” no JVM compilation happens here. + +**Composition** + +- `site/` โ€” the Hugo project (config, layouts, assets, content shell). +- `config/` โ€” the only git submodule, pointing at + [`SpineEventEngine/config`][config] (shared Spine build conventions). +- `build.gradle.kts` โ€” exposes shell-backed tasks: + `:runSite`, `:buildSite`, and `:checkLinks`. + +**External pieces (consumed, not vendored)** + +- [`documentation`][documentation] โ€” pulled in as a Hugo Module; provides the + documentation content. Code snippets shown on the rendered site are + embedded *there* (via `embed-code`) before being pulled in as a module. +- [`site-commons`][site-commons] โ€” Hugo theme module shared across Spine + public sites (anchor icons, snackbars, etc.). + +**CI / deployment** (`.github/workflows/`) + +- `gh-pages.yml` โ€” builds the Hugo site and deploys to GitHub Pages on push + to the default branch. This is the deploy path; treat changes that affect + it with care. +- `check-links.yml` โ€” validates rendered-site links (mirrors the local + `checkLinks` task and the `check-links` skill). +- `gradle-wrapper-validation.yml` โ€” guards the Gradle wrapper checksum on + PRs that touch the wrapper. + +## Constraints & guardrails + +- **Hugo Extended version is pinned.** Use `v0.161.1` or higher of the + *Extended* build. A mismatched/non-extended Hugo will break the theme + pipeline and SCSS. +- **The `config/` submodule is pinned to a specific commit.** Do not bump + it outside a dedicated update task โ€” drive-by pointer changes silently + alter shared Spine build conventions. +- **Documentation content lives in the [`documentation`][documentation] + repo, not here.** Edits to documentation pages must be made there (or in + the relevant doc module). This repo only owns the site shell, landing + pages, and Spine-SDK-wide marketing surfaces. + +## Local development + +Two equivalent ways to run the site locally: + +1. Via Gradle (preferred โ€” handles working directory and Hugo flags): + ```shell + ./gradlew :runSite # build + serve on http://localhost:1313 + ./gradlew :buildSite # build only, output under site/public + ./gradlew :checkLinks # validate links against the built site + ``` +2. Directly with Hugo, from inside `site/`: + ```shell + hugo server + ``` + +If Hugo Module fetches misbehave (stale cache, theme not updating), clear +the module cache and retry: + +```shell +hugo mod clean --all +``` + +The `getting-help` page integrates with **Paygate**. In dev it points at the +staging Paygate server; test the checkout flow with +[LHV sandbox cards](https://merchant.lhv.ee/help/en/articles/12807566-test-cards). +Changes touching that page have a real third-party integration surface โ€” +verify the flow end-to-end, not just the rendered markup. + +## Authoring + +- Content authoring conventions: [`AUTHORING.md`](../AUTHORING.md). +- Embedding code samples into pages happens in the + [`documentation`][documentation] repo, not here โ€” see its + [`EMBEDDING.md`](https://github.com/SpineEventEngine/documentation/blob/master/EMBEDDING.md). + +[hugo]: https://gohugo.io/getting-started/quick-start/#step-1-install-hugo +[documentation]: https://github.com/SpineEventEngine/documentation +[site-commons]: https://github.com/SpineEventEngine/site-commons +[config]: https://github.com/SpineEventEngine/config diff --git a/init-submodules b/init-submodules new file mode 100755 index 00000000..2ae143d8 --- /dev/null +++ b/init-submodules @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +################################################################################ +# +# Materialize the *config-managed* submodules a fresh working tree is missing, so +# agent assets resolve. +# +# `git worktree add` โ€” and some shallow CI / cloud checkouts โ€” populate only the +# superproject's own tracked files; registered submodules are left UNinitialized. +# In a Spine repo that means the `config` and `.agents/shared` submodules are +# empty, the `.agents/skills` -> `.agents/shared/skills` symlink dangles, and no +# agent skills, scripts, or guidelines can be found. +# +# This script is the bootstrap that has to run BEFORE `./config/pull`: `pull` +# lives inside the `config` submodule, so on a fresh worktree it does not yet +# exist. `init-submodules`, by contrast, is a plain tracked file at the repo root +# (distributed by `config`), so `git worktree add` always checks it out โ€” it can +# therefore bring `config` itself into existence. +# +# It initializes ONLY submodules that are BOTH: +# +# * not yet checked out โ€” those `git submodule status` marks with a leading `-`, +# at the commit the branch pins; and +# +# * config-managed โ€” `config` itself (the bootstrap target `pull` lives inside, +# which carries no tracked `branch` in a consumer's `.gitmodules`), plus every +# submodule that declares a tracked `branch` in `.gitmodules`. This is exactly +# the rule `./config/pull` uses to decide what it floats, so the two scripts +# can never disagree about what is shared. +# +# Consumer-owned submodules (a Hugo theme, a vendored library, documentation +# examples, ...) declare no tracked branch and are deliberately left untouched. +# Because a `SessionStart` hook runs this script automatically on every session, +# initializing them would mean trying to clone โ€” or failing on credentials for โ€” +# a submodule this project does not manage, on every single start. They are +# skipped (noted on stderr). +# +# Submodules already present are left exactly as they are, so a tree that floated +# `config` / `.agents/shared` to a branch tip via `./config/pull` is never +# silently rewound to the pin. That makes the script idempotent and safe to run on +# every session start. +# +# It does NOT float submodules to their branch tips โ€” run `./config/pull` +# afterwards for that. Unlike `pull`, it depends on no pre-existing `config` +# submodule, so it can bootstrap a bare worktree where `./config/pull` does not +# yet exist. +# +################################################################################ + +set -u + +root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 +cd "$root" || exit 0 + +# Nothing to do in a repo without submodules. +[ -f .gitmodules ] || exit 0 + +# The set of config-managed submodule paths: `config` itself (handled specially โ€” +# it carries no tracked branch, exactly as in `./config/pull`), plus every +# submodule declaring a tracked `branch` in `.gitmodules`. Mirrors `pull`'s rule. +config_managed_paths() { + printf '%s\n' 'config' + git config -f .gitmodules --get-regexp '^submodule\..*\.branch$' 2>/dev/null \ + | while read -r key _branch; do + name=${key#submodule.}; name=${name%.branch} + git config -f .gitmodules --get "submodule.$name.path" 2>/dev/null + done +} + +managed=$(config_managed_paths | sort -u) + +# `git submodule status` prefixes each uninitialized submodule with `-`; an +# initialized one starts with a space (at the pinned commit) or `+` (ahead of it). +# Act only on the `-` lines, taking the path from the second field, and only when +# that path is config-managed. +git submodule status 2>/dev/null | awk '$1 ~ /^-/ { print $2 }' | while read -r path; do + [ -n "$path" ] || continue + if printf '%s\n' "$managed" | grep -qxF -- "$path"; then + echo "init-submodules: initializing '$path'" + git submodule update --init --recursive -- "$path" \ + || echo "init-submodules: WARNING โ€” could not initialize '$path' (offline?)." >&2 + else + echo "init-submodules: skipping consumer-owned '$path' (not config-managed)." >&2 + fi +done + +# Route Git hooks to the shared hooks directory so the secret-scan `pre-commit` +# hook is active even in a brand-new worktree, before `./config/pull` runs. The +# path floats with the `.agents/shared` submodule; until that submodule is +# initialized the hook simply does not fire (Git skips a missing hook). Set only +# when unset or already ours โ€” never override a repo's own `core.hooksPath`. +desired_hooks=".agents/scripts/git-hooks" +current_hooks=$(git config --local --get core.hooksPath 2>/dev/null || true) +if [ -z "$current_hooks" ] || [ "$current_hooks" = "$desired_hooks" ]; then + git config --local core.hooksPath "$desired_hooks" \ + && echo "init-submodules: Git hooks routed to '$desired_hooks' (secret-scan pre-commit active)." +fi + +exit 0 diff --git a/lychee.toml b/lychee.toml index e544f9dc..6b7d3097 100644 --- a/lychee.toml +++ b/lychee.toml @@ -1,37 +1,66 @@ +# Lychee configuration for the `check-links` skill and the +# `Check Links` GitHub workflow. +# +# Mirrors the configuration used by the sibling +# `SpineEventEngine/SpineEventEngine.github.io` repository, with the same +# exclude list of flaky external endpoints. + # Exclude URLs and mail addresses from checking (supports regex). +# +# The entries are interpreted as Rust regexes, NOT shell globs: +# - `.` is escaped (`\.`) because an unescaped dot matches any character +# and would silently over-match (e.g. `fonts.googleapis.com` would +# also match `fontsXgoogleapisYcomZ`, masking real broken links). +# - `/.*` replaces the shell-style `/*` so a trailing path of any length +# matches (zero-or-more of any character, not zero-or-more slashes). +# TOML literal strings (single-quoted) are used so backslashes stay +# literal โ€” basic strings (double quotes) would treat `\.` as an unknown +# escape and either error out or strip the backslash. exclude = [ # Links that return errors during checks, but work for the user. - "fonts.googleapis.com/*", - "fonts.gstatic.com/*", - "chromium.googlesource.com/*", - "chromereleases.googleblog.com/*", - "clients4.google.com/", - "ssl.gstatic.com/", - "googletagmanager.com/*", - "x.com/*", - - "stackoverflow.com/questions/*", - "openjdk.org/*", - "npmjs.com/*", - "raw.githubusercontent.com/SpineEventEngine/*", - "medium.com/*", - "levelup.gitconnected.com/*" + 'fonts\.googleapis\.com/.*', + 'fonts\.gstatic\.com/.*', + 'chromium\.googlesource\.com/.*', + 'chromereleases\.googleblog\.com/.*', + 'clients4\.google\.com/.*', + 'ssl\.gstatic\.com/.*', + 'googletagmanager\.com/.*', + 'x\.com/.*', + + 'stackoverflow\.com/questions/.*', + 'openjdk\.org/.*', + 'npmjs\.com/.*', + 'medium\.com/.*', + 'levelup\.gitconnected\.com/.*', ] +# Deliberately NOT excluded: `raw.githubusercontent.com/SpineEventEngine/*`. +# Catching broken refs into our own raw GitHub content is part of the reason +# this skill exists. If rate-limit flake appears, prefer narrowing the +# pattern to the specific failing path rather than re-adding the broad rule. +# Existing `max_retries`/`retry_wait_time` below should absorb transient 429s. # Exclude these filesystem paths from getting checked. exclude_path = [] -# Check links inside `` and `
` blocks as well
-# as Markdown code blocks.
+# Do NOT check links inside `` and `
` blocks or
+# Markdown code blocks (verbatim/code sections are excluded).
 include_verbatim = false
 
 # Verbose program output
 verbose = "error"
 
-# Don't show interactive progress bar while checking links.
-no_progress = false
+# Don't show the interactive progress bar while checking links. Both
+# consumers (CI and the `check-links` skill) run non-interactively, so
+# the progress bar adds noise to logs without value.
+no_progress = true
 
 # Comma-separated list of accepted status codes for valid links.
+# `429` (Too Many Requests) is accepted as a tradeoff against CI flake: some
+# providers rate-limit unauthenticated link probes from CI runners even when
+# the URL is healthy. Combined with `max_retries`/`retry_wait_time` below,
+# this avoids spurious failures on healthy-but-rate-limited URLs. The
+# downside: a URL that is genuinely broken AND returns `429` (rare) will
+# pass. Revisit this if false negatives accumulate.
 accept = ["200..=204", "429"]
 
 # Link caching to avoid checking the same links on multiple runs.
diff --git a/settings.gradle.kts b/settings.gradle.kts
index b10de4e9..fc931cce 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 rootProject.name = "spine.io"
diff --git a/site/assets/js/main.js b/site/assets/js/main.js
index d9199362..47b67ea6 100644
--- a/site/assets/js/main.js
+++ b/site/assets/js/main.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 import './modules/navbar/navbar';
diff --git a/site/assets/js/modules/forms/phone-number.js b/site/assets/js/modules/forms/phone-number.js
index 66bc4de0..ccaa75ab 100644
--- a/site/assets/js/modules/forms/phone-number.js
+++ b/site/assets/js/modules/forms/phone-number.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/modules/go-top-button.js b/site/assets/js/modules/go-top-button.js
index 0b34be66..7fc79d94 100644
--- a/site/assets/js/modules/go-top-button.js
+++ b/site/assets/js/modules/go-top-button.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/modules/navbar/mobile-navbar.js b/site/assets/js/modules/navbar/mobile-navbar.js
index 5f8001b5..7eebcf8b 100644
--- a/site/assets/js/modules/navbar/mobile-navbar.js
+++ b/site/assets/js/modules/navbar/mobile-navbar.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/modules/navbar/navbar.js b/site/assets/js/modules/navbar/navbar.js
index 50b92b23..4f2cafc5 100644
--- a/site/assets/js/modules/navbar/navbar.js
+++ b/site/assets/js/modules/navbar/navbar.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 import {toggleMobileNavbar} from './mobile-navbar';
diff --git a/site/assets/js/modules/navbar/sticky-navbar.js b/site/assets/js/modules/navbar/sticky-navbar.js
index 35552e56..0c88dc24 100644
--- a/site/assets/js/modules/navbar/sticky-navbar.js
+++ b/site/assets/js/modules/navbar/sticky-navbar.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/modules/paygate/purchases.js b/site/assets/js/modules/paygate/purchases.js
index a1b73be8..9e0237a1 100644
--- a/site/assets/js/modules/paygate/purchases.js
+++ b/site/assets/js/modules/paygate/purchases.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/charge-controller.js b/site/assets/js/pages/checkout/charge-controller.js
index 47944ca7..5a3002a8 100644
--- a/site/assets/js/pages/checkout/charge-controller.js
+++ b/site/assets/js/pages/checkout/charge-controller.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/charge-request.js b/site/assets/js/pages/checkout/charge-request.js
index e813ac1a..2187e620 100644
--- a/site/assets/js/pages/checkout/charge-request.js
+++ b/site/assets/js/pages/checkout/charge-request.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/completed-page-url.js b/site/assets/js/pages/checkout/completed-page-url.js
index 02544d7b..11e69d9b 100644
--- a/site/assets/js/pages/checkout/completed-page-url.js
+++ b/site/assets/js/pages/checkout/completed-page-url.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/completed.js b/site/assets/js/pages/checkout/completed.js
index fc473164..bbae1cc7 100644
--- a/site/assets/js/pages/checkout/completed.js
+++ b/site/assets/js/pages/checkout/completed.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/countries.js b/site/assets/js/pages/checkout/countries.js
index a3716411..edff45f7 100644
--- a/site/assets/js/pages/checkout/countries.js
+++ b/site/assets/js/pages/checkout/countries.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/delayed-request-controller.js b/site/assets/js/pages/checkout/delayed-request-controller.js
index 7ce7ad8b..5c1d34da 100644
--- a/site/assets/js/pages/checkout/delayed-request-controller.js
+++ b/site/assets/js/pages/checkout/delayed-request-controller.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/dom.js b/site/assets/js/pages/checkout/dom.js
index d14a34b8..e369e670 100644
--- a/site/assets/js/pages/checkout/dom.js
+++ b/site/assets/js/pages/checkout/dom.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/form-controller.js b/site/assets/js/pages/checkout/form-controller.js
index 84ce4461..8b80d132 100644
--- a/site/assets/js/pages/checkout/form-controller.js
+++ b/site/assets/js/pages/checkout/form-controller.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/index.js b/site/assets/js/pages/checkout/index.js
index 886646d4..1b59c40e 100644
--- a/site/assets/js/pages/checkout/index.js
+++ b/site/assets/js/pages/checkout/index.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/navigation.js b/site/assets/js/pages/checkout/navigation.js
index 9d29ed19..ce8b520a 100644
--- a/site/assets/js/pages/checkout/navigation.js
+++ b/site/assets/js/pages/checkout/navigation.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/order-id.js b/site/assets/js/pages/checkout/order-id.js
index 5a93bd20..0a3b8800 100644
--- a/site/assets/js/pages/checkout/order-id.js
+++ b/site/assets/js/pages/checkout/order-id.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/vat-countries.js b/site/assets/js/pages/checkout/vat-countries.js
index 3caf64fe..29aa5cd7 100644
--- a/site/assets/js/pages/checkout/vat-countries.js
+++ b/site/assets/js/pages/checkout/vat-countries.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/checkout/view-controller.js b/site/assets/js/pages/checkout/view-controller.js
index e97aa751..8614d886 100644
--- a/site/assets/js/pages/checkout/view-controller.js
+++ b/site/assets/js/pages/checkout/view-controller.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/faq.js b/site/assets/js/pages/faq.js
index bb13f613..aad9aead 100644
--- a/site/assets/js/pages/faq.js
+++ b/site/assets/js/pages/faq.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2025, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/oss-licenses.js b/site/assets/js/pages/oss-licenses.js
index 45e74576..1300f75f 100644
--- a/site/assets/js/pages/oss-licenses.js
+++ b/site/assets/js/pages/oss-licenses.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/js/pages/pricing.js b/site/assets/js/pages/pricing.js
index ee2d7aa4..9200c4a9 100644
--- a/site/assets/js/pages/pricing.js
+++ b/site/assets/js/pages/pricing.js
@@ -1,27 +1,15 @@
 /*
- * Copyright 2026, TeamDev. All rights reserved.
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 'use strict';
diff --git a/site/assets/scss/base/_colors.scss b/site/assets/scss/base/_colors.scss
index eecd9989..5f222922 100755
--- a/site/assets/scss/base/_colors.scss
+++ b/site/assets/scss/base/_colors.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 /* Extends colors from `site-commons`. */
diff --git a/site/assets/scss/base/_common.scss b/site/assets/scss/base/_common.scss
index 74380b83..ee4a0406 100755
--- a/site/assets/scss/base/_common.scss
+++ b/site/assets/scss/base/_common.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .intro-section {
diff --git a/site/assets/scss/main.scss b/site/assets/scss/main.scss
index e1c4c4f7..ac9a9dda 100644
--- a/site/assets/scss/main.scss
+++ b/site/assets/scss/main.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 /* Styles from `SpineEventEngine/site-commons`. */
diff --git a/site/assets/scss/modules/_buttons.scss b/site/assets/scss/modules/_buttons.scss
index cafcf833..19e55837 100644
--- a/site/assets/scss/modules/_buttons.scss
+++ b/site/assets/scss/modules/_buttons.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $btn-font-size: 14px;
diff --git a/site/assets/scss/modules/_call-to-action.scss b/site/assets/scss/modules/_call-to-action.scss
index 229e5a95..f2d41b52 100644
--- a/site/assets/scss/modules/_call-to-action.scss
+++ b/site/assets/scss/modules/_call-to-action.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .call-to-action {
diff --git a/site/assets/scss/modules/_checkbox.scss b/site/assets/scss/modules/_checkbox.scss
index 67805f41..599498e1 100644
--- a/site/assets/scss/modules/_checkbox.scss
+++ b/site/assets/scss/modules/_checkbox.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $checkbox-size: 18px;
diff --git a/site/assets/scss/modules/_feature-cards.scss b/site/assets/scss/modules/_feature-cards.scss
index 9a4788e8..97a7a5b7 100644
--- a/site/assets/scss/modules/_feature-cards.scss
+++ b/site/assets/scss/modules/_feature-cards.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $feature-card-shadow: 0 1px 3px 0 rgba($footer-color, .08);
diff --git a/site/assets/scss/modules/_footer.scss b/site/assets/scss/modules/_footer.scss
index 81ff1d47..49628f43 100755
--- a/site/assets/scss/modules/_footer.scss
+++ b/site/assets/scss/modules/_footer.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $footer-header-color: #54595c;
diff --git a/site/assets/scss/modules/_forms.scss b/site/assets/scss/modules/_forms.scss
index 6d78882e..c83493f4 100644
--- a/site/assets/scss/modules/_forms.scss
+++ b/site/assets/scss/modules/_forms.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $form-error-color: $red;
diff --git a/site/assets/scss/modules/_go-top-button.scss b/site/assets/scss/modules/_go-top-button.scss
index 2da9aba3..2d995a2c 100644
--- a/site/assets/scss/modules/_go-top-button.scss
+++ b/site/assets/scss/modules/_go-top-button.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 #go-top-button {
diff --git a/site/assets/scss/modules/_hero.scss b/site/assets/scss/modules/_hero.scss
index 60526c1c..949bebdc 100755
--- a/site/assets/scss/modules/_hero.scss
+++ b/site/assets/scss/modules/_hero.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .nav-hero-container {
diff --git a/site/assets/scss/modules/_loader.scss b/site/assets/scss/modules/_loader.scss
index b5751c64..01320b8a 100644
--- a/site/assets/scss/modules/_loader.scss
+++ b/site/assets/scss/modules/_loader.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .loader-container {
diff --git a/site/assets/scss/modules/_message-modal.scss b/site/assets/scss/modules/_message-modal.scss
index f0818b17..42983a3e 100644
--- a/site/assets/scss/modules/_message-modal.scss
+++ b/site/assets/scss/modules/_message-modal.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .message-modal {
diff --git a/site/assets/scss/modules/_nav-search.scss b/site/assets/scss/modules/_nav-search.scss
index 395e9ddf..44f5b407 100644
--- a/site/assets/scss/modules/_nav-search.scss
+++ b/site/assets/scss/modules/_nav-search.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $docsearch-icon-color: rgba(white, .54);
diff --git a/site/assets/scss/modules/_navbar.scss b/site/assets/scss/modules/_navbar.scss
index 1e04fa85..515a4b7a 100755
--- a/site/assets/scss/modules/_navbar.scss
+++ b/site/assets/scss/modules/_navbar.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .navigation-opened {
diff --git a/site/assets/scss/modules/_numbered-headings.scss b/site/assets/scss/modules/_numbered-headings.scss
index 50af4555..31916850 100644
--- a/site/assets/scss/modules/_numbered-headings.scss
+++ b/site/assets/scss/modules/_numbered-headings.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 /*
@@ -133,10 +121,14 @@
   #TableOfContents {
     ul {
       li {
+        position: relative;
         display: flex;
 
+        // The number sits in the left padding of the link, so it keeps its place
+        // when the title wraps.
         $counter-width: 30px;
         &:before {
+          position: absolute;
           display: block;
           counter-increment: toc;
           content: counter(toc) ".";
diff --git a/site/assets/scss/modules/_redirect-screen.scss b/site/assets/scss/modules/_redirect-screen.scss
index 513786f8..29550b8f 100644
--- a/site/assets/scss/modules/_redirect-screen.scss
+++ b/site/assets/scss/modules/_redirect-screen.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .redirect-screen {
diff --git a/site/assets/scss/modules/_result-panel.scss b/site/assets/scss/modules/_result-panel.scss
index cad773db..9f6facfe 100644
--- a/site/assets/scss/modules/_result-panel.scss
+++ b/site/assets/scss/modules/_result-panel.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .error-pages {
diff --git a/site/assets/scss/modules/_subscribe-container.scss b/site/assets/scss/modules/_subscribe-container.scss
index ce178a92..09f12e08 100644
--- a/site/assets/scss/modules/_subscribe-container.scss
+++ b/site/assets/scss/modules/_subscribe-container.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .subscribe-container {
diff --git a/site/assets/scss/pages/_about.scss b/site/assets/scss/pages/_about.scss
index 25c110a9..84d08d80 100755
--- a/site/assets/scss/pages/_about.scss
+++ b/site/assets/scss/pages/_about.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .about {
diff --git a/site/assets/scss/pages/_checkout.scss b/site/assets/scss/pages/_checkout.scss
index e22a7b24..a97fd887 100644
--- a/site/assets/scss/pages/_checkout.scss
+++ b/site/assets/scss/pages/_checkout.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 @import 'libs/country-select/select2';
diff --git a/site/assets/scss/pages/_faq.scss b/site/assets/scss/pages/_faq.scss
index b32bcc9a..f34688d6 100644
--- a/site/assets/scss/pages/_faq.scss
+++ b/site/assets/scss/pages/_faq.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $icon-right-offset: 48px;
diff --git a/site/assets/scss/pages/_getting-help.scss b/site/assets/scss/pages/_getting-help.scss
index 01fa9200..9a23c0e5 100644
--- a/site/assets/scss/pages/_getting-help.scss
+++ b/site/assets/scss/pages/_getting-help.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
  * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 :root {
diff --git a/site/assets/scss/pages/_landing.scss b/site/assets/scss/pages/_landing.scss
index 13e228d6..f6b3ce1c 100644
--- a/site/assets/scss/pages/_landing.scss
+++ b/site/assets/scss/pages/_landing.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .landing-content-container {
diff --git a/site/assets/scss/pages/_licenses.scss b/site/assets/scss/pages/_licenses.scss
index 72b44b91..3d7ba138 100644
--- a/site/assets/scss/pages/_licenses.scss
+++ b/site/assets/scss/pages/_licenses.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 $panel-padding: 16px 56px;
diff --git a/site/assets/scss/pages/_privacy.scss b/site/assets/scss/pages/_privacy.scss
index 5810c566..4c4cd9cc 100644
--- a/site/assets/scss/pages/_privacy.scss
+++ b/site/assets/scss/pages/_privacy.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2026, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .privacy {
diff --git a/site/assets/scss/pages/blog/_blog-container.scss b/site/assets/scss/pages/blog/_blog-container.scss
index c34b6c66..a30bb5de 100644
--- a/site/assets/scss/pages/blog/_blog-container.scss
+++ b/site/assets/scss/pages/blog/_blog-container.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .blog-container {
diff --git a/site/assets/scss/pages/blog/_blog.scss b/site/assets/scss/pages/blog/_blog.scss
index 065bfeb2..b4f3bc1d 100755
--- a/site/assets/scss/pages/blog/_blog.scss
+++ b/site/assets/scss/pages/blog/_blog.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .blog {
diff --git a/site/assets/scss/pages/blog/_index.scss b/site/assets/scss/pages/blog/_index.scss
index 1d6f8dc2..1f988b54 100644
--- a/site/assets/scss/pages/blog/_index.scss
+++ b/site/assets/scss/pages/blog/_index.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 @import "blog-container";
diff --git a/site/assets/scss/pages/blog/_post-info.scss b/site/assets/scss/pages/blog/_post-info.scss
index a780ed1b..097e7637 100644
--- a/site/assets/scss/pages/blog/_post-info.scss
+++ b/site/assets/scss/pages/blog/_post-info.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .post-info {
diff --git a/site/assets/scss/pages/blog/_post-navigation.scss b/site/assets/scss/pages/blog/_post-navigation.scss
index 4674cf55..93b514e4 100644
--- a/site/assets/scss/pages/blog/_post-navigation.scss
+++ b/site/assets/scss/pages/blog/_post-navigation.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .post-navigation {
diff --git a/site/assets/scss/pages/blog/_post.scss b/site/assets/scss/pages/blog/_post.scss
index ff77a450..888b7407 100644
--- a/site/assets/scss/pages/blog/_post.scss
+++ b/site/assets/scss/pages/blog/_post.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .post {
diff --git a/site/assets/scss/pages/release-notes/_release-notes.scss b/site/assets/scss/pages/release-notes/_release-notes.scss
index eed069eb..6fc11785 100644
--- a/site/assets/scss/pages/release-notes/_release-notes.scss
+++ b/site/assets/scss/pages/release-notes/_release-notes.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 .release-notes {
diff --git a/site/assets/scss/pages/release-notes/_sidenav.scss b/site/assets/scss/pages/release-notes/_sidenav.scss
index 13e7b803..fbd6d463 100644
--- a/site/assets/scss/pages/release-notes/_sidenav.scss
+++ b/site/assets/scss/pages/release-notes/_sidenav.scss
@@ -1,27 +1,15 @@
-/*!
- * Copyright 2025, TeamDev. All rights reserved.
+/*
+ * Copyright 2026 CodeMatters, Lda.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * https://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and/or binary forms, with or without
- * modification, must retain the above copyright notice and the following
- * disclaimer.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
  */
 
 // Extends styles from `scss/docs/modules/_sidenav.scss`.
diff --git a/site/config/_default/hugo.toml b/site/config/_default/hugo.toml
index c15de947..0bbc120e 100644
--- a/site/config/_default/hugo.toml
+++ b/site/config/_default/hugo.toml
@@ -44,6 +44,14 @@ disableKinds = ['taxonomy', 'term']
   [security.node.permissions]
     disable = true
 
+# Remote files that the build fetches, such as `SECURITY.md` for the Security policy page,
+# stay cached for an hour. Hugo never expires them by default, so a cache that CI restores
+# between runs would keep serving the first copy. Deploys don't reuse them at all:
+# see `config/production/hugo.toml`.
+[caches]
+  [caches.getresource]
+    maxAge = '1h'
+
 # Enables HTML rendering in markdown files.
 [markup]
   [markup.goldmark]
diff --git a/site/config/production/hugo.toml b/site/config/production/hugo.toml
new file mode 100644
index 00000000..5cf31339
--- /dev/null
+++ b/site/config/production/hugo.toml
@@ -0,0 +1,5 @@
+# A deploy publishes the current `SECURITY.md` for the Security policy page, so it never reuses
+# a fetched file, whatever the state of the Hugo cache. Other builds keep them for an hour.
+[caches]
+  [caches.getresource]
+    maxAge = 0
diff --git a/site/content/privacy/cvd-policy.md b/site/content/privacy/cvd-policy.md
deleted file mode 100644
index 4a7f6066..00000000
--- a/site/content/privacy/cvd-policy.md
+++ /dev/null
@@ -1,104 +0,0 @@
----
-title: Coordinated Vulnerability Disclosure Policy
-lastmod: 2026-09-11
-url: /cvd-policy
-header_type: fixed-header
-body_class: privacy
-has_numbered_headings: true
----
-
-# Coordinated Vulnerability Disclosure Policy
-
-{{< date label="Last updated:" >}}
-
-## Why we have this Policy
-
-Security is an integral part of how we develop and maintain our product.
-
-We welcome responsible disclosure of security risks and product vulnerabilities by our customers,
-security researchers, and anyone who may discover a potential vulnerability.
-
-This Policy explains how to report a security vulnerability affecting Spine Event Engine
-(โ€œSpineโ€), how we handle the information you provide, and what you can expect from us in
-response.
-
-## What this Policy covers
-
-This Policy covers Spine, along with the third-party components we use inside it.
-
-## How to report
-
-Our single point of contact for vulnerability reports is {{< cloakemail "cra-request@codematters.ltd" >}}.
-
-When submitting a report, please indicate the affected version and include the following, where
-available:
-
-- the name of the security risk and any relevant CWE and CVE identifiers;
-- a description of the issue and clear steps to reproduce it;
-- an illustrative example; and
-- your assessment of the impact.
-
-We appreciate reports submitted in English.
-
-Please report the vulnerability to us before making any information about it public.
-
-## What happens next
-
-We will confirm receipt of your report within 3 business days.
-
-Within the next 10 business days, we will inform you of the results of our initial assessment.
-
-After that, we will provide the issue status updates at least once every 30 business days until
-the matter is resolved or otherwise closed.
-
-## Publication
-
-Once the fix is available, we publish an advisory on our website and reference it in the release
-notes.
-
-We ask you to coordinate the timing of any public disclosure with us. As a general rule, this
-means waiting until we have released a fix or until 90 days have passed since your report,
-whichever is earlier.
-
-## Rules we ask you to follow
-
-When conducting security research and reporting a vulnerability under this Policy, we ask that you:
-
-- act in good faith, and only to identify and report a vulnerability;
-- use your own systems, licences and test data;
-- stop testing once you have confirmed the vulnerability, and collect only the information
-  reasonably necessary to demonstrate it;
-- avoid any activity that could harm our systems, our customers, or other people's data;
-- keep information about the vulnerability confidential until disclosure has been coordinated
-  with us.
-
-## What you can expect from us
-
-If you follow this Policy, we will keep your report confidential and will not take legal action
-against you or report your research to law enforcement.
-
-## Third-party components
-
-Spine incorporates third-party components and other dependencies. Where a vulnerability in such a
-component affects Spine, we will treat the vulnerability as an issue affecting our product and
-address it accordingly.
-
-When such an issue is reported, we will assess its impact on Spine, coordinate with the relevant
-third-party maintainer, publish a security advisory where users are affected, and make available
-any fixes or updates provided by the third-party provider.
-
-Where no fix is available, we will identify appropriate measures to eliminate or mitigate the
-risk and inform affected users of any actions they should take in the meantime.
-
-If we develop a fix for the affected third-party component ourselves, we will, where appropriate,
-share or coordinate that fix with the component's maintainer.
-
-## Reporting to authorities
-
-Where required by applicable law, we will notify the competent authorities, including the
-Portuguese CSIRT (CNCS/CERT.PT) and ENISA, of actively exploited vulnerabilities and severe security
-incidents affecting Spine within the time limits prescribed by the Cyber Resilience Act. We will
-also inform affected users where required.
-
-Such notifications describe the vulnerability, not the person who reported it. We do not share
-your identity with authorities unless the law obliges us to.
diff --git a/site/go.mod b/site/go.mod
index 7db4e2a0..d1f7a4e2 100644
--- a/site/go.mod
+++ b/site/go.mod
@@ -3,9 +3,9 @@ module github.com/SpineEventEngine/SpineEventEngine.github.io
 go 1.22.0
 
 require (
-	github.com/SpineEventEngine/documentation/docs v0.0.0-20260522190949-135339f8eef6 // indirect
+	github.com/SpineEventEngine/documentation/docs v0.0.0-20260930201256-1d7c3e1021be // indirect
 	github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f // indirect
-	github.com/SpineEventEngine/validation/docs v0.0.0-20260522175555-cf09b4c706ea // indirect
+	github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65 // indirect
 	github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 // indirect
 	github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000 // indirect
 	github.com/twbs/bootstrap v5.3.8+incompatible // indirect
diff --git a/site/go.sum b/site/go.sum
index 3306cea2..20c5cbab 100644
--- a/site/go.sum
+++ b/site/go.sum
@@ -2,12 +2,16 @@ github.com/SpineEventEngine/documentation/docs v0.0.0-20260522173421-4174bf3cf80
 github.com/SpineEventEngine/documentation/docs v0.0.0-20260522173421-4174bf3cf80a/go.mod h1:0lSB4kPZY9Qk429mepYiraNIEucYDIwiNVhJ3Wn3gKo=
 github.com/SpineEventEngine/documentation/docs v0.0.0-20260522190949-135339f8eef6 h1:9oJp64ALkcO3RVMSe5Ft0q+WzseZQTFeVQEtyMfpfMM=
 github.com/SpineEventEngine/documentation/docs v0.0.0-20260522190949-135339f8eef6/go.mod h1:0lSB4kPZY9Qk429mepYiraNIEucYDIwiNVhJ3Wn3gKo=
+github.com/SpineEventEngine/documentation/docs v0.0.0-20260930201256-1d7c3e1021be h1:BAzioFLPEdwwLNZmkcPR1ab3kzIF6xUI3LJmD//Hcno=
+github.com/SpineEventEngine/documentation/docs v0.0.0-20260930201256-1d7c3e1021be/go.mod h1:fEPcJFgW19NDIdb/zGEXVPcUNVMGEBkqfgAZI0VMbVk=
 github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f h1:yF43XTDNj+w1S0fLA/XhrWMrhAoDpqPB7gmc4mUF/9Q=
 github.com/SpineEventEngine/site-commons v0.0.0-20260522171914-2a606d89558f/go.mod h1:tkAl4StIREKmz9r5PiJtuDhvwMMkFXKWcaTyxhIikho=
 github.com/SpineEventEngine/validation/docs v0.0.0-20260205202311-181ba8844107 h1:2DHRIwwLudP6l3Kh7Nd4KR4uXcsDtIAXE9adW68ivME=
 github.com/SpineEventEngine/validation/docs v0.0.0-20260205202311-181ba8844107/go.mod h1:STHyjXejVvPmfrxujfDvhofmjg55mMk+fwI3TVL0b4Y=
 github.com/SpineEventEngine/validation/docs v0.0.0-20260522175555-cf09b4c706ea h1:YEMSlr5KJXkZdK7epMSnN+6HSr2K41n8O3c8OhtF8pM=
 github.com/SpineEventEngine/validation/docs v0.0.0-20260522175555-cf09b4c706ea/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
+github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65 h1:gLlFsu7ZznIurecioKUHTP+sKD4EapeKFhtEqL/JvdU=
+github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
 github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 h1:j5myhhzYwIHTr5ctK96Elfgp5uRROvrlTzYwwe1nF8o=
 github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800/go.mod h1:P5GGyhdxi00C5zW7vkRo/IS532gZY/YS2TS395Xaxho=
 github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000 h1:GZxx4Hc+yb0/t3/rau1j8XlAxLE4CyXns2fqQbyqWfs=
diff --git a/site/layouts/_partials/about/clients.html b/site/layouts/_partials/about/clients.html
index b142717c..f5afd309 100644
--- a/site/layouts/_partials/about/clients.html
+++ b/site/layouts/_partials/about/clients.html
@@ -1,27 +1,15 @@
 
 
 
diff --git a/site/layouts/_partials/about/intro.html b/site/layouts/_partials/about/intro.html index 21a73f94..c2d90cf1 100644 --- a/site/layouts/_partials/about/intro.html +++ b/site/layouts/_partials/about/intro.html @@ -1,27 +1,15 @@
diff --git a/site/layouts/_partials/about/libraries.html b/site/layouts/_partials/about/libraries.html index 5a837772..70314453 100644 --- a/site/layouts/_partials/about/libraries.html +++ b/site/layouts/_partials/about/libraries.html @@ -1,27 +1,15 @@ {{ with .Page.GetPage "about/libraries.md" }} diff --git a/site/layouts/_partials/about/principles.html b/site/layouts/_partials/about/principles.html index fed138bd..ad6d31e7 100644 --- a/site/layouts/_partials/about/principles.html +++ b/site/layouts/_partials/about/principles.html @@ -1,27 +1,15 @@ {{ with .Page.GetPage "about/principles.md" }} diff --git a/site/layouts/_partials/about/tools.html b/site/layouts/_partials/about/tools.html index 554e1d2b..51d0f0f0 100644 --- a/site/layouts/_partials/about/tools.html +++ b/site/layouts/_partials/about/tools.html @@ -1,27 +1,15 @@
diff --git a/site/layouts/_partials/blog/functions/filter-posts.html b/site/layouts/_partials/blog/functions/filter-posts.html index c6ad59d6..7585dcc3 100644 --- a/site/layouts/_partials/blog/functions/filter-posts.html +++ b/site/layouts/_partials/blog/functions/filter-posts.html @@ -1,27 +1,15 @@ {{ $posts := where site.RegularPages "Type" "blog/post" }} diff --git a/site/layouts/_partials/blog/post-board.html b/site/layouts/_partials/blog/post-board.html index aebc977c..2bd1388a 100644 --- a/site/layouts/_partials/blog/post-board.html +++ b/site/layouts/_partials/blog/post-board.html @@ -1,27 +1,15 @@ {{ $posts := partial "blog/functions/filter-posts.html" . }} diff --git a/site/layouts/_partials/blog/post-info.html b/site/layouts/_partials/blog/post-info.html index b173f2b4..3e46ad50 100644 --- a/site/layouts/_partials/blog/post-info.html +++ b/site/layouts/_partials/blog/post-info.html @@ -1,27 +1,15 @@ {{ $context := .context }} diff --git a/site/layouts/_partials/blog/post-nav.html b/site/layouts/_partials/blog/post-nav.html index b4bf9260..48046316 100644 --- a/site/layouts/_partials/blog/post-nav.html +++ b/site/layouts/_partials/blog/post-nav.html @@ -1,27 +1,15 @@ {{ $posts := partial "blog/functions/filter-posts.html" . }} diff --git a/site/layouts/_partials/blog/post-tile.html b/site/layouts/_partials/blog/post-tile.html index 1e396a9c..8f796be8 100644 --- a/site/layouts/_partials/blog/post-tile.html +++ b/site/layouts/_partials/blog/post-tile.html @@ -1,27 +1,15 @@ {{ $postUrl := .RelPermalink }} diff --git a/site/layouts/_partials/components/call-to-action.html b/site/layouts/_partials/components/call-to-action.html index 530b328e..59f69ca2 100644 --- a/site/layouts/_partials/components/call-to-action.html +++ b/site/layouts/_partials/components/call-to-action.html @@ -1,27 +1,15 @@
diff --git a/site/layouts/_partials/components/footer/columns.html b/site/layouts/_partials/components/footer/columns.html index 65e64636..a4fa5747 100644 --- a/site/layouts/_partials/components/footer/columns.html +++ b/site/layouts/_partials/components/footer/columns.html @@ -1,27 +1,15 @@ {{ $columns := hugo.Data.footer.columns }} diff --git a/site/layouts/_partials/components/footer/copyright.html b/site/layouts/_partials/components/footer/copyright.html index d625f73d..b5ab9509 100644 --- a/site/layouts/_partials/components/footer/copyright.html +++ b/site/layouts/_partials/components/footer/copyright.html @@ -1,27 +1,15 @@ {{ $currentYear := now.Year }} @@ -30,7 +18,7 @@ © 2015 โ€“ {{ $currentYear }} diff --git a/site/layouts/_partials/components/footer/footer.html b/site/layouts/_partials/components/footer/footer.html index aea1a8f3..1be24290 100644 --- a/site/layouts/_partials/components/footer/footer.html +++ b/site/layouts/_partials/components/footer/footer.html @@ -1,27 +1,15 @@