diff --git a/.agents/guidelines b/.agents/guidelines new file mode 120000 index 0000000..6f9d966 --- /dev/null +++ b/.agents/guidelines @@ -0,0 +1 @@ +shared/guidelines \ No newline at end of file diff --git a/.agents/project.md b/.agents/project.md new file mode 120000 index 0000000..7e0bf9b --- /dev/null +++ b/.agents/project.md @@ -0,0 +1 @@ +../docs/project.md \ No newline at end of file diff --git a/.agents/scripts b/.agents/scripts new file mode 120000 index 0000000..96bf06e --- /dev/null +++ b/.agents/scripts @@ -0,0 +1 @@ +shared/scripts \ No newline at end of file diff --git a/.agents/shared b/.agents/shared new file mode 160000 index 0000000..2369ab8 --- /dev/null +++ b/.agents/shared @@ -0,0 +1 @@ +Subproject commit 2369ab816bb5d0feee562bfd6395b0b2852a3a9d diff --git a/.agents/skills b/.agents/skills new file mode 120000 index 0000000..f14734d --- /dev/null +++ b/.agents/skills @@ -0,0 +1 @@ +shared/skills \ No newline at end of file diff --git a/.claude/agents b/.claude/agents new file mode 120000 index 0000000..18e96c9 --- /dev/null +++ b/.claude/agents @@ -0,0 +1 @@ +../.agents/shared/claude/agents \ No newline at end of file diff --git a/.claude/commands b/.claude/commands new file mode 120000 index 0000000..ad85cd8 --- /dev/null +++ b/.claude/commands @@ -0,0 +1 @@ +../.agents/shared/claude/commands \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..3c7da68 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "plansDirectory": ".claude/plans", + "permissions": { + "allow": [ + "Edit(version.gradle.kts)", + "Bash(./gradlew:*)", + "Bash(./config/gradlew:*)", + "Bash(git status:*)", + "Bash(git diff:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git branch:*)", + "Bash(git switch:*)", + "Bash(git checkout:*)", + "Bash(git add:*)", + "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(ls:*)", + "Bash(cat:*)", + "Bash(head:*)", + "Bash(tail:*)", + "Bash(wc:*)", + "Bash(find:*)", + "Bash(rg:*)", + "Bash(grep:*)", + "Bash(mkdir:*)", + "Bash(touch:*)", + "Bash(python3 .agents/skills/update-copyright/scripts/update_copyright.py:*)", + "Bash(.agents/skills/version-bumped/scripts/version-bumped.sh)", + "Bash(./config/pull)", + "Bash(./config/migrate)", + "Skill(pre-pr)", + "Skill(pre-pr:*)" + ], + "deny": [ + "Bash(git reset --hard:*)", + "Bash(git clean -fdx:*)", + "Bash(rm -rf /:*)", + "Bash(rm -rf ~:*)", + "Bash(gh pr merge:*)", + "Bash(gh release create:*)" + ], + "ask": [ + "Bash(git commit:*)", + "Bash(git rebase:*)", + "Bash(git merge:*)", + "Bash(git cherry-pick:*)", + "Bash(./gradlew publish:*)", + "Bash(./gradlew uploadArtifacts:*)", + "Bash(./gradlew clean:*)" + ] + }, + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/init-submodules" + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/secret-scan-gate.sh" + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/pre-pr-gate.sh" + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/publish-version-gate.sh" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/sanitize-source-code.sh" + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.agents/scripts/update-copyright.sh" + } + ] + } + ] + } +} diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000..2b7a412 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..aa71b2f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,66 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. + +# Common formats +*.html text +*.xml text +*.css text +*.scss text +*.svg text +*.js text +*.properties text +*.rtf text +*.yaml text +*.yml text +*.md text + +LICENSE text + +# SQL scripts +*.sql text + +# Java sources +*.java text + +# Kotlin sources +*.kt text +*.kts text + +# Python sources +*.py text + +# Gradle build files +*.gradle text + +# Google protocol buffers +*.proto text + +# Miscellaneous +*.rb text + +# Declare files that will always have CRLF line endings on checkout. +*.bat text eol=crlf + +# Declare files that will always have LF line endings on checkout. +*.sh text eol=lf +gradlew text eol=lf +pull text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.gif binary +*.swf binary +*.jar binary +*.desc binary + +*.scpt binary +*.scssc binary + +# Encrypted files +*.enc binary +*.gpg binary +*.weis binary diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..039657b --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,45 @@ +# GitHub Copilot Instructions + +## Repository context + +This repository is part of the Spine SDK organisation (~40 repos). + +Universal agent instructions are in [`AGENTS.md`](../AGENTS.md) at the +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/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 + +**Do not suggest:** +- Any git history operation — `git commit`, `git push`, `git tag`, + `git rebase`, `git merge`, `git cherry-pick`, `gh pr merge`, or any other + command that writes to history — leave these to the developer. +- Auto-updating dependency versions outside a dedicated update task. +- Feature flags, backwards-compatibility shims, or fallbacks for scenarios + that cannot occur in the current codebase. +- Analytics, telemetry, or tracking code. +- Reflection or unsafe code without explicit approval. diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml new file mode 100644 index 0000000..6f0130e --- /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 new file mode 100644 index 0000000..18ef021 --- /dev/null +++ b/.gitignore @@ -0,0 +1,200 @@ +# >>> shared config (managed by ./config/pull -- do not edit inside this block) >>> +# +# 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 +# +# 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. +# + +# +# This file is used for two purposes: +# 1. ignoring files in the `config` project. +# 2. ignoring files in the projects that import `config` as a sub-module. +# +# Therefore, instructions below are superset of instructions required for all the projects. + +# Temporary output of AI agents. +.output + +# `jenv` local configuration. +.java-version + +# Internal tool directories. +.fleet/ +.junie/memory/ + +# Kotlin temp directories. +**/.kotlin/ + +# IntelliJ IDEA modules and interim config files. +*.iml +.idea/*.xml +.idea/.name +.idea/artifacts +.idea/libraries +.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. + +# Do not ignore the following IDEA settings +!.idea/codeStyleSettings.xml +!.idea/codeStyles/ +!.idea/copyright/ + +# Ignore IDEA config files under `tests` +/tests/.idea/** + +# Gradle interim configs +**/.gradle/** + +# Temp directory for Gradle TestKit runners +**/.gradle-test-kit/** + +# Integration test log files +/tests/_out/** + +# Generated source code +**/generated/** +**/*.pb.dart +**/*.pbenum.dart +**/*.pbserver.dart +**/*.pbjson.dart + +# Generated source code with custom path under `tests` +/tests/**/proto-gen/** + +# Gradle build files +**/build/** +!**/src/**/build/** + +# Build files produced by the IDE +**/out/** + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +# Spine internal directory for storing intermediate artifacts +**/.spine/** + +# --------------------------------------------------------------------------- +# 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 + +# Package Files # +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +.packages +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/ + +# Lychee link-checker cache (created by the `check-links` skill and +# the `Check Links` workflow when run locally). +.lycheecache + +# Hugo docs preview site build artifacts (used by the `check-links` +# skill and the `Check Links` workflow in repos that contain a +# `docs/_preview` Hugo site). +docs/_preview/node_modules/ +docs/_preview/public/ +docs/_preview/resources/ +# <<< shared config <<< diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b37014f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,57 @@ +[submodule "base-libraries"] + path = base-libraries + url = https://github.com/SpineEventEngine/base-libraries.git +[submodule "core-jvm"] + path = core-jvm + url = https://github.com/SpineEventEngine/core-jvm.git +[submodule "validation"] + path = validation + url = https://github.com/SpineEventEngine/validation.git +[submodule "tool-base"] + path = tool-base + url = https://github.com/SpineEventEngine/tool-base.git +[submodule "delivery-server"] + path = delivery-server + url = https://github.com/SpineEventEngine/delivery-server.git +[submodule "config"] + path = config + url = https://github.com/SpineEventEngine/config.git +[submodule "compiler"] + path = compiler + url = https://github.com/SpineEventEngine/compiler.git +[submodule "time"] + path = time + url = https://github.com/SpineEventEngine/time.git +[submodule "logging"] + path = logging + url = https://github.com/SpineEventEngine/logging.git +[submodule "jdbc-storage"] + path = jdbc-storage + url = https://github.com/SpineEventEngine/jdbc-storage.git +[submodule "core-jvm-compiler"] + path = core-jvm-compiler + url = https://github.com/SpineEventEngine/core-jvm-compiler.git +[submodule "ProtoTap"] + path = ProtoTap + url = https://github.com/SpineEventEngine/ProtoTap.git +[submodule "base-types"] + path = base-types + url = https://github.com/SpineEventEngine/base-types.git +[submodule "testlib"] + path = testlib + url = https://github.com/SpineEventEngine/testlib.git +[submodule "change"] + path = change + url = https://github.com/SpineEventEngine/change.git +[submodule "reflect"] + path = reflect + url = https://github.com/SpineEventEngine/reflect.git +[submodule "money"] + path = money + url = https://github.com/SpineEventEngine/money.git +[submodule ".agents/shared"] + path = .agents/shared + url = https://github.com/SpineEventEngine/agents.git + branch = master + update = merge + ignore = all diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..f60c273 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,104 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..0f7bc51 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + diff --git a/.idea/copyright/TeamDev_Open_Source.xml b/.idea/copyright/TeamDev_Open_Source.xml new file mode 100644 index 0000000..cea7fed --- /dev/null +++ b/.idea/copyright/TeamDev_Open_Source.xml @@ -0,0 +1,6 @@ + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..0b8f9a1 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/dictionaries/common.xml b/.idea/dictionaries/common.xml new file mode 100644 index 0000000..d1c3a7b --- /dev/null +++ b/.idea/dictionaries/common.xml @@ -0,0 +1,71 @@ + + + + afghani + arraybuffer + aspx + bytebuffer + callees + closeables + cqrs + dartdocs + dataset + datastore + datastores + deserialized + dirham + enrichable + enrichments + escaper + flushables + googleapis + gradle + grpc + handshaker + hohpe + idempotency + jspecify + kotest + lempira + liskov + melnik + memoized + memoizes + memoizing + mergeable + mikhaylov + millisecs + multitenancy + multitenant + nullable + onclose + oneof + onmessage + onopen + parameterizing + plugable + processmanager + procman + proto's + protodata + protos + sfixed + stderr + stringifier + stringifiers + substituter + switchman + testutil + threeten + tuples + unicast + unregister + unregistering + unregisters + unregistration + websocket + workflows + yevsyukov + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..7be402d --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,915 @@ + + + + \ No newline at end of file diff --git a/.idea/live-templates/README.md b/.idea/live-templates/README.md new file mode 100644 index 0000000..9500667 --- /dev/null +++ b/.idea/live-templates/README.md @@ -0,0 +1,27 @@ +### Live Templates + +This directory contains two live template groups: + +1. `Spine.xml`: shortcuts for the repeated patterns used in the framework. +2. `User.xml`: a single shortcut to generate TODO comments. + +### Installation + +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 + [settings folder][settings_folder]. +2. Restart IntelliJ IDEA: `File -> Invalidate Caches -> Just restart`. +3. Go to `Preferences -> Editor -> Live Templates`. +4. Verify `User` and `Spine` template groups are present. + +[settings_folder]: https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory + +### Configuring `User.todo` template + +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, + 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/live-templates/Spine.xml b/.idea/live-templates/Spine.xml new file mode 100644 index 0000000..369b72d --- /dev/null +++ b/.idea/live-templates/Spine.xml @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/.idea/live-templates/User.xml b/.idea/live-templates/User.xml new file mode 100644 index 0000000..958e2ea --- /dev/null +++ b/.idea/live-templates/User.xml @@ -0,0 +1,11 @@ + + + diff --git a/.junie/guidelines.md b/.junie/guidelines.md new file mode 100644 index 0000000..7c1f866 --- /dev/null +++ b/.junie/guidelines.md @@ -0,0 +1,21 @@ +# Guidelines for Junie and AI Agent from JetBrains + +Read the `../.agents/guidelines/_TOC.md` file to understand: + - the agent responsibilities, + - project overview, + - coding guidelines, + - other relevant topics. + +Also follow the Junie-specific rules described below. + +## Junie Assistance Tips + +When working with Junie AI on the Spine family of projects: + +1. **Project Navigation**: Use `search_project` to find relevant files and code segments. +2. **Code Understanding**: Request file structure with `get_file_structure` before editing. +3. **Code Editing**: Make minimal changes with `search_replace` to maintain project consistency. +4. **Testing**: Verify changes with `run_test` on relevant test files. +5. **Documentation**: Follow KDoc style for documentation. +6. **Kotlin Idioms**: Prefer Kotlin-style solutions over Java-style approaches. +7. **Version Updates**: Remember to update `version.gradle.kts` for PRs. diff --git a/.junie/skills b/.junie/skills new file mode 120000 index 0000000..2b7a412 --- /dev/null +++ b/.junie/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8c5f619 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,135 @@ +# 👋 Welcome, Agents! + +## Orientation + +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. 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/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/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** +unless one of the following is true *right now*: + +1. The currently active skill's `SKILL.md` has a `## Commit authorization` section + that explicitly permits the operation. +2. The user's *current* prompt explicitly requests the operation. + +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/guidelines/safety-rules.md`](.agents/guidelines/safety-rules.md) → *Commits and history-writing*. + +## Other safety rules + +- All code must compile and pass static analysis. +- Do not auto-update external dependencies outside a dedicated update task. +- No analytics, telemetry, or tracking code. +- No reflection or unsafe code without explicit approval. + +See [`.agents/guidelines/safety-rules.md`](.agents/guidelines/safety-rules.md) for the full list. + +## Moving files + +When moving or renaming tracked files, always use `git mv`. Do not simulate a +move by deleting the old file and creating a new one — preserve Git history +unless the user explicitly asks for a fresh replacement. + +If `git mv` fails due to permissions or sandbox restrictions, request approval; +do not fall back to delete/create. + +## Memory + +Team-shared memory lives in `.agents/memory/` (checked into git). Use it for +feedback rules, durable project rationale, and external system pointers. +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). +- Ask: "Would a senior/staff engineer approve this?" +- For non-trivial changes: pause and consider a more elegant solution. +- Fix bugs autonomously — find root cause, no hand-holding, no band-aids. + +## Core Principles + +- Simplicity first: minimal code impact, minimal surface area. +- No laziness: always find root causes. +- Minimal side effects: avoid new bugs. +- Prefer early returns and clear naming. +- Challenge your own work before presenting it. + +## Task planning + +- Write plans to `.agents/tasks/.md` before coding. + See `.agents/tasks/README.md` for format and lifecycle. +- 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/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2ddd0b9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,16 @@ +@AGENTS.md + +## Claude Code-specific notes + +- Use Plan mode (`EnterPlanMode`) for architecture, refactoring, multi-file + changes, or lengthy documentation. Show the plan (`ExitPlanMode`) before + implementing. +- Track live progress with `TaskCreate`. +- In JVM repos: before reading library source code from `~/.gradle/caches`, + follow the `api-discovery` skill — never `unzip` JARs directly. +- Per-developer memory lives in the built-in auto-memory dir. Use it for + personal preferences, ephemeral project state, and per-machine resources. + Litmus test: *would a teammate benefit from this next month?* → repo. + Otherwise → auto-memory. +- This is living team memory. Update it regularly and keep it concise + (<120 lines / ~2.5k tokens). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0a1b5f2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +developers@spine.io. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2185ef6 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +How to contribute +================== +Thank you for wanting to contribute to Spine. The following links will help you get started: + * [Wiki home][wiki-home] — the home of the framework developer's documentation. + * [Getting started with Spine in Java][quick-start] — this guide will walk you through + a minimal client-server “Hello World!” application in Java. + * [Introduction][docs-intro] — this section of the Spine Documentation will help you understand + the foundation of the framework. + +Pull requests +------------- +The work on an improvement starts with creating an issue that describes a bug or a feature. The issue will be used for communications on the proposed improvements. +If code changes are going to be introduced, the issue should also have a link to the corresponding Pull Request. + +Code contributions should: + * Be accompanied by tests. + * Be licensed under the Apache v2.0 license with the appropriate copyright header for each file. + * Formatted according to the code style. See [Wiki home][wiki-home] for the links to + style guides of the programming languages used in the framework. + +Contributor License Agreement +----------------------------- +Contributions to the code of Spine Event Engine framework and its libraries must be accompanied by +Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you own + the intellectual property, then you'll need to sign an individual CLA. + + * If you work for a company which wants you to contribute your work, + then an authorized person from your company will need to sign a corporate CLA. + +Please [contact us][legal-email] for arranging the paper formalities. + +[wiki-home]: https://github.com/SpineEventEngine/SpineEventEngine.github.io/wiki +[quick-start]: https://spine.io/docs/quick-start +[docs-intro]: https://spine.io/docs/introduction +[legal-email]: mailto:legal@teamdev.com diff --git a/ProtoTap b/ProtoTap new file mode 160000 index 0000000..43ac1ac --- /dev/null +++ b/ProtoTap @@ -0,0 +1 @@ +Subproject commit 43ac1acaa282398920c1706230241660a3817041 diff --git a/base-libraries b/base-libraries new file mode 160000 index 0000000..76b2d00 --- /dev/null +++ b/base-libraries @@ -0,0 +1 @@ +Subproject commit 76b2d003f8e5711a50741bbd0b016302f399e82d diff --git a/base-types b/base-types new file mode 160000 index 0000000..ee191de --- /dev/null +++ b/base-types @@ -0,0 +1 @@ +Subproject commit ee191de26a712f0ab318ee8e794929f8e7a89dbe diff --git a/change b/change new file mode 160000 index 0000000..53fe702 --- /dev/null +++ b/change @@ -0,0 +1 @@ +Subproject commit 53fe7029a18920b8cd5d79c8636affb79f77ce84 diff --git a/compiler b/compiler new file mode 160000 index 0000000..313a647 --- /dev/null +++ b/compiler @@ -0,0 +1 @@ +Subproject commit 313a647055faf29942d94047fcbc256386a8ebdc diff --git a/config b/config new file mode 160000 index 0000000..6cee3cf --- /dev/null +++ b/config @@ -0,0 +1 @@ +Subproject commit 6cee3cf52f3044f08a8433cf838713c804ea1084 diff --git a/core-jvm b/core-jvm new file mode 160000 index 0000000..a408b0d --- /dev/null +++ b/core-jvm @@ -0,0 +1 @@ +Subproject commit a408b0d70dafd603efc55b89c8b4b6f3e8c19d3b diff --git a/core-jvm-compiler b/core-jvm-compiler new file mode 160000 index 0000000..0eb8ec3 --- /dev/null +++ b/core-jvm-compiler @@ -0,0 +1 @@ +Subproject commit 0eb8ec332cbad5731193bea5b22a21759300e4f9 diff --git a/delivery-server b/delivery-server new file mode 160000 index 0000000..27aef68 --- /dev/null +++ b/delivery-server @@ -0,0 +1 @@ +Subproject commit 27aef68d77c9b40c19f2f0dd253e351363f76377 diff --git a/docs/project.md b/docs/project.md new file mode 100644 index 0000000..0fdaffc --- /dev/null +++ b/docs/project.md @@ -0,0 +1,18 @@ + + +# 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/init-submodules b/init-submodules new file mode 100755 index 0000000..2ae143d --- /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/jdbc-storage b/jdbc-storage new file mode 160000 index 0000000..1b77832 --- /dev/null +++ b/jdbc-storage @@ -0,0 +1 @@ +Subproject commit 1b778327a6d24c9834102af86e18f9c532b8ac98 diff --git a/logging b/logging new file mode 160000 index 0000000..b883c8f --- /dev/null +++ b/logging @@ -0,0 +1 @@ +Subproject commit b883c8f9707c105f6dafceb94b192dfd8a082662 diff --git a/money b/money new file mode 160000 index 0000000..d8d5f6b --- /dev/null +++ b/money @@ -0,0 +1 @@ +Subproject commit d8d5f6ba522e21b690b68ac0401d393ee226204d diff --git a/reflect b/reflect new file mode 160000 index 0000000..980720d --- /dev/null +++ b/reflect @@ -0,0 +1 @@ +Subproject commit 980720d676e5bca7e85905267627b657e5b5ae01 diff --git a/testlib b/testlib new file mode 160000 index 0000000..1abead1 --- /dev/null +++ b/testlib @@ -0,0 +1 @@ +Subproject commit 1abead10d847fef5d32cdc5787e3aa5e53c4ed7f diff --git a/time b/time new file mode 160000 index 0000000..57d3dd9 --- /dev/null +++ b/time @@ -0,0 +1 @@ +Subproject commit 57d3dd98fea8efcdc4a3843f91143acc2dce87dc diff --git a/tool-base b/tool-base new file mode 160000 index 0000000..cca5d03 --- /dev/null +++ b/tool-base @@ -0,0 +1 @@ +Subproject commit cca5d03ff5f313b5c03beb37c798c1fc65543919 diff --git a/validation b/validation new file mode 160000 index 0000000..c737fc1 --- /dev/null +++ b/validation @@ -0,0 +1 @@ +Subproject commit c737fc15ff3b11e72bf3d2540d763015d8cd5908