From ffc0598431c938fde0c816b00ebde456f8f58534 Mon Sep 17 00:00:00 2001 From: Anatolii Karlov <19729841+karle0wne@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:39:37 +0700 Subject: [PATCH 1/4] Refactor agent rules to progressive guidance --- .gitignore | 10 +- README.md | 140 ++++++++++-------- check.sh | 2 +- ci/github-actions/agent-rules-drift.yml | 9 +- guidance/core.md | 20 +++ hooks/format-kotlin.sh | 27 +--- hooks/lib/common.sh | 25 +--- install.sh | 80 ++++------ references/code-conventions.md | 53 +++++++ references/code-generation.md | 23 +++ references/database.md | 52 +++++++ references/openapi.md | 17 +++ rules/code-conventions.md | 104 ------------- rules/code-generation.md | 7 - rules/database-conventions.md | 98 ------------ rules/index.md | 15 -- rules/profiles/adapter.md | 73 --------- rules/profiles/openapi.md | 17 --- rules/protobuf.md | 4 - skills/provider-adapter/SKILL.md | 51 +++++++ .../references/provider-boundary.md | 41 +++++ .../references/state-and-idempotency.md | 34 +++++ skills/provider-adapter/references/testing.md | 36 +++++ 23 files changed, 444 insertions(+), 494 deletions(-) create mode 100644 guidance/core.md mode change 100644 => 100755 hooks/lib/common.sh create mode 100644 references/code-conventions.md create mode 100644 references/code-generation.md create mode 100644 references/database.md create mode 100644 references/openapi.md delete mode 100644 rules/code-conventions.md delete mode 100644 rules/code-generation.md delete mode 100644 rules/database-conventions.md delete mode 100644 rules/index.md delete mode 100644 rules/profiles/adapter.md delete mode 100644 rules/profiles/openapi.md delete mode 100644 rules/protobuf.md create mode 100644 skills/provider-adapter/SKILL.md create mode 100644 skills/provider-adapter/references/provider-boundary.md create mode 100644 skills/provider-adapter/references/state-and-idempotency.md create mode 100644 skills/provider-adapter/references/testing.md diff --git a/.gitignore b/.gitignore index 7b69d09..3a9a139 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,3 @@ -# IDE +.DS_Store .idea/ *.iml -.vscode/ - -# macOS -.DS_Store - -# Editor scratch -*.swp -*~ diff --git a/README.md b/README.md index 4d4015b..33a36e7 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,122 @@ # code-generation-rules -Shared engineering rules and agent tooling for the organization, mounted into -projects as a git submodule. +Shared agent guidance and deterministic coding hooks for Vality services. -The repository carries three things: +The repository is mounted into a consuming project as `.agent-rules`. The always-on +agent context stays intentionally small: durable cross-project invariants and routes to +more specific guidance. Detailed conventions are read only for tasks that need them. -- `rules/` — common rules and opt-in profiles, as plain markdown. Single source - of truth. -- `hooks/` — scripts wired into agent lifecycle events (Claude Code and Codex). -- `install.sh` / `check.sh` — wire the above into a consuming project, idempotently. +Repository-local code, tests, build configuration, `AGENTS.md`, and `CLAUDE.md` remain +the primary source for local architecture and implementation patterns. -## What belongs here +## Repository layout -Top-level files in `rules/` hold rules that apply to the whole organization. -Rules shared by one family of services live in `rules/profiles/` and are selected -by the consuming project. Anything tied to one service — its build quirks and -local conventions — stays in that service's own `AGENTS.md` / `CLAUDE.md`, -outside the synced block. +- `guidance/core.md` — compact guidance installed into the persistent agent context. +- `skills/provider-adapter/` — workflow and focused references for external + payment/provider integrations. +- `references/` — task-specific guidance for database, generated contracts, OpenAPI, + and cross-layer code decisions. +- `hooks/` — deterministic lifecycle checks; currently Kotlin formatting/linting. +- `agents/` — Claude Code and Codex hook fragments. +- `install.sh` — installs or refreshes the managed agent block and hooks. +- `check.sh` — verifies that the consuming repository is synchronized with the pinned + submodule revision. +- `ci/github-actions/agent-rules-drift.yml` — optional CI drift check. -## Adding to a project +## Install + +From the consuming repository: ```bash git submodule add .agent-rules ./.agent-rules/install.sh ``` -`install.sh` is idempotent and touches only what it owns: - -- registers the Kotlin format hook in `.claude/settings.json` and `.codex/hooks.json` -- writes `@`-imports of the selected rule files into `CLAUDE.md` -- syncs the rule text into `AGENTS.md` between `` and - `` +The installer is idempotent. It owns only: -Everything outside those markers is yours and is never rewritten. +- content between `` and `` in + `AGENTS.md` and `CLAUDE.md`; +- the hook entries registered in `.codex/hooks.json` and `.claude/settings.json`. -Commit the resulting changes together with the submodule pointer. +Content outside the managed block is preserved. -## Rule profiles +Requirements: `git` and `jq`. The Kotlin hook uses Maven only when the target project +contains `ktlint-maven-plugin`. -Without configuration, `install.sh` applies only the common rules. A consuming -project can commit `.agent-rules-profile` with one of these values: +## Profiles -- `common` — common rules only; -- `openapi` — common rules and contract-first OpenAPI conventions; -- `adapter` — common rules and external-adapter conventions. +The default profile is `common`. -For example: +For a persistent project profile, commit `.agent-rules-profile` with exactly one of: ```text +common openapi +adapter ``` -The profile can be overridden for a single command. The same option is accepted -by `check.sh`: +- `common` — compact core plus common task routes. +- `openapi` — common routes plus OpenAPI guidance. +- `adapter` — common routes plus the provider-adapter workflow. + +A profile can be overridden for one invocation: ```bash -./.agent-rules/install.sh --profile openapi -./.agent-rules/check.sh --profile openapi +./.agent-rules/install.sh --profile adapter ``` -The command-line value takes precedence over `.agent-rules-profile`. Unknown or -empty profile values are rejected. +For normal repository use, prefer committing `.agent-rules-profile` so `check.sh` and +CI resolve the same profile without extra flags. -## Updating +## Update ```bash git submodule update --remote .agent-rules ./.agent-rules/install.sh +git diff +``` + +Review and commit the submodule pointer together with generated changes to the managed +agent configuration. + +## CI drift check + +Run: + +```bash +./.agent-rules/check.sh ``` -Review the diff, then commit. The bump is explicit per project — rules never -change under a project without a commit in it. +The command writes nothing. It exits non-zero if the managed blocks or hook +configuration do not match the pinned `.agent-rules` revision. -## Keeping projects honest +A GitHub Actions example is available at: + +```text +ci/github-actions/agent-rules-drift.yml +``` -`check.sh` runs `install.sh --check` with the configured profile: it writes -nothing and exits non-zero when a project has drifted from the submodule it pins. -Wire it into CI with -`ci/github-actions/agent-rules-drift.yml` — note the `submodules: true` on -checkout, without it the check runs against an empty directory. +The consuming workflow must checkout submodules. -## The Kotlin format hook +## Agent routing -`hooks/format-kotlin.sh` runs on the agent's `Stop` event — once per turn, after -the code is generated, in both Claude Code and Codex. +The installed persistent block does not copy the contents of `references/` or +`skills/` into every task. It tells the coding agent when to read them: -When the turn touched Kotlin, it runs `ktlint:format` and `ktlint:check` in one -maven invocation. Both goals are needed: `format` fixes what it can but exits -successfully while staying silent about the rest, so only `check` surfaces the -violations that need a human-shaped fix. Those are handed back to the agent, -which then has to correct them before the turn can end. +- schema/migration/repository/transaction work → `references/database.md`; +- generated-source or Protobuf work → `references/code-generation.md`; +- cross-layer architecture/client/converter decisions not settled by local code → + `references/code-conventions.md`; +- OpenAPI work in the `openapi` profile → `references/openapi.md`; +- external payment/provider integrations in the `adapter` profile → + `skills/provider-adapter/SKILL.md`. -It is deliberately quiet and cheap: with no changed `.kt`/`.kts` files, or in a -project with no ktlint, it exits in well under a tenth of a second without -starting a JVM. +The provider-adapter workflow starts from existing production adapters in the target +repository and loads its narrower references only when the concrete flow needs them. -Note that `ktlint:format` covers the whole module, not just the changed files. -In a project where CI already enforces `ktlint:check`, everything committed is -formatted anyway, so this is a no-op on untouched code. +## Project-specific environment -If a project needs specific environment to run its build (a particular -`JAVA_HOME`, a locale), put it in `.agent-rules.env` in the project root — the -hook sources it when present. That file belongs to the project, not here. +If the build hook needs project-local environment such as `JAVA_HOME`, the consuming +repository may provide `.agent-rules.env` at its root. The hook sources that file when +present. Keep secrets out of this file unless the consuming repository already has an +appropriate secret-injection mechanism and the file itself is not committed. diff --git a/check.sh b/check.sh index 72f769b..d0518f4 100755 --- a/check.sh +++ b/check.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -# CI entry point: fails when the project has drifted from the rules it pins. +# CI entry point: fails when the installed compact guidance/hook configuration drifts. exec "$(cd -- "$(dirname -- "$0")" && pwd)/install.sh" --check "$@" diff --git a/ci/github-actions/agent-rules-drift.yml b/ci/github-actions/agent-rules-drift.yml index d3d4b7f..7120b5a 100644 --- a/ci/github-actions/agent-rules-drift.yml +++ b/ci/github-actions/agent-rules-drift.yml @@ -1,8 +1,4 @@ -# Copy into .github/workflows/ of a project that mounts .agent-rules. -# -# Fails the pull request when the project's agent configuration no longer -# matches the rules revision it pins — usually because the submodule was bumped -# without re-running install.sh. +# Copy into .github/workflows/ of a consuming project. name: agent-rules drift on: @@ -16,8 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - # Without this the check runs against an empty .agent-rules directory. submodules: true - - name: Check agent rules are in sync + - name: Check agent guidance is in sync run: ./.agent-rules/check.sh diff --git a/guidance/core.md b/guidance/core.md new file mode 100644 index 0000000..318d755 --- /dev/null +++ b/guidance/core.md @@ -0,0 +1,20 @@ +# Shared agent guidance + +Treat repository-local code, tests, build configuration, and local `AGENTS.md` / +`CLAUDE.md` content as the primary evidence for how this project is built. + +Keep these organization-level invariants: + +- Do not edit generated sources. Change their source contract or generator instead. +- Preserve published wire/storage compatibility unless the task explicitly requires a + coordinated breaking change or migration. +- Never hardcode, expose, or log credentials, tokens, PANs, bank-account data, or + other payment-sensitive values. +- Prefer an established local implementation pattern over introducing a new framework, + package layout, abstraction, or dependency without a concrete need. +- Run the project's existing focused tests, linters, generators, and compatibility + checks that cover the changed area. +- Do not load or apply detailed guidance that is unrelated to the current task. + +Detailed references are defaults and checklists, not permission to override stronger +repository evidence or explicit task requirements. diff --git a/hooks/format-kotlin.sh b/hooks/format-kotlin.sh index 73267c3..155e877 100755 --- a/hooks/format-kotlin.sh +++ b/hooks/format-kotlin.sh @@ -1,17 +1,8 @@ #!/usr/bin/env bash # Formats Kotlin sources touched during the turn. # -# Wired to the Stop event of Claude Code and Codex alike: both hand the hook a -# JSON event on stdin, and both read exit code 2 with stderr as text to give -# back to the model. So one script serves both. -# -# Contract: -# exit 0 — nothing to do, or everything formatted cleanly -# exit 2 — ktlint found violations it cannot fix; stderr goes back to the agent -# -# It never fails the session for its own reasons: no Kotlin changes, no ktlint, -# no maven, no repository — all of these exit 0. Written against bash 3.2, which -# is still what ships with macOS. +# exit 0 — nothing to do or formatting/checking succeeded +# exit 2 — ktlint found violations it could not fix; stderr is returned to agent set -uo pipefail @@ -25,14 +16,10 @@ REPO_ROOT="$(hook_repo_root)" || exit 0 [ -n "$REPO_ROOT" ] || exit 0 CHANGED="$(hook_changed_files "$REPO_ROOT" '*.kt' '*.kts')" -# The common case is a turn that touched no Kotlin. Leave before paying for a JVM. [ -n "$CHANGED" ] || exit 0 hook_load_project_env "$REPO_ROOT" -# Resolves the module directories to format: for each changed file, the nearest -# ancestor holding a pom.xml. A leaf module inherits the plugin from its parent, -# so running there is enough. maven_module_dirs() { printf '%s\n' "$CHANGED" | while IFS= read -r file; do [ -n "$file" ] || continue @@ -54,9 +41,6 @@ project_has_ktlint_maven() { grep -q . } -# Keeps the ktlint violation lines and drops maven's own failure boilerplate and -# JVM warnings, so the agent gets the findings rather than a wall of noise. Falls -# back to the raw output if the run failed for some reason other than lint. extract_violations() { local raw filtered raw="$(cat)" @@ -70,8 +54,6 @@ extract_violations() { } run_maven_ktlint() { - # No subshell below: the loop is fed by a heredoc precisely so that a - # failure inside it survives into the return value. local status=0 local dirs dir output @@ -85,8 +67,6 @@ run_maven_ktlint() { while IFS= read -r dir; do [ -n "$dir" ] || continue - # Both goals in one invocation: format is silent about what it cannot - # fix — only check reports that — and a single mvn run means a single JVM. if ! output="$(cd "$dir" && mvn --batch-mode -q -Dstyle.color=never ktlint:format ktlint:check 2>&1)"; then status=1 printf '%s\n' "$output" | extract_violations @@ -98,10 +78,7 @@ EOF return "$status" } -# The runner is picked per project. Adding Gradle or the standalone CLI later -# means adding a branch here, not touching anything else. if ! project_has_ktlint_maven; then - # Plenty of repositories mount this submodule without being Kotlin projects. exit 0 fi diff --git a/hooks/lib/common.sh b/hooks/lib/common.sh old mode 100644 new mode 100755 index 2164e40..5dffc98 --- a/hooks/lib/common.sh +++ b/hooks/lib/common.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # Shared helpers for agent lifecycle hooks. # -# Hooks run inside somebody's coding session. The overriding rule for everything -# here: never break the session. Anything unexpected means "exit 0 quietly", not -# "fail loudly". +# Hooks run inside a coding session. Unexpected hook/environment conditions should +# not break the session; deterministic findings from a check may still be returned +# to the agent. HOOK_PAYLOAD="" HOOK_SESSION_ID="" @@ -14,18 +14,12 @@ hook_log() { printf '%s\n' "$*" >&2 } -# Reads the event JSON from stdin and populates HOOK_* variables. -# -# jq is used when available; the fallback covers the two scalar fields we -# actually need, so a machine without jq still gets a working hook. hook_read_payload() { HOOK_PAYLOAD="$(cat)" [ -n "$HOOK_PAYLOAD" ] || return 0 if command -v jq >/dev/null 2>&1; then - # One jq for all three fields: this runs on every turn, so the process - # spawns are worth counting. - IFS=' ' read -r HOOK_SESSION_ID HOOK_CWD HOOK_STOP_ACTIVE </dev/null) EOF else @@ -46,15 +40,12 @@ hook_scalar_fallback() { head -n 1 } -# Echoes the repository root, or returns non-zero when there is no repository. hook_repo_root() { local dir="${HOOK_CWD:-$PWD}" [ -d "$dir" ] || dir="$PWD" git -C "$dir" rev-parse --show-toplevel 2>/dev/null } -# Projects declare their own build environment (JAVA_HOME, locale) here. The -# file belongs to the project; this repository only agrees to read it. hook_load_project_env() { local env_file="$1/.agent-rules.env" [ -f "$env_file" ] || return 0 @@ -64,9 +55,6 @@ hook_load_project_env() { set +a } -# Echoes working-tree files matching the given globs, one per line, relative to -# the repository root: everything changed against HEAD plus untracked files. -# Build and generated output is filtered out. hook_changed_files() { local root="$1" shift @@ -88,11 +76,6 @@ hook_changed_files() { done } -# Loop guard for hooks that block on Stop. -# -# Blocking makes the agent run again, which fires Stop again. The agent's own -# stop_hook_active flag covers Claude Code; this covers the general case by -# refusing to block twice in a row on an identical message. hook_should_block() { local message="$1" local state_dir="${TMPDIR:-/tmp}/agent-rules-hooks" diff --git a/install.sh b/install.sh index c377f2f..e651bf1 100755 --- a/install.sh +++ b/install.sh @@ -1,14 +1,7 @@ #!/usr/bin/env bash -# Wires the shared rules into the project that mounts this submodule. -# -# ./.agent-rules/install.sh apply common rules -# ./.agent-rules/install.sh --profile openapi apply a rule profile -# ./.agent-rules/install.sh --check [--profile ...] report drift, write nothing -# -# Everything here is idempotent and owns a bounded piece of each file: the hook -# entries it registered, and the text between the agent-rules markers. Whatever -# else the project keeps in CLAUDE.md, AGENTS.md or its agent settings is left -# untouched. +# Installs a compact agent-routing block and deterministic hooks into a consuming +# repository. Detailed references/skills remain in .agent-rules and are loaded only +# when the task needs them. set -uo pipefail @@ -63,7 +56,7 @@ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || { case "$RULES_DIR" in "$PROJECT_ROOT"/*) ;; *) - printf 'agent-rules: %s is not inside %s — run install.sh from the project that mounts it\n' \ + printf 'agent-rules: %s is not inside %s — mount it inside the consuming project\n' \ "$RULES_DIR" "$PROJECT_ROOT" >&2 exit 1 ;; @@ -73,9 +66,8 @@ PROFILE="common" PROFILE_FILE="$PROJECT_ROOT/.agent-rules-profile" if [ -f "$PROFILE_FILE" ]; then - PROFILE="$(cat "$PROFILE_FILE")" + PROFILE="$(tr -d '\r\n' <"$PROFILE_FILE")" fi - if [ -n "$PROFILE_OVERRIDE" ]; then PROFILE="$PROFILE_OVERRIDE" fi @@ -102,7 +94,6 @@ report() { fi } -# Writes $2 to $1 unless --check, in which case it only records the difference. apply_file() { local path="$1" desired="$2" label="$3" @@ -117,11 +108,6 @@ apply_file() { printf '%s\n' "$desired" >"$path" } -# --- agent hook registration ------------------------------------------------- - -# Drops any previously registered entry for our hook, then appends the current -# one. That makes the merge both idempotent and self-healing when a project has -# edited the command by hand. merge_hooks() { local target="$1" fragment="$2" root_path="$3" label="$4" local base desired @@ -147,7 +133,6 @@ merge_hooks() { exit 1 } - # Compare normalized so that key order and indentation never look like drift. if [ -f "$target" ] && [ "$(jq -S . "$target" 2>/dev/null)" = "$(printf '%s' "$desired" | jq -S .)" ]; then return 0 @@ -160,44 +145,35 @@ merge_hooks() { printf '%s' "$desired" | jq . >"$target" } -# --- markdown block sync ----------------------------------------------------- +routing_body() { + cat "$RULES_DIR/guidance/core.md" + + cat <<'EOF' -rule_files() { - find "$RULES_DIR/rules" -maxdepth 1 -name '*.md' -not -name 'index.md' | sort +## Load targeted guidance only when relevant + +- Database/schema/repository/transaction work: read `.agent-rules/references/database.md`. +- Generated-source or Protobuf work: read `.agent-rules/references/code-generation.md`. +- Cross-layer architecture/client/converter decisions not settled by local code: consult + `.agent-rules/references/code-conventions.md`. +EOF case "$PROFILE" in openapi) - printf '%s\n' "$RULES_DIR/rules/profiles/openapi.md" + cat <<'EOF' +- OpenAPI contract/generation work: read `.agent-rules/references/openapi.md`. +EOF ;; adapter) - printf '%s\n' "$RULES_DIR/rules/profiles/adapter.md" + cat <<'EOF' +- External provider/payment-adapter work: read + `.agent-rules/skills/provider-adapter/SKILL.md` and follow its progressive-disclosure + workflow. +EOF ;; esac } -# Path of a rule file relative to the project root, e.g. .agent-rules/rules/x.md -rule_rel_path() { - printf '%s' "${1#"$PROJECT_ROOT"/}" -} - -claude_block_body() { - printf '%s\n' "Shared organization rules, synced by .agent-rules/install.sh." - printf '\n' - rule_files | while IFS= read -r file; do - printf '@%s\n' "$(rule_rel_path "$file")" - done -} - -agents_block_body() { - printf '%s\n' "Shared organization rules, synced by .agent-rules/install.sh. Do not edit by hand." - rule_files | while IFS= read -r file; do - printf '\n' - cat "$file" - done -} - -# Replaces the marked block in $1 with $2, keeping everything outside it. Creates -# the file, or appends the block, when either is missing. render_with_block() { local path="$1" body="$2" local block existing @@ -225,8 +201,6 @@ $END_MARKER" ' "$path" } -# --- run --------------------------------------------------------------------- - merge_hooks "$PROJECT_ROOT/.claude/settings.json" \ "$RULES_DIR/agents/claude/settings.hooks.json" \ "hooks" \ @@ -237,12 +211,14 @@ merge_hooks "$PROJECT_ROOT/.codex/hooks.json" \ "" \ ".codex/hooks.json" +BODY="$(routing_body)" + apply_file "$PROJECT_ROOT/CLAUDE.md" \ - "$(render_with_block "$PROJECT_ROOT/CLAUDE.md" "$(claude_block_body)")" \ + "$(render_with_block "$PROJECT_ROOT/CLAUDE.md" "$BODY")" \ "CLAUDE.md" apply_file "$PROJECT_ROOT/AGENTS.md" \ - "$(render_with_block "$PROJECT_ROOT/AGENTS.md" "$(agents_block_body)")" \ + "$(render_with_block "$PROJECT_ROOT/AGENTS.md" "$BODY")" \ "AGENTS.md" if [ "$CHECK_ONLY" -eq 1 ] && [ "$DRIFT" -eq 1 ]; then diff --git a/references/code-conventions.md b/references/code-conventions.md new file mode 100644 index 0000000..6470bd5 --- /dev/null +++ b/references/code-conventions.md @@ -0,0 +1,53 @@ +# Code conventions reference + +Use this reference when a task materially changes application architecture, transport +boundaries, conversion, external clients, or configuration. It is not part of the +always-on prompt. + +## First inspect the repository + +Before applying any convention below, identify the nearest production implementation +of the same responsibility. Prefer the project's established framework and package +structure when it is coherent. + +Do not introduce Spring `Converter`, Lombok, a fixed package taxonomy, a new +handler/service split, or any other abstraction solely because it appears in this +reference. + +## Durable boundaries + +These are useful defaults when the local codebase does not provide stronger evidence: + +- Transport adapters own protocol validation and protocol error mapping, not business + orchestration. +- Services coordinate business scenarios and transaction boundaries. +- Persistence access stays behind repositories or an equivalent persistence boundary. +- External systems stay behind local clients/interfaces so generated stubs and retry + mechanics do not leak into unrelated business code. +- Converters map data; they should not write to the database or perform remote calls. +- Typed DTOs are preferred for stable external contracts over unstructured maps. +- Collection conversion should not introduce N+1 remote or database calls. +- Request/correlation identifiers should be propagated across transport boundaries + when the existing system supports them. +- Expected domain failures should remain distinguishable at the transport boundary. + +## Configuration and clients + +Use the project's existing configuration mechanism. In Spring projects, typed +`@ConfigurationProperties` is generally preferable to scattered string lookups. + +Keep transport, mapping, and business scenario responsibilities separable. Retry, +backoff, authentication, base URL resolution, and serialization should have one +obvious owner rather than being duplicated across handlers. + +## Logging + +Use the project's logging library and style. Preserve identifiers useful for +diagnostics, but do not log sensitive payloads. Large external payloads should not be +promoted to normal INFO-level logging merely for convenience. + +## Testing + +Choose the narrowest test that exercises the changed responsibility. Prefer observable +events over fixed sleeps in asynchronous tests. For external clients, assert both the +outbound request and the mapped result when that behavior is part of the change. diff --git a/references/code-generation.md b/references/code-generation.md new file mode 100644 index 0000000..025d42a --- /dev/null +++ b/references/code-generation.md @@ -0,0 +1,23 @@ +# Generated contracts and Protobuf reference + +Read this only for generated sources, Protobuf, OpenAPI generation, or another +published generated contract. + +## Generated code + +- Never edit generated output as the source of a fix. +- Change the source schema/specification/generator and regenerate. +- Generation should be reproducible in CI from repository state. +- Review generated diffs for unrelated churn. + +## Protobuf + +- Published field numbers are immutable. +- Reserve removed field numbers and names. +- Preserve wire-compatible field types. +- Use the repository's established package/versioning strategy. +- When consumers may receive new enum values, oneof variants, or messages, add or + update compatibility tests and tolerant handling as appropriate. + +If a change is intentionally breaking, make the break explicit and coordinate the +producer/consumer migration rather than silently weakening these rules. diff --git a/references/database.md b/references/database.md new file mode 100644 index 0000000..7fef9bd --- /dev/null +++ b/references/database.md @@ -0,0 +1,52 @@ +# Database change reference + +Read this only for schema, migration, repository, persistence, or transactional-state +work. + +This file intentionally separates compatibility/integrity concerns from historical +implementation preferences. Inspect the target repository before choosing a database +style. + +## Hard compatibility and integrity checks + +- Applied migrations are immutable; introduce a new migration for a schema change. +- Trace a schema change through migration, generated model (if any), write path, read + path, conversion, and tests. +- Keep database writes that form one business transition in one transaction. +- Protect real business keys against concurrent races with an appropriate database + constraint and domain handling. +- Do not build SQL by concatenating untrusted values. +- Batch related reads when a collection path would otherwise produce N+1 queries. +- Verify state-changing batch operations when a partial update would violate the + business transition. +- Transactional outbox/event delivery must commit the domain write and event record + atomically when that pattern is used. + +## Project-specific choices: verify before applying + +The following may be valid conventions in existing Vality repositories, but they are +not universal database truths. Preserve them where the target project already relies +on them; do not introduce them into a new architecture merely because they are listed +here: + +- Flyway location and migration naming; +- jOOQ generation layout; +- absence of foreign keys; +- soft-delete as the default lifecycle; +- `TIMESTAMP WITHOUT TIME ZONE` + `LocalDateTime` interpreted as UTC; +- particular upsert patterns; +- ShedLock/Flyway generator exclusions. + +If the task is specifically to define an organization-wide database policy, decide +these points explicitly and enforce the deterministic parts with migration/CI tooling +rather than relying only on agent prose. + +## Testing + +For PostgreSQL-specific behavior, migrations, locking, conflicts, or transaction +semantics, prefer a real PostgreSQL integration test (for example Testcontainers) +over a substitute database. + +Cover the failure mode introduced by the change: conflict, rollback, concurrent claim, +idempotent replay, empty result, pagination boundary, or retry exhaustion as +applicable. diff --git a/references/openapi.md b/references/openapi.md new file mode 100644 index 0000000..8821c5b --- /dev/null +++ b/references/openapi.md @@ -0,0 +1,17 @@ +# OpenAPI contract reference + +Read this only when the task changes an OpenAPI document, generated API artifact, or +contract compatibility. + +- Treat the repository's root OpenAPI document as the contract source, not generated + server/client code. +- Preserve stable `operationId` values unless the change intentionally coordinates a + breaking API migration. +- Reuse shared parameters, error schemas, security schemes, and components rather than + copying equivalent definitions. +- Make request/response constraints explicit when they are part of the public + contract: required/nullable state, formats, enums, bounds, and collection limits. +- Keep the project's existing request/correlation-id and typed-error conventions. +- Run the repository's OpenAPI validation and regenerate affected artifacts in the + same change. +- Review the generated diff for accidental contract churn. diff --git a/rules/code-conventions.md b/rules/code-conventions.md deleted file mode 100644 index ead56d3..0000000 --- a/rules/code-conventions.md +++ /dev/null @@ -1,104 +0,0 @@ -# Code conventions - -## Architecture and dependencies - -- Transport adapters handle protocol concerns only: they validate the transport - contract, delegate to a service, and translate failures into protocol errors. -- Services implement business scenarios, define the order of operations, and own - transaction boundaries. -- Complex changes to aggregate parts are delegated to focused handlers instead of - growing a single service class. -- Repositories encapsulate persistence and return database or domain models. They do - not build transport responses. -- External systems are hidden behind local client or service interfaces; generated - stubs and retry mechanics do not leak into business code. -- Spring dependencies are provided through constructor injection and stored in - `final`/`val` fields. Java components use Lombok's `@RequiredArgsConstructor` - instead of handwritten constructors when no custom initialization is required. -- Code is organized into the `config`, `config.properties`, `resource`, - `servlet`, `service`, `repository`, `repository.model`, `scheduler`, `client`, - `client.model`, `converter`, and `extensions` packages. -- Standalone classes and models are placed in separate files. - -## DTOs and converters - -- External API requests and responses are represented by typed DTOs, without - `Map`. -- Transport models are converted before reaching repositories. Simple entities may - use generated persistence models; aggregates use local domain models. -- JSON property names are specified with Jackson annotations, and closed sets of - values are represented by enums. -- Model conversion is performed by dedicated `@Component` classes implementing - Spring's `Converter`. -- Requests and responses are created by converters. -- Converters map data but do not write to the database or call external systems. -- Optional fields are set only when present. An omitted value and an explicitly - empty value remain distinct when the API contract distinguishes them. -- Unsupported conversion directions fail explicitly instead of returning `null`. -- Concrete converters are provided through constructor injection. -- Related data for collections is loaded in batches before conversion; converters - must not introduce N+1 calls. - -## Business operations - -- Writes that form one business operation run in one transaction. -- Collection equality ignores order when order has no business meaning. -- One operation timestamp is reused for the persisted changes. - -## REST-to-gRPC gateways - -- Generated REST interfaces define the transport contract. Controllers and resources - implement them, validate transport concerns, and delegate without duplicating the - contract or containing business orchestration. -- Orchestration services build typed gRPC requests, invoke generated clients, and use - dedicated converters for REST-to-Protobuf and Protobuf-to-REST mapping. -- A request or correlation identifier received at the public boundary is propagated to - every downstream request and included in logs and typed error responses. -- gRPC failures are mapped centrally to the API's declared error model. At minimum, - invalid input, unauthenticated, forbidden, not found, conflict, throttling, deadline, - downstream unavailability, and unexpected internal failures remain distinguishable. -- Transport failures never produce an untyped or accidentally empty error response. - -## Kotlin style - -- Calls to regular functions and methods use positional arguments. -- Named arguments are allowed for constructors and annotations. -- Constants belonging to a single class are placed in its `private companion object`. -- Shared constants are placed in the appropriate `constants/*.kt` file. - -## Configuration - -- External integrations are replaced with mock or stub beans in tests. -- Settings are grouped into typed `@ConfigurationProperties`. -- Retry policies, backoff, and asynchronous executors are configured centrally and - injected by name. - -## External clients - -- The client is responsible for transport, the converter for mapping, and the service - for the business scenario. -- A client owns its generated stub and applies the configured retry policy in one - place. -- Missing recipients or input for an optional side effect causes an early return - without an external call. -- Asynchronous entry points catch and log failures that cannot be returned to the - caller. - -## Errors and logging - -- Expected domain failures use specific exception types and are mapped to transport - statuses at the transport boundary. -- Logs use parameterized placeholders instead of string concatenation and include - available request and domain identifiers. -- Large payloads and user content are logged only at `DEBUG` or `TRACE`. -- Transport adapters log request boundaries; services and handlers log business - steps without duplicating the full payload. - -## Testing - -- Pure converters and external-client orchestration are covered by unit tests, - including optional values, empty collections, invalid input, retries, and early - returns. -- External calls use mocks or stubs and assert the generated request as well as the - returned result. -- Asynchronous tests wait for an observable event instead of using a fixed `sleep`. diff --git a/rules/code-generation.md b/rules/code-generation.md deleted file mode 100644 index 0487857..0000000 --- a/rules/code-generation.md +++ /dev/null @@ -1,7 +0,0 @@ -# Code generation - -- Generated sources are never edited manually. -- Protobuf field numbers are immutable after publication. -- Removed protobuf fields and names are reserved. -- OpenAPI changes are validated and generated clients are rebuilt in the same pull request. -- Generation must be deterministic and runnable in CI without repository-local state. diff --git a/rules/database-conventions.md b/rules/database-conventions.md deleted file mode 100644 index 7de38e4..0000000 --- a/rules/database-conventions.md +++ /dev/null @@ -1,98 +0,0 @@ -# Database conventions - -## Stack and migrations - -- Migrations are run by Flyway from `src/main/resources/db/migration`. -- Every schema change is introduced by a new immutable migration; an applied - migration is never rewritten. -- Migration names follow `V__.sql` and describe one - complete schema or index change. -- `IF NOT EXISTS` is used for supported PostgreSQL objects. -- Primary keys, constraints, and indexes are defined explicitly and - given meaningful names. -- Storage invariants use database defaults and `NOT NULL` constraints and are also - represented consistently in converters and repositories. -- Flyway and ShedLock tables are excluded from jOOQ code generation. -- No foreign keys are used. - -## jOOQ - -- Flyway runs before jOOQ code generation. -- Generated classes are created in `target/generated-sources/jooq`. -- Generated tables, records, POJOs, and enums are used; generated code is not edited - manually. -- A schema change is traced through migration, generated model, input conversion, - write query, read model, output conversion, and tests. -- Repositories use `DSLContext` and keep jOOQ queries out of services and transport - adapters. -- Inserts populate the complete model and map it to a generated record. Updates set - only fields that the operation is allowed to change. -- Insert, update, and upsert operations set their audit timestamp in UTC. -- Query aliases match read-model property names when results are mapped with - `fetchInto`. -- Empty collections are handled before `IN` queries and collection writes. -- Upsert is used only with a defined business key. Conflict columns and the minimal - set of updated columns are listed explicitly. -- Related data for result collections is fetched in batches to avoid N+1 queries. -- Type-safe jOOQ DSL is preferred. PostgreSQL-specific plain SQL uses bind values or - `inline(...)`, never string concatenation of user input. - -## Data lifecycle - -- When designing, priority is given to soft-delete -- Replacing related records and updating the owning aggregate happen in one transaction. -- Absence from a single-row query is represented consistently. - -## State transitions - -- A state transition, its validation, and all resulting writes run in one transaction. -- Repeating the same transition with the same business data is idempotent. A transition - that conflicts with an existing final state fails with a specific domain error. -- Business keys are protected by explicit unique constraints. Upsert or conflict - handling complements domain validation when concurrent requests may race. -- Batch state changes verify that the number of affected rows matches the expected - number; a partial update fails the transaction. - -## Transactional event delivery - -- The domain write and insertion of its delivery event are committed in the same - database transaction. A failure rolls back both. -- Every event has a stable identifier, a deterministic sequence or ordering key, a - delivery status, an attempt count, and the time at which it became eligible. -- The event payload contains the immutable data required for delivery; a retry does not - rebuild a materially different event from current mutable state. -- Concurrent workers claim disjoint events with a short transaction, for example by - using a lease or `FOR UPDATE SKIP LOCKED`. A database row lock is not held while a - remote call is in flight. -- Delivery is idempotent by event identifier. A worker records success only after the - recipient accepts the event and safely retries an ambiguous outcome. -- Retries are bounded and use configured backoff and next-attempt time. Exhausted events - move to an explicit terminal or dead-letter state and remain observable. - -## Time and secrets - -- `TIMESTAMP WITHOUT TIME ZONE` and `LocalDateTime` are used; values are - interpreted as UTC. -- Current timestamps are created explicitly in UTC and shared across all writes in - one business operation. -- Test credentials are allowed only for embedded PostgreSQL and Testcontainers. - -## Integration testing - -- Migration, query, filter, search, and transactional changes are tested against a - real PostgreSQL instance provided by embedded PostgreSQL or Testcontainers. -- Tests clean only the data they own and do not depend on execution order. -- Repository tests assert persisted values, conflict/update behavior, and empty-result - boundaries, not only affected-row counts. -- CRUD scenarios verify create, read, update, logical deletion, and the values in both - the database and returned model. -- Filtering and search rules include positive, negative, and boundary cases; - pagination also covers page boundaries and continuation tokens. -- Stateful-operation tests cover idempotent replay, conflicting final states, - concurrent requests, affected-row mismatches, and full transactional rollback. -- Event-delivery tests cover atomic domain/event rollback, concurrent workers, ordered - delivery, duplicate replay, ambiguous responses, process restart, retry backoff, and - exhaustion of the attempt limit. - -Database changes are verified with Flyway, jOOQ code generation, and repository -integration tests using PostgreSQL. diff --git a/rules/index.md b/rules/index.md deleted file mode 100644 index 254ff87..0000000 --- a/rules/index.md +++ /dev/null @@ -1,15 +0,0 @@ -# Shared engineering rules - -Top-level rules apply to every repository that mounts this submodule. Profiles -extend them for a selected family of services; anything specific to a single -service belongs in that service. - -- [Code generation](code-generation.md) -- [Code conventions](code-conventions.md) -- [Database conventions](database-conventions.md) -- [Protobuf](protobuf.md) - -## Profiles - -- [OpenAPI contract](profiles/openapi.md) -- [Adapter](profiles/adapter.md) diff --git a/rules/profiles/adapter.md b/rules/profiles/adapter.md deleted file mode 100644 index e7ce7e7..0000000 --- a/rules/profiles/adapter.md +++ /dev/null @@ -1,73 +0,0 @@ -# Adapter conventions - -These rules extend the common rules for services that integrate with external -providers. - -## Architecture and flow - -- Transport entry points validate the transport contract and delegate to services. -- Services coordinate the integration scenario; step-specific behavior is placed in - focused handlers selected by an explicit state or operation type. -- State transitions and transport intents are built centrally instead of being - assembled independently by handlers. -- Provider request and response models, converters, constants, and error handling - stay behind the provider client boundary. -- Runtime configuration keys, provider method names, URL paths, statuses, and error - codes are declared centrally as constants or enums. -- Callback handlers dispatch by an explicit callback type and are idempotent. A - repeated callback must not overwrite completed state or repeat a side effect. - -## Configuration and clients - -- Application settings use typed `@ConfigurationProperties` with `@Validated` and - field constraints for required values. -- Per-operation runtime options are validated by a dedicated validator before a - converter or handler accesses them as non-null values. -- Provider calls use the application's configured `RestClient` and `ObjectMapper`. -- Base URLs, environment selection, request paths, and authorization headers are - resolved centrally. -- Provider requests and responses use typed DTOs. Internal configuration and helper - fields that are not part of the wire contract are excluded from serialization. -- An empty response body or a body that cannot be parsed is handled explicitly and - mapped to a stable integration error. -- HTTP status errors, provider errors, and response parsing errors are distinguished - before being mapped to domain failures. - -## Secrets and logging - -- Provider credentials and tokens are obtained through the configured secret service, - such as Vault. They are never hardcoded or included in logs. -- Kotlin files use a file-level `private val log = KotlinLogging.logger {}` and lazy - logging blocks. -- PANs, phone numbers, bank accounts, tokens, and other sensitive fields are masked - before logging or storing diagnostic metadata. -- External request, response, and callback payloads pass through the shared log - sanitizer before being logged. -- DTOs containing sensitive values provide a safe `toString()` or are never logged as - complete objects. - -## State and polling - -- Multi-step operation state is held in a dedicated context and serialized into the - transport's continuation state through one serializer. -- Missing continuation state creates a new context; malformed state fails explicitly. -- Serialized context changes are backward compatible with states produced by the - previous deployed version and are covered by compatibility tests. -- Polling metadata, including the deadline and next interval, is stored with the - operation state. -- Polling is bounded by a deadline. Pending and unknown non-final statuses schedule - the next attempt using the configured backoff instead of looping immediately. -- Final success, final failure, timeout, transport failure, and malformed provider - responses produce distinct, deterministic outcomes. - -## Testing - -- Provider HTTP integration tests use WireMock with the application context and real - client serialization. -- Every provider method covers success, provider failure, HTTP failure, empty body, - malformed body, and required-field validation where applicable. -- Stateful flows cover pending-to-success, pending-to-failure, polling timeout, and - callback replay. -- Tests assert outbound method, path, headers, and body as well as the mapped result. -- Shared flow fixtures and builders contain transport mechanics; test cases describe - scenario-specific mocks, actions, and assertions. diff --git a/rules/profiles/openapi.md b/rules/profiles/openapi.md deleted file mode 100644 index a49a137..0000000 --- a/rules/profiles/openapi.md +++ /dev/null @@ -1,17 +0,0 @@ -# OpenAPI contract conventions - -These rules extend the common rules for repositories that own an OpenAPI contract and -publish generated server or client artifacts. - -## Contract structure - -- One root OpenAPI document is the source entry point. Paths and reusable components - are split into focused files and connected through local `$ref` references. -- Every operation has a stable, unique `operationId`, an appropriate tag, and explicit - request parameters, request body, responses, and security requirements. -- Common parameters, error responses, schemas, and security schemes are defined once - under `components` and reused instead of being copied between operations. -- Public operations require and document a request or correlation identifier and use a - shared typed error schema. -- Schema fields declare `required`, `nullable`, formats, enums, bounds, and collection - constraints explicitly whenever they are part of the contract. diff --git a/rules/protobuf.md b/rules/protobuf.md deleted file mode 100644 index cf270e1..0000000 --- a/rules/protobuf.md +++ /dev/null @@ -1,4 +0,0 @@ -# Protobuf - -Use versioned packages and directories. Keep wire-compatible field types, reserve removed fields, -and add compatibility tests when a consumer may receive a newly added oneof variant or enum value. diff --git a/skills/provider-adapter/SKILL.md b/skills/provider-adapter/SKILL.md new file mode 100644 index 0000000..7ac3615 --- /dev/null +++ b/skills/provider-adapter/SKILL.md @@ -0,0 +1,51 @@ +--- +name: provider-adapter +description: Implement or modify an external payment/provider adapter, including provider clients, callbacks, polling, continuation state, error mapping, or adapter integration tests. Do not use for ordinary internal service changes that do not cross a provider boundary. +--- + +# Provider adapter + +The repository itself is the first specification. Before designing the change, find +the closest existing production adapters and inspect at least one successful analogue +for the same operation type. Prefer established local abstractions when they satisfy +the required semantics. + +## Non-negotiable domain behavior + +Keep these invariants when they apply to the flow: + +- repeated callbacks must not repeat a completed side effect or overwrite final state; +- polling must be bounded by a deadline and use configured backoff rather than an + immediate unbounded loop; +- continuation/state changes must remain readable by the next deployed version unless + the task explicitly coordinates a migration; +- credentials, tokens, PANs, account identifiers, and other sensitive values must not + leak through source code, logs, exceptions, or diagnostic metadata; +- HTTP/transport failures, provider-declared failures, malformed responses, timeout, + and final business outcomes must not collapse accidentally into one ambiguous path. + +## Workflow + +1. Identify the operation: synchronous request, redirect, polling, callback, + recurrent operation, refund, payout, or another established flow. +2. Find the nearest existing adapter(s) and map the local boundaries: transport entry + point, scenario/service, provider client, state/context, error mapping, and tests. +3. Read only the references needed for the task: + - client/configuration/error/logging work → `references/provider-boundary.md`; + - callback, polling, or multi-step continuation state → + `references/state-and-idempotency.md`; + - before finalizing an adapter behavior change → `references/testing.md`. +4. Implement the smallest change that preserves the local architecture and the domain + invariants above. Do not refactor unrelated adapters to make them match this skill. +5. Run the project's focused tests plus the existing formatter/linter/generator checks + for touched code. +6. If repository evidence conflicts with this skill on a non-safety architectural + preference, follow the repository and record the discrepancy rather than silently + rewriting the project toward a generic template. + +## What not to encode here + +Package names, Spring annotations, specific converter interfaces, logging libraries, +and DTO layout are implementation choices unless the target repository already +standardizes them. Consult `../../references/code-conventions.md` only when the task +actually needs an architectural decision not settled by local code. diff --git a/skills/provider-adapter/references/provider-boundary.md b/skills/provider-adapter/references/provider-boundary.md new file mode 100644 index 0000000..9646858 --- /dev/null +++ b/skills/provider-adapter/references/provider-boundary.md @@ -0,0 +1,41 @@ +# Provider boundary + +Use for provider client, configuration, serialization, error mapping, or sensitive +logging work. + +## Client and configuration + +Keep provider-specific transport details behind one clear boundary. Centralize base +URL/environment selection, authentication, request paths, and serialization according +to the target repository's existing client stack. + +Use typed request/response DTOs for stable provider contracts. Treat empty bodies, +malformed bodies, unexpected status codes, and provider-declared errors explicitly. + +Runtime configuration required for an operation should be validated before business +logic assumes it is present. + +## Error model + +Preserve enough information to distinguish: + +- transport/HTTP failure; +- provider-declared rejection/failure; +- response parsing/shape failure; +- retryable/pending outcome; +- final business failure; +- unexpected internal failure. + +Map these into the repository's existing domain error model rather than creating a +parallel hierarchy unless necessary. + +## Secrets and diagnostics + +Obtain credentials from the project's configured secret mechanism. Never hardcode +them. + +Sanitize sensitive provider request/response/callback data before logging. Do not rely +on a DTO's default `toString()` when it can expose PANs, tokens, phone numbers, bank +accounts, credentials, or equivalent data. + +Use the project's existing sanitizer and logging abstraction when present. diff --git a/skills/provider-adapter/references/state-and-idempotency.md b/skills/provider-adapter/references/state-and-idempotency.md new file mode 100644 index 0000000..33cb24f --- /dev/null +++ b/skills/provider-adapter/references/state-and-idempotency.md @@ -0,0 +1,34 @@ +# State, polling, callbacks, and idempotency + +Use for multi-step flows, continuation state, polling, callbacks, or replay handling. + +## Continuation state + +Have one obvious serializer/decoder for continuation state. Missing state may create a +new context when the protocol defines that behavior; malformed state should fail +deterministically rather than being silently interpreted as a new operation. + +Before changing a serialized context, inspect examples/states produced by the previous +deployed version. Add a compatibility test when old state can survive a deployment. + +## Polling + +Persist enough metadata to continue safely after process restart, including deadline +and backoff/next-attempt state when the surrounding framework does not already own it. + +Pending or unknown non-final provider statuses should schedule another attempt through +the existing retry mechanism. Do not tight-loop. + +Make timeout distinct from provider failure and malformed response. + +## Callbacks + +Dispatch by an explicit callback/event type when the provider exposes more than one +semantic event. + +Treat callback delivery as at-least-once unless the provider contract proves +otherwise. A replay must be safe: completed state should not be overwritten and a +side effect should not be emitted twice. + +When idempotency depends on persistence or a business key, enforce it at the layer that +can actually survive concurrent requests; an in-memory guard is insufficient. diff --git a/skills/provider-adapter/references/testing.md b/skills/provider-adapter/references/testing.md new file mode 100644 index 0000000..c52cd6e --- /dev/null +++ b/skills/provider-adapter/references/testing.md @@ -0,0 +1,36 @@ +# Provider adapter test matrix + +Use the smallest relevant subset; do not generate a ceremonial test suite when the +provider operation cannot produce a listed case. + +## Client/contract behavior + +For each changed provider method, consider: + +- success; +- provider-declared failure; +- transport/HTTP failure; +- empty response; +- malformed response; +- missing/invalid required runtime data. + +Assert the outbound method/path/headers/body when those are part of the adapter +contract, as well as the mapped result. + +Use the repository's established HTTP test mechanism. In Spring/WireMock projects, +prefer real serialization through the application client over mocking the client +itself when testing wire compatibility. + +## Stateful behavior + +For polling or callbacks, consider: + +- pending → success; +- pending → final provider failure; +- timeout/deadline; +- callback replay; +- old serialized state produced by the previous deployed version; +- restart/retry behavior if continuation state is persistent. + +The test name and fixture should describe the business scenario rather than reproduce +transport boilerplate. From 033b7ec8c673477613007e61cecc57f368df983f Mon Sep 17 00:00:00 2001 From: Anatolii Karlov <19729841+karle0wne@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:25:15 +0700 Subject: [PATCH 2/4] Update SKILL.md --- skills/provider-adapter/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/skills/provider-adapter/SKILL.md b/skills/provider-adapter/SKILL.md index 7ac3615..b26c062 100644 --- a/skills/provider-adapter/SKILL.md +++ b/skills/provider-adapter/SKILL.md @@ -23,6 +23,7 @@ Keep these invariants when they apply to the flow: leak through source code, logs, exceptions, or diagnostic metadata; - HTTP/transport failures, provider-declared failures, malformed responses, timeout, and final business outcomes must not collapse accidentally into one ambiguous path. +- OpenAPI contract changes → `../../references/openapi.md` ## Workflow From ecca285304989fbf5d2a7086f6e870e6c76d6a91 Mon Sep 17 00:00:00 2001 From: Anatolii Karlov <19729841+karle0wne@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:26:48 +0700 Subject: [PATCH 3/4] Update SKILL.md with additional references Add references for Protobuf and persistence/schema changes. --- skills/provider-adapter/SKILL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skills/provider-adapter/SKILL.md b/skills/provider-adapter/SKILL.md index b26c062..3f51344 100644 --- a/skills/provider-adapter/SKILL.md +++ b/skills/provider-adapter/SKILL.md @@ -24,6 +24,8 @@ Keep these invariants when they apply to the flow: - HTTP/transport failures, provider-declared failures, malformed responses, timeout, and final business outcomes must not collapse accidentally into one ambiguous path. - OpenAPI contract changes → `../../references/openapi.md` +- Protobuf/generated contract changes → `../../references/code-generation.md` +- persistence/schema changes → `../../references/database.md` ## Workflow From df2f691e9615a8898cac7d1b23c1b631741fdd8b Mon Sep 17 00:00:00 2001 From: Anatolii Karlov <19729841+karle0wne@users.noreply.github.com> Date: Wed, 19 Aug 2026 23:27:57 +0700 Subject: [PATCH 4/4] Update SKILL.md --- skills/provider-adapter/SKILL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/skills/provider-adapter/SKILL.md b/skills/provider-adapter/SKILL.md index 3f51344..459c19f 100644 --- a/skills/provider-adapter/SKILL.md +++ b/skills/provider-adapter/SKILL.md @@ -23,9 +23,12 @@ Keep these invariants when they apply to the flow: leak through source code, logs, exceptions, or diagnostic metadata; - HTTP/transport failures, provider-declared failures, malformed responses, timeout, and final business outcomes must not collapse accidentally into one ambiguous path. + +## Load additional guidance only when relevant + - OpenAPI contract changes → `../../references/openapi.md` - Protobuf/generated contract changes → `../../references/code-generation.md` -- persistence/schema changes → `../../references/database.md` +- Persistence/schema changes → `../../references/database.md` ## Workflow