From 925c19f2d14f10968707a313a8370e85659ed9dc Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 23 Jul 2026 15:38:24 +0200 Subject: [PATCH 01/11] Add skills. --- .agents/guidelines/writing-style.md | 48 +++++ .agents/skills/gradle-engineer/SKILL.md | 96 +++++++++ .../skills/gradle-engineer/agents/openai.yaml | 4 + .../references/gradle-practices.md | 151 +++++++++++++ .agents/skills/kotlin-engineer/SKILL.md | 67 ++++++ .../skills/kotlin-engineer/agents/openai.yaml | 4 + .../references/kotlin-policy.md | 96 +++++++++ .agents/skills/reviewer/SKILL.md | 81 +++++++ .agents/skills/reviewer/agents/openai.yaml | 4 + .agents/skills/security-engineer/SKILL.md | 93 ++++++++ .../security-engineer/agents/openai.yaml | 4 + .../references/trust-boundaries.md | 204 ++++++++++++++++++ .agents/skills/test-engineer/SKILL.md | 81 +++++++ .../skills/test-engineer/agents/openai.yaml | 4 + .../test-engineer/references/test-patterns.md | 133 ++++++++++++ .agents/skills/writer/SKILL.md | 87 ++++++++ .agents/skills/writer/agents/openai.yaml | 4 + .claude/skills | 1 + .github/copilot-instructions.md | 35 +++ AGENTS.md | 63 ++++++ CLAUDE.md | 11 + PROJECT.md | 118 ++++++++++ 22 files changed, 1389 insertions(+) create mode 100644 .agents/guidelines/writing-style.md create mode 100644 .agents/skills/gradle-engineer/SKILL.md create mode 100644 .agents/skills/gradle-engineer/agents/openai.yaml create mode 100644 .agents/skills/gradle-engineer/references/gradle-practices.md create mode 100644 .agents/skills/kotlin-engineer/SKILL.md create mode 100644 .agents/skills/kotlin-engineer/agents/openai.yaml create mode 100644 .agents/skills/kotlin-engineer/references/kotlin-policy.md create mode 100644 .agents/skills/reviewer/SKILL.md create mode 100644 .agents/skills/reviewer/agents/openai.yaml create mode 100644 .agents/skills/security-engineer/SKILL.md create mode 100644 .agents/skills/security-engineer/agents/openai.yaml create mode 100644 .agents/skills/security-engineer/references/trust-boundaries.md create mode 100644 .agents/skills/test-engineer/SKILL.md create mode 100644 .agents/skills/test-engineer/agents/openai.yaml create mode 100644 .agents/skills/test-engineer/references/test-patterns.md create mode 100644 .agents/skills/writer/SKILL.md create mode 100644 .agents/skills/writer/agents/openai.yaml create mode 120000 .claude/skills create mode 100644 .github/copilot-instructions.md create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 PROJECT.md diff --git a/.agents/guidelines/writing-style.md b/.agents/guidelines/writing-style.md new file mode 100644 index 0000000..5f77463 --- /dev/null +++ b/.agents/guidelines/writing-style.md @@ -0,0 +1,48 @@ +# Writing style + +Apply these rules to changed Markdown and KDoc prose. Preserve a file's valid local +structure when it is more specific. + +## Write for the reader + +- Explain the reader's problem before the implementation or solution details. +- Use direct, active sentences and concrete verbs. +- Prefer precise project terms over synonyms, marketing language, or jargon. +- State prerequisites before steps and expected outcomes after them. +- Verify factual claims against current code, tests, build configuration, or behavior. + +## Format prose consistently + +- Keep changed Markdown and KDoc prose at no more than 100 characters per source line + when the syntax permits. +- Use sentence case for headings. +- Use one top-level heading and do not skip heading levels. +- Format paths, identifiers, Gradle tasks, properties, flags, commands, and literal values + as code. +- Put multiline commands, configuration, source, output, and other machine text in fenced + code blocks with a language identifier when one applies. +- Use relative links for repository files. +- Prefer reference-style links for external destinations. + +## Use typography deliberately + +- Use typographic quotation marks only for actual page or section titles, such as the + “Requirements” section. +- Do not use quotation marks for emphasis, identifiers, invented labels, or technical + terms. Use plain prose, italics, or code formatting as appropriate. +- Avoid runts and orphans. Reflow or rewrite a paragraph, list item, or table cell whose + final source line contains one word or an unusually short fragment. +- Avoid widows in rendered or paginated material. Keep headings with their following + content and avoid isolating a paragraph's opening or closing line. +- Avoid rivers when a rendered layout shows repeated aligned gaps through adjacent lines. + Reflow or rephrase the smallest affected passage. +- Do not add manual line breaks or non-breaking spaces merely to force a Markdown layout. + +## Edit minimally + +- Correct grammar only where needed for correctness, clarity, or consistency. +- Limit rewording and reflow to the affected sentence or paragraph. +- Preserve the author's meaning, the document's ownership, and established terminology. +- Do not restyle unrelated prose while making a focused change. +- Preserve code, machine-generated text, exact command output, logs, URLs, frontmatter, + serialized data, and license or copyright text verbatim unless the task targets it. diff --git a/.agents/skills/gradle-engineer/SKILL.md b/.agents/skills/gradle-engineer/SKILL.md new file mode 100644 index 0000000..b58915d --- /dev/null +++ b/.agents/skills/gradle-engineer/SKILL.md @@ -0,0 +1,96 @@ +--- +name: gradle-engineer +description: >- + Apply repository-specific Gradle engineering policy when changing or reviewing + build scripts, buildSrc, Kotlin DSL, plugin, extension, or task APIs, lazy + configuration, task state, configuration-cache behavior, TestKit coverage, + consumer compatibility, or publication metadata. +--- + +# Gradle engineering + +Preserve Gradle semantics across the plugin implementation and its build. Treat +configuration avoidance, task state, consumer compatibility, and cross-platform +behavior as public contracts. + +## Start + +1. Read [`PROJECT.md`](../../../PROJECT.md) for the project map and supported behavior. +2. Read the affected build scripts, plugin API, task types, and nearby unit or + functional tests in full. +3. Trace values from the extension through `Provider` wiring into task inputs, + execution behavior, outputs, and local state. +4. Read [Gradle practices](references/gradle-practices.md) when changing task + modeling, compatibility, build logic, publication, or platform behavior. +5. Clarify only material ambiguity in the public DSL, compatibility floor, or + verification target before editing. + +## Coordinate responsibilities + +- Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin source and + build-logic implementation quality. +- Apply [`test-engineer`](../test-engineer/SKILL.md) to test structure, + assertions, fixtures, and regression design. +- Apply [`security-engineer`](../security-engineer/SKILL.md) to release + downloads, integrity metadata, offline reuse, tokens, archives, and paths. +- Apply [`writer`](../writer/SKILL.md) to public DSL documentation and examples. +- Apply [`reviewer`](../reviewer/SKILL.md) for findings-first review output. + +## Apply the Gradle model + +- Register tasks lazily and retain `TaskProvider` or `Provider` values. Avoid + eager realization and configuration-time filesystem or network work. +- Model configurable values with `Property`, `ListProperty`, `MapProperty`, + `DirectoryProperty`, `RegularFileProperty`, and provider transformations. +- Wire producer-backed files through providers or file collections so Gradle + retains task dependencies. +- Declare every task input, output, local-state file, and internal value + according to its actual semantics. Choose path sensitivity deliberately. +- Keep secrets internal. Never expose a token as an input, log value, cache + identity, or publication value. +- Keep task actions configuration-cache safe. Inject execution services and + avoid accessing `Project` or mutable configuration state at execution time. +- Disable caching or up-to-date reuse when in-place writes, mutable external + sources, or validation of local state make reuse unsound. +- Prefer Kotlin DSL and existing convention plugins over ad hoc Groovy or + duplicated module configuration. + +## Preserve repository contracts + +- Keep consumer builds compatible with Gradle 8.14.4 and newer. Do not infer + the compatibility floor from the current wrapper version. +- Compile against the Kotlin runtime supplied by supported Gradle versions. + Do not publish a conflicting Kotlin standard library with the plugin. +- Preserve Java 17 bytecode compatibility unless the supported Gradle floor is + intentionally changed with tests and documentation. +- Keep the public configuration Gradle-native. Treat generated Embed Code + configuration as an internal implementation detail. +- Preserve Linux and Windows behavior for paths, executable names, permissions, + process arguments, and task outputs. +- Keep plugin ID, implementation class, website, VCS URL, tags, artifact + coordinates, POM metadata, and license packaging aligned. +- Allow the install task to remain ungrouped and hidden from the standard task + listing. Do not flag or change that behavior without a user-facing reason. +- Do not import organization-wide configuration-repository filtering or add a + rule requiring dependency groups to contain `spine`; neither is a repository + policy. + +## Verify + +Run the narrowest relevant command first, then broaden: + +1. `./gradlew :gradle-plugin:test --tests ` +2. `./gradlew :gradle-plugin:functionalTest --tests ` +3. `./gradlew :gradle-plugin:test :gradle-plugin:functionalTest` +4. `./gradlew :gradle-plugin:validatePlugins` +5. `./gradlew check` + +Add or run a Gradle 8.14.4 TestKit scenario when compatibility-sensitive code +changes. Exercise configuration-cache reuse when plugin or task wiring changes. +Exercise platform-specific behavior on the relevant operating system; report an +unavailable Windows or Linux probe instead of treating another host as proof. + +## Report + +Summarize the Gradle contract changed, focused and full checks run, supported +consumer/platform coverage, and any remaining unverified behavior. diff --git a/.agents/skills/gradle-engineer/agents/openai.yaml b/.agents/skills/gradle-engineer/agents/openai.yaml new file mode 100644 index 0000000..c39a294 --- /dev/null +++ b/.agents/skills/gradle-engineer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gradle Engineer" + short_description: "Build robust Gradle plugin and build logic" + default_prompt: "Use $gradle-engineer to implement this Gradle plugin or build-logic change." diff --git a/.agents/skills/gradle-engineer/references/gradle-practices.md b/.agents/skills/gradle-engineer/references/gradle-practices.md new file mode 100644 index 0000000..0400a50 --- /dev/null +++ b/.agents/skills/gradle-engineer/references/gradle-practices.md @@ -0,0 +1,151 @@ +# Gradle practices + +Use these rules when changing build logic or the Gradle-facing implementation. +Verify current versions and paths in the checkout before relying on this map. + +## Contents + +- [Project ownership](#project-ownership) +- [Lazy configuration](#lazy-configuration) +- [Task state and caching](#task-state-and-caching) +- [Configuration cache](#configuration-cache) +- [Compatibility and Kotlin runtime](#compatibility-and-kotlin-runtime) +- [Kotlin DSL and build logic](#kotlin-dsl-and-build-logic) +- [Cross-platform behavior](#cross-platform-behavior) +- [Publication](#publication) +- [Test strategy](#test-strategy) +- [Repository exclusions](#repository-exclusions) + +## Project ownership + +- Keep repository composition and dependency repositories in + `settings.gradle.kts`. +- Keep shared coordinates and version application in the root build and + `version.gradle.kts`. +- Keep build-only constants and dependency coordinates in `buildSrc/`. +- Keep shared JVM compilation and test setup in the `jvm-module` convention + plugin. +- Keep plugin declaration, generated sources, functional-test source set, and + publication metadata in `gradle-plugin/build.gradle.kts`. +- Keep extension defaults and task registration in `EmbedCodePlugin`. +- Keep user-configurable values in `EmbedCodeExtension`. +- Keep execution behavior in typed task classes. Keep pure release, platform, + JSON, and checksum rules in focused Kotlin files. + +## Lazy configuration + +- Use `tasks.register`, `tasks.named`, provider mapping, and `flatMap`. +- Pass `TaskProvider` and provider-backed files through the graph instead of + calling `get()` during configuration. +- Use conventions for defaults and preserve later user configuration. +- Use `disallowChanges()` only for values owned entirely by the plugin after + wiring. +- Preserve task dependencies carried by `Provider` and + `ConfigurableFileCollection`. +- Avoid configuration-time downloads, process execution, directory creation, + and file reads whose result belongs to task execution. +- Keep collision handling lazy. Preserve underscore-prefixed fallback task + names when preferred names are occupied. + +## Task state and caching + +- Mark scalar and collection configuration with `@Input`. +- Mark source directories with `@InputDirectory` or `@InputFiles`; declare + `@Optional` only when absence is valid. +- Mark consumed artifacts with `@InputFile`. +- Mark reproducible task products with output annotations. +- Mark retained caches, downloaded assets, and integrity markers that should + not enter the build cache with `@LocalState`. +- Mark services, roots used only to validate other properties, working + directories, and secrets with `@Internal`. +- Choose `PathSensitivity.RELATIVE` for project content and + `PathSensitivity.NONE` when only file bytes matter. +- Disable build caching for in-place document mutation, external mutable + release state, or tasks whose primary purpose is validating local state. +- Override up-to-date behavior only with an explicit invariant. The install + task intentionally reruns to authenticate local installation state. + +## Configuration cache + +- Inject `ExecOperations` and other Gradle services into task types. +- Resolve extension values into task properties during configuration. +- Avoid retaining `Project`, extension objects, closures over project state, or + arbitrary mutable objects for task execution. +- Keep task actions dependent only on declared task properties, injected + services, and intentional internal/local state. +- Verify a second invocation reports configuration-cache reuse after changing + plugin application, registration, task properties, or process execution. + +## Compatibility and Kotlin runtime + +- Preserve the documented consumer floor of Gradle 8.14.4. +- Treat the wrapper as the development runtime, not as the minimum supported + consumer version. +- Keep Java bytecode at version 17 while supporting Gradle 8.14.4 and Gradle 9. +- Keep Kotlin language and API usage compatible with Kotlin 2.0.21 supplied by + Gradle 8.14.4. +- Keep Kotlin standard library dependencies `compileOnly` for plugin + publication. Supply explicit test runtime dependencies where unit tests need + Gradle and Kotlin classes. +- Avoid Kotlin or Gradle APIs introduced after the supported consumer floor + unless guarded by a compatible alternative and covered by versioned TestKit. +- Change the floor only together with README, build settings, compatibility + tests, and publication claims. + +## Kotlin DSL and build logic + +- Prefer typed Kotlin DSL accessors and typed task registration. +- Keep reusable module policy in convention plugins rather than copying blocks + between projects. +- Keep dependency coordinates centralized only when that improves ownership; + avoid abstraction for a single opaque use. +- Keep public plugin configuration in `build.gradle.kts`. Do not require users + to maintain an additional YAML file. +- Treat internally generated JSON configuration as task implementation state, + not as a public configuration surface. + +## Cross-platform behavior + +- Use Gradle file properties and Java path APIs instead of string concatenation + for filesystem paths. +- Preserve Windows `.exe` naming and Linux executable permissions. +- Keep path comparisons case and separator aware. Do not infer Windows safety + from a Linux-only test. +- Avoid shell-specific execution; pass executable and arguments separately. +- Preserve stable argument ordering and messages when tests or users rely on + them. +- Add platform conditions only for genuinely unsupported host behavior, not to + hide portable failures. + +## Publication + +- Keep `io.spine.embed-code` and its implementation class stable unless a + migration is explicitly requested. +- Keep display name, description, website, VCS URL, and tags consistent with + README terminology. +- Keep Maven artifact ID, POM name, description, license, developers, and SCM + coordinates aligned with the plugin declaration. +- Keep `LICENSE` in published JARs and keep sources and Javadoc artifacts. +- Run `validatePlugins` after changing task annotations, plugin declarations, + extension types, or publication configuration. + +## Test strategy + +- Use unit tests for pure version, checksum, JSON, and platform rules. +- Use TestKit functional tests for plugin application, DSL wiring, task + dependencies, task-name collisions, help output, configuration-cache reuse, + publication-facing behavior, and real Gradle failures. +- Keep a TestKit probe on Gradle 8.14.4 for the minimum consumer contract. +- Assert task outcomes, generated files, process arguments, and actionable + output rather than internal registration details alone. +- Run focused test classes while iterating, then unit tests, functional tests, + `validatePlugins`, and `check`. + +## Repository exclusions + +- Do not add configuration-repository filtering copied from a shared + organization build unless this repository adopts that policy explicitly. +- Do not require dependency group names to contain `spine`. +- Do not expose `installEmbedCode` in the standard grouped task list merely for + consistency. Its hidden helper status is intentional; `checkEmbedding` and + `embedCode` are the user-facing tasks. diff --git a/.agents/skills/kotlin-engineer/SKILL.md b/.agents/skills/kotlin-engineer/SKILL.md new file mode 100644 index 0000000..254b486 --- /dev/null +++ b/.agents/skills/kotlin-engineer/SKILL.md @@ -0,0 +1,67 @@ +--- +name: kotlin-engineer +description: > + Use for implementing, refactoring, explaining, or reviewing Kotlin source and + Kotlin DSL files (`.kt` and `.kts`) in this repository, especially public + plugin APIs, Gradle task types, extensions, and build logic. +--- + +# Kotlin engineer + +## Workflow + +1. Read [the project context](../../../PROJECT.md), the complete affected files, + their nearest tests, and the build configuration that defines their runtime. +2. Identify whether the change affects a public Gradle API, task execution, + build configuration, serialization, file handling, or network behavior. +3. Load [the Kotlin policy](references/kotlin-policy.md) for declaration, + compatibility, style, and KDoc patterns. +4. Preserve the existing architecture and behavior outside the requested scope. + Prefer the smallest idiomatic change that makes the contract explicit. +5. Add or update tests through + [test-engineer](../test-engineer/SKILL.md) for every behavioral change. +6. Verify with the narrowest relevant Gradle task, then run the broader check + required by the changed surface. +7. Report the affected contract, files changed, and verification performed. + +## Required policy + +- Follow the official Kotlin coding conventions and the established local + formatting, naming, and file organization. +- Preserve explicit API mode. Declare visibility and API types deliberately; + avoid accidental public surface expansion. +- Keep published plugin bytecode compatible with Java 17 even when the build + uses a newer JDK. Do not call runtime APIs introduced after Java 17. +- Preserve the Gradle-supplied Kotlin runtime model. Do not package a separate + Kotlin runtime or raise the configured language/API compatibility casually. +- Prefer immutable values and read-only interfaces. Limit mutation to the + narrowest implementation scope. +- Express invalid user configuration with clear validation at the boundary. + Keep internal failures distinct from actionable Gradle user errors. +- Avoid `!!`. Use it only for a proven invariant that cannot be represented + more safely, and explain that invariant next to the use. +- Keep comments useful: explain constraints, invariants, or surprising choices + rather than restating code. + +## Documentation contract + +- Write useful KDoc for every named type, regardless of visibility: classes, + interfaces, objects, companion objects, enum classes, annotation classes, + and type aliases. +- Write useful KDoc for every public constructor, property, and function. +- Document every public enum entry. +- Document private and internal non-type declarations only when their contract + or implementation is not obvious. +- Describe behavior, defaults, side effects, failure conditions, Gradle + lifecycle semantics, and compatibility constraints where applicable. +- Update KDoc when code changes invalidate an existing statement. + +## Companion skills + +- Use [gradle-engineer](../gradle-engineer/SKILL.md) for Provider API, task + modeling, configuration-cache, and plugin wiring decisions. +- Use [test-engineer](../test-engineer/SKILL.md) for unit and TestKit tests. +- Use [security-engineer](../security-engineer/SKILL.md) when changing downloads, + checksums, archives, paths, caches, processes, or credentials. +- Use [writer](../writer/SKILL.md) for README and other user-facing prose. +- Use [reviewer](../reviewer/SKILL.md) for an independent findings-first review. diff --git a/.agents/skills/kotlin-engineer/agents/openai.yaml b/.agents/skills/kotlin-engineer/agents/openai.yaml new file mode 100644 index 0000000..3631e8f --- /dev/null +++ b/.agents/skills/kotlin-engineer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Kotlin Engineer" + short_description: "Implement idiomatic, compatible Kotlin changes" + default_prompt: "Use $kotlin-engineer to implement this Kotlin change in the repository." diff --git a/.agents/skills/kotlin-engineer/references/kotlin-policy.md b/.agents/skills/kotlin-engineer/references/kotlin-policy.md new file mode 100644 index 0000000..c8df6a4 --- /dev/null +++ b/.agents/skills/kotlin-engineer/references/kotlin-policy.md @@ -0,0 +1,96 @@ +# Kotlin policy + +Use this reference while changing Kotlin source or Kotlin DSL in this +repository. Prefer nearby established code when it is more specific. + +## Compatibility boundary + +- Build with the configured toolchain, but emit Java 17-compatible bytecode for + the published plugin. +- Treat Java 17 as the maximum runtime API surface. A successful build on a + newer JDK does not prove consumer compatibility. +- Preserve the configured Kotlin language and API versions. The plugin runs + with Kotlin supplied by Gradle; consumers must not need to install Kotlin or + apply the Kotlin plugin. +- Keep the Kotlin standard library non-published as configured. Do not replace + `compileOnly` with `implementation` without an explicit compatibility reason. +- Use project-relative paths and Gradle abstractions instead of environment- + specific absolute paths. + +## Declarations and APIs + +- Add explicit visibility to public declarations and any declaration whose + intended boundary could be ambiguous. +- Add explicit return and property types to public APIs. Prefer an explicit + type internally when it communicates a Gradle or domain contract. +- Avoid exposing mutable collections or implementation-specific mutable state. +- Prefer Gradle managed properties for configurable values: + `Property`, `ListProperty`, `MapProperty`, + `DirectoryProperty`, and `RegularFileProperty`. +- Accept `Provider` when callers should retain lazy evaluation. Do not + realize a provider merely to convert it into an eager value. +- Keep task and extension APIs declarative. Put execution work in task actions, + not property accessors or plugin application. +- Keep user-facing validation close to the configuration or execution boundary. + Use Gradle exception types when the failure is actionable to a build user. + +## Kotlin style + +- Follow the official Kotlin conventions and match the closest file. +- Prefer `val`, expression bodies when they remain readable, exhaustive `when`, + safe calls, Elvis expressions, `require`, `check`, and `requireNotNull`. +- Use a data class only for a value with value-based equality. Do not convert a + service, task, plugin, or stateful object into a data class. +- Keep functions focused. Extract a helper when it gives a rule or invariant a + name; avoid helpers that only obscure one straightforward expression. +- Prefer named arguments when adjacent arguments have the same type or the + call's meaning is otherwise unclear. +- Keep Java interop intentional. Do not add `@JvmStatic`, `@JvmOverloads`, or + other bridge annotations unless a verified consumer needs them. +- Preserve local constant naming instead of normalizing unrelated declarations. +- Do not introduce unrelated coroutine, Flow, multiplatform, Android, + Protobuf, or framework conventions. + +## Nullability and failures + +- Model optional data as nullable only when absence has one clear meaning. +- Avoid `!!`; prefer validation or a non-null type. If an external API makes an + invariant unrepresentable, document the invariant next to the assertion. +- Catch the narrowest useful exception. Preserve the original cause when + wrapping an internal failure in an actionable Gradle error. +- Do not expose credentials, tokens, internal paths, or low-level exception + details in user-facing messages. + +## KDoc + +Document every named type and every public API. Named types include regular, +data, value, sealed, enum, and annotation classes; interfaces; objects; +companion objects; and type aliases. Document every public enum entry. Make the +documentation useful to a plugin consumer or future maintainer. + +Include the applicable parts of this contract: + +- what the declaration represents or performs; +- defaults and whether configuration is required; +- input, output, caching, offline, or network behavior; +- accepted formats and validation rules; +- observable side effects and failures; +- Java 17, Gradle, or Kotlin-runtime compatibility constraints. + +Avoid repeating the declaration in prose: + +```kotlin +/** + * Selects the exact Embed Code release tag to install. + * + * Rejects blank or unsafe tags before resolving an installation path. + */ +public abstract val version: Property +``` + +Use KDoc links only for symbols visible to the documented source set. Do not +link published API documentation to `.agents/`, `buildSrc`, issues, branches, +or transient implementation notes. + +For private and internal non-type declarations, add KDoc only when it explains +a non-obvious contract. Prefer a short inline comment for a local invariant. diff --git a/.agents/skills/reviewer/SKILL.md b/.agents/skills/reviewer/SKILL.md new file mode 100644 index 0000000..49b7b5a --- /dev/null +++ b/.agents/skills/reviewer/SKILL.md @@ -0,0 +1,81 @@ +--- +name: reviewer +description: >- + Use when reviewing a diff, pull request, commit, or proposed repository change; + perform a strictly read-only, evidence-based review of the complete changed + files and report prioritized findings followed by a verdict. +--- + +# Repository reviewer + +Remain read-only with respect to source, configuration, documentation, Git state, and +remote state. Running tests and read-only diagnostics is allowed; normal ignored build +and test outputs are their only permitted filesystem side effect. Never edit files, +apply patches, stage, commit, push, publish, reply to review comments, or resolve review +threads. Route implementation to a companion skill. + +Read the [project context](../../../PROJECT.md) before reviewing. + +## Establish the review scope + +1. Resolve the requested base, head, commit, or working-tree diff exactly. +2. Inspect repository status without cleaning, staging, switching branches, or fetching. +3. Read the complete diff and every changed file in full. +4. Identify the acceptance source: request, issue, review comment, contract, test, or docs. +5. Trace affected callers, task wiring, data flows, tests, and documentation far enough to + establish the actual impact. +6. Inspect resolved or outdated review threads when the request includes prior comments. + +## Gather evidence + +- Reproduce a reported defect when practical before treating it as open. +- Run focused tests and read-only diagnostics when they materially improve confidence. +- Distinguish observed facts from inference. +- Confirm reachability and user impact before reporting a correctness or security issue. +- Omit speculative findings. Do not promote an incomplete probe or a theoretical concern + into a defect. +- Reconcile historical findings against the current checkout before repeating them. +- Preserve unrelated local changes and untracked files. + +## Compose companion skills + +Load only the guidance relevant to the changed files. Apply it during review instead of +copying its policy into this skill. + +- Use [Kotlin engineer](../kotlin-engineer/SKILL.md) for Kotlin implementation and API + conventions. +- Use [Gradle engineer](../gradle-engineer/SKILL.md) for build logic, task modeling, + configuration cache, compatibility, and publication configuration. +- Use [test engineer](../test-engineer/SKILL.md) for test design and verification depth. +- Use [security engineer](../security-engineer/SKILL.md) for executable, checksum, cache, + path, archive, token, network, and offline trust boundaries. +- Use [writer](../writer/SKILL.md) and the + [writing style](../../guidelines/writing-style.md) for prose, KDoc, examples, errors, + comments, and agent instructions. + +## Report only actionable findings + +Lead with findings. For each finding: + +- Give a concise, specific title. +- Cite the tightest file and line range. +- State the evidence, affected scenario, and concrete impact. +- Explain the smallest safe correction without writing or applying it. +- Avoid praise, summaries of correct code, and style preferences unsupported by project + policy. + +Return these sections in order: + +- **Must fix** — confirmed defects that can break behavior, compatibility, security, + published API, builds, or materially false user guidance. +- **Should fix** — confirmed maintainability, testing, diagnostics, or documentation gaps + with meaningful future cost or user confusion. +- **Nits** — optional, non-blocking local improvements. Keep this section short. + +Write `None.` under an empty section. + +End with one verdict: + +- `REQUEST CHANGES` when **Must fix** is non-empty. +- `APPROVE WITH CHANGES` when only **Should fix** is non-empty. +- `APPROVE` when only **Nits** or no findings remain. diff --git a/.agents/skills/reviewer/agents/openai.yaml b/.agents/skills/reviewer/agents/openai.yaml new file mode 100644 index 0000000..ec28e0a --- /dev/null +++ b/.agents/skills/reviewer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Reviewer" + short_description: "Review changes without modifying the repository" + default_prompt: "Use $reviewer to review these changes and report actionable findings." diff --git a/.agents/skills/security-engineer/SKILL.md b/.agents/skills/security-engineer/SKILL.md new file mode 100644 index 0000000..413a2e5 --- /dev/null +++ b/.agents/skills/security-engineer/SKILL.md @@ -0,0 +1,93 @@ +--- +name: security-engineer +description: >- + Apply repository-specific security engineering policy when changing or + reviewing release URLs or tags, SHA-256 trust, executable caches, offline + reuse, temporary files, archive extraction, redirects, GitHub tokens, path + containment, or symbolic-link and Windows-junction handling. +--- + +# Security engineering + +Protect the boundary between remote release data and an executable run by a +consumer build. Require evidence for security claims and preserve fail-closed +behavior when authenticity or filesystem ownership cannot be established. + +## Start + +1. Read [`PROJECT.md`](../../../PROJECT.md) and the affected release, checksum, + installation, platform, and functional-test code. +2. Identify the attacker-controlled value, trusted value, filesystem boundary, + online/offline state, and final executable before proposing a change. +3. Reproduce a reported weakness with a concrete regression probe. Do not + present a hypothetical path as exploitable when the probe is unavailable or + blocked by an existing guard. +4. Read [Trust boundaries](references/trust-boundaries.md) for download, cache, + redirect, archive, token, or path work. + +## Coordinate responsibilities + +- Apply [`gradle-engineer`](../gradle-engineer/SKILL.md) to task properties, + local state, configuration cache, and TestKit wiring. +- Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin and Java + filesystem or network API usage. +- Apply [`test-engineer`](../test-engineer/SKILL.md) to regression structure and + deterministic fixtures. +- Apply [`reviewer`](../reviewer/SKILL.md) to severity, evidence, and review + scope. +- Apply [`writer`](../writer/SKILL.md) when security behavior changes public + configuration, diagnostics, or release documentation. + +## Preserve trust invariants + +- Accept exact validated release tags. Keep rolling or path-shaped identifiers + out of release URLs and cache paths. +- Authenticate every downloaded or retained asset with a trusted SHA-256 before + extraction or execution. +- Bind reusable state to release base URL, exact tag, and platform asset. Treat + a source-identity mismatch as unverified state. +- Rehash the installed executable before reuse. Never treat file existence, + executable permission, or an old marker alone as authenticity. +- Fail closed offline unless a locally verified executable is intact or a + cached asset can be authenticated with an already trusted digest. +- Keep temporary files unpredictable, contained, cleaned in `finally`, and + promoted only after verification. +- Extract only the expected executable into a controlled staging file. Never + resolve arbitrary archive entry paths into the installation tree. +- Validate normalized path containment and reject symbolic links, junctions, + redirecting entries, and non-directory components from the installation root + to every destination. +- Keep tokens opt-in, internal, and absent from logs, errors, cache metadata, + task inputs, and requests to untrusted hosts. +- Keep authenticated metadata requests from following redirects. Verify asset + bytes after any allowed unauthenticated download redirect. + +## Build regression evidence + +- Prove digest mismatch, stale source identity, modified executable, and + unauthenticated offline cache behavior. +- Probe `..` and encoded path input, absolute destination overrides, symlinked + files or directories, and Windows junctions where the host supports them. +- Probe metadata redirects separately from release-asset redirects, including + whether authorization could cross a host boundary. +- Probe crafted archives against the actual extraction strategy and assert no + write outside controlled staging and installation paths. +- Assert failure leaves no trusted executable or valid-looking integrity + metadata behind. + +## Verify + +Run the smallest relevant unit or TestKit test first. Then run: + +1. `./gradlew :gradle-plugin:test` +2. `./gradlew :gradle-plugin:functionalTest` +3. `./gradlew check` + +Run filesystem probes on the operating system whose semantics they claim to +cover. Report an unrun Windows junction or Linux permission probe explicitly. + +## Report + +For implementation, state the trust invariant, regression probe, and verification +result. For review, report only reproducible vulnerabilities or concrete +defense-in-depth gaps; omit speculative exploitability and identify unrun probes. diff --git a/.agents/skills/security-engineer/agents/openai.yaml b/.agents/skills/security-engineer/agents/openai.yaml new file mode 100644 index 0000000..91869e8 --- /dev/null +++ b/.agents/skills/security-engineer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Security Engineer" + short_description: "Protect download, cache, and filesystem trust" + default_prompt: "Use $security-engineer to implement or assess this security-sensitive change." diff --git a/.agents/skills/security-engineer/references/trust-boundaries.md b/.agents/skills/security-engineer/references/trust-boundaries.md new file mode 100644 index 0000000..cbeec1f --- /dev/null +++ b/.agents/skills/security-engineer/references/trust-boundaries.md @@ -0,0 +1,204 @@ +# Trust boundaries + +Use this reference to reason about release installation and executable reuse. +Re-read current implementation and tests before assuming the flow is unchanged. + +## Contents + +- [Installation flow](#installation-flow) +- [Trust inputs](#trust-inputs) +- [Release identity](#release-identity) +- [SHA-256 trust](#sha-256-trust) +- [Cache and offline reuse](#cache-and-offline-reuse) +- [Temporary files and promotion](#temporary-files-and-promotion) +- [Archive extraction](#archive-extraction) +- [Path containment](#path-containment) +- [Tokens and redirects](#tokens-and-redirects) +- [Regression probes](#regression-probes) +- [Review standard](#review-standard) + +## Installation flow + +Preserve this order: + +1. Validate the exact release tag and select the platform asset. +2. Normalize the release base URL and derive source identity from the base URL, + exact tag, and asset name. +3. Resolve a trusted SHA-256 from explicit configuration or supported GitHub + release metadata. +4. Download into an unpredictable task-temporary file, or stage a retained + cached asset without following links. +5. Hash staged bytes and compare them with the trusted digest. +6. Extract or copy into a separate staged executable. +7. Mark the staged file executable and calculate its digest. +8. Move it into a checked installation path. +9. Write asset digest, executable digest, and source identity through checked + temporary files. +10. Rehash and compare the installed executable before every reuse. + +Do not execute, expose as valid, or persist trusted-looking metadata before the +corresponding verification step succeeds. + +## Trust inputs + +Treat these values as untrusted until validated: + +- user-configured release tag; +- custom release base URL; +- operating-system and architecture strings when task inputs are overridden; +- remote response status, headers, metadata JSON, and asset bytes; +- archive entry names and content; +- retained cache files and marker files; +- public task output and local-state paths when consumers reconfigure them; +- every existing filesystem component below the installation root. + +Treat these values as trust anchors only within their intended scope: + +- an explicitly configured normalized SHA-256; +- the digest for the named asset returned by the supported GitHub Releases API; +- a stored executable digest only when source identity and asset metadata still + match and the marker path is safe. + +## Release identity + +- Keep release tags exact and case-sensitive. +- Reject empty, rolling `latest`, slash-containing, traversal-shaped, and + unsupported-character tags before URL or path construction. +- Use a filesystem-portable hash of the tag for cache directories; do not use + the raw tag as a directory name. +- Bind cache state to release base URL, exact tag, and asset name. +- Invalidate reuse when any identity component changes, even if the destination + executable already exists. +- Preserve explicit checksum configuration as authoritative for custom mirrors. + +## SHA-256 trust + +- Normalize accepted SHA-256 text to exactly 64 lowercase hexadecimal digits. +- Prefer an explicitly configured digest when present. +- Resolve automatic digests only for the supported HTTPS `github.com` release + URL shape and the exact named asset from `api.github.com`. +- Require explicit SHA-256 configuration for unsupported custom release + sources. +- Hash staged bytes with no-follow file access before extraction. +- Hash the prepared executable separately; asset and executable digests serve + different reuse checks. +- Reject mismatch with expected and observed values that aid diagnosis but + contain no secret. + +## Cache and offline reuse + +- Treat cached assets and all metadata as attacker-modifiable local state. +- Reuse an installed executable only after source identity matches and its + current bytes match the stored executable digest. +- Reauthenticate a cached asset before rebuilding a missing or modified + executable. +- In offline mode, accept an intact locally verified executable. +- In offline mode, rebuild from a cached asset only when a trusted configured + digest authenticates it. +- Fail closed when neither path can establish authenticity. +- Never convert offline mode into “trust whatever is already in the build + directory.” + +## Temporary files and promotion + +- Create download and preparation files with unpredictable names under the + task temporary directory. +- Create metadata replacement files with unpredictable names beside their + checked destinations. +- Open retained sources with `NOFOLLOW_LINKS`. +- Verify staged data before moving it into the installation tree. +- Prefer atomic replacement and retain a safe fallback when atomic moves are + unsupported. +- Recheck destination components before and after promotion. +- Delete temporary files in `finally`, including failure paths. +- Write integrity metadata only after the executable has been prepared and + promoted successfully. + +## Archive extraction + +- Treat every ZIP entry name as untrusted. +- Search for the expected platform executable and stream its bytes into a + caller-selected staging file. +- Never resolve an entry path against the installation directory or extract + arbitrary archive structure. +- Reject archives that lack the expected executable. +- Add explicit handling and probes before accepting archives with ambiguous + duplicate basenames, unexpected link metadata, or resource-exhaustion risk. +- Do not report zip-slip from a `../` entry alone when the implementation never + uses the entry path as an output path; prove an out-of-bound write. + +## Path containment + +- Normalize the installation root and every configured destination. +- Require destinations to be strict descendants of the installation root. +- Do not rely on lexical `startsWith` alone. +- Reject a symbolic-link or redirecting installation root. +- Walk every existing path component without following links. +- Reject symbolic links, Windows junctions, other redirecting filesystem + entries, and non-directory intermediate components. +- Create missing directories one component at a time and verify their real + paths remain below the real installation root. +- Apply the same checks to executable, cached asset, checksums, source identity, + and metadata temporary destinations. +- Recheck after moves to reduce time-of-check/time-of-use exposure. +- Test Windows junction semantics on Windows; Unix symlink coverage is not a + substitute. + +## Tokens and redirects + +- Read a GitHub token only from explicit plugin configuration. +- Mark it internal and keep it out of task fingerprints, cache identity, + persisted metadata, logs, exception messages, and generated configuration. +- Attach authorization only to the exact trusted GitHub API host used for + checksum metadata. +- Disable redirects for authenticated metadata requests. +- If redirect handling changes, validate every hop and never forward + authorization across a host boundary. +- Allow unauthenticated asset redirects only while final downloaded bytes remain + subject to trusted SHA-256 verification before use. +- Keep actionable HTTP status and source context without echoing credentials. + +## Regression probes + +- **Tag:** Try `../../escaped`, slash, encoded slash, empty, and `latest`. Reject + them before URL or path use. +- **Digest:** Serve bytes that differ from the trusted SHA-256. Fail without + installing anything trusted. +- **Identity:** Change the base URL, tag case, or platform asset with a cache + present. Reject stale reuse or reauthenticate it. +- **Executable:** Modify installed bytes after verification. Reject direct reuse. +- **Offline:** Remove metadata or provide a modified cache without a configured + digest. Fail closed. +- **Temporary files:** Pre-create likely names and force a mid-install failure. + Avoid collisions and clean staging files. +- **Archive:** Include traversal entries and the expected executable. Write only + the controlled staged file. +- **Output path:** Reconfigure executable or metadata outside the root. Reject + it before writing. +- **Symbolic link:** Link an intermediate directory, asset, marker, or + destination. Reject it without touching the target. +- **Junction:** Redirect the installation root or a child on Windows. Reject it + without touching the target. +- **Metadata redirect:** Return a `3xx` response from the authenticated API + endpoint. Do not follow it or forward the token. +- **Asset redirect:** Redirect to bytes with a bad digest. Follow only as + allowed, then reject the bytes. +- **Token:** Run failures with verbose logging. Never expose the token text. + +Assert both the failure message and filesystem aftermath. A failing build alone +does not prove containment or cleanup. + +## Review standard + +- Reproduce against the current checkout and the actual public configuration + path. +- Identify the trust boundary, attacker capability, reachable operation, and + concrete impact. +- Distinguish a vulnerability from hardening, misleading diagnostics, or an + unreachable edge. +- Omit exploitability claims when the required host-specific or network probe + cannot be run. +- Prefer a minimal regression fixture that fails before the fix and passes + afterward. +- Preserve unrelated cache, release, and compatibility behavior while fixing a + confirmed issue. diff --git a/.agents/skills/test-engineer/SKILL.md b/.agents/skills/test-engineer/SKILL.md new file mode 100644 index 0000000..8ded58b --- /dev/null +++ b/.agents/skills/test-engineer/SKILL.md @@ -0,0 +1,81 @@ +--- +name: test-engineer +description: > + Use for adding, changing, diagnosing, or reviewing Kotlin tests in this + repository, including JUnit Jupiter unit specs, Gradle TestKit functional + specs, bug reproductions, and security or compatibility regressions. +--- + +# Test engineer + +## Workflow + +1. Read [the project context](../../../PROJECT.md), the production code in full, + and the closest specs in the same source set. +2. Reproduce a reported defect before changing production code whenever a + deterministic reproduction is possible. +3. Classify the behavior as a focused unit contract or a consuming-build + contract. Use [the test patterns](references/test-patterns.md) to choose the + source set and fixture. +4. Add the smallest test that proves the requested behavior and its important + failure boundary. Keep unrelated coverage work out of scope. +5. Run the focused spec or source-set task first. Run the full repository check + after the focused test passes. +6. Report the behavior covered, the test location, and every verification + command with its result. + +## Test style + +- Use JUnit Jupiter for test structure and assertions. +- Do not add Kotest, a mocking framework, or another assertion dependency. +- Name a suite for subject `X` as `XSpec`; make Kotlin suites `internal`. +- Annotate every suite with `@DisplayName` describing what the subject should + do. +- Name test functions as backticked behavioral phrases that read as + specifications. +- Prefer hand-written stubs, fixed inputs, and observable outputs over mocks or + implementation-detail verification. +- Keep setup close to the behavior unless a helper removes substantial, + repeated noise. + +## Test boundary + +- Put pure functions, validation, parsing, value transformations, and isolated + class behavior under `src/test/kotlin`. +- Put plugin application, task registration and execution, consuming-build + configuration, configuration-cache behavior, Gradle compatibility, and + process/file integration under `src/functionalTest/kotlin` with TestKit. +- Assert outcomes visible to a consumer: task outcomes, files, messages, + generated configuration, requests, and exit behavior. +- Avoid asserting Gradle internals or private call order. + +## Determinism and isolation + +- Use temporary directories and fixed fixture contents. +- Replace release services with local files or a loopback fake HTTP server. +- Never depend on live GitHub, release assets, DNS, credentials, or public + network availability. +- Bind fake servers to loopback on an ephemeral port and stop them reliably. +- Use fixed clocks, versions, digests, and environment values where relevant. +- Gate genuinely platform-specific behavior with JUnit conditions; do not hide + portable failures behind an OS condition. + +## Verification + +- Run a focused unit spec with `:gradle-plugin:test --tests `. +- Run a focused functional spec with + `:gradle-plugin:functionalTest --tests `. +- Run `./gradlew check` after focused verification. +- Run the broader build or publication checks required by + [gradle-engineer](../gradle-engineer/SKILL.md) when build logic, plugin + metadata, compatibility, or publication behavior changes. + +## Companion skills + +- Use [kotlin-engineer](../kotlin-engineer/SKILL.md) for Kotlin and KDoc style + inside tests and production code. +- Use [gradle-engineer](../gradle-engineer/SKILL.md) for Gradle task and plugin + semantics. +- Use [security-engineer](../security-engineer/SKILL.md) to identify adversarial + path, archive, download, checksum, cache, process, and credential cases. +- Use [reviewer](../reviewer/SKILL.md) for an independent findings-first review. diff --git a/.agents/skills/test-engineer/agents/openai.yaml b/.agents/skills/test-engineer/agents/openai.yaml new file mode 100644 index 0000000..1f7378d --- /dev/null +++ b/.agents/skills/test-engineer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Test Engineer" + short_description: "Write focused JUnit and TestKit coverage" + default_prompt: "Use $test-engineer to add focused regression coverage for this change." diff --git a/.agents/skills/test-engineer/references/test-patterns.md b/.agents/skills/test-engineer/references/test-patterns.md new file mode 100644 index 0000000..034704a --- /dev/null +++ b/.agents/skills/test-engineer/references/test-patterns.md @@ -0,0 +1,133 @@ +# Test patterns + +Match the nearest existing spec before introducing a new helper or layout. + +## Unit spec + +Use JUnit Jupiter structure and assertions: + +```kotlin +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`ReleaseTag` should") +internal class ReleaseTagSpec { + + @Test + fun `reject a blank value`() { + val error = assertThrows(IllegalArgumentException::class.java) { + validateReleaseTag(" ") + } + + assertEquals("Release tag must not be blank.", error.message) + } +} +``` + +Use several assertions when they describe one behavior. Split a test when a +failure would leave the broken contract unclear. + +## Unit or functional + +| Behavior | Source set | +|---|---| +| Parse, normalize, hash, validate, or map a value | `src/test/kotlin` | +| Select a platform name without executing Gradle | `src/test/kotlin` | +| Apply the plugin or configure its extension | `src/functionalTest/kotlin` | +| Register, wire, execute, or inspect a task | `src/functionalTest/kotlin` | +| Verify configuration-cache reuse | `src/functionalTest/kotlin` | +| Verify behavior across Gradle versions | `src/functionalTest/kotlin` | +| Launch the fake Embed Code executable | `src/functionalTest/kotlin` | + +Do not use TestKit when a direct unit call proves the contract. Do not replace +a consuming-build test with a unit test when Gradle wiring is the behavior. + +## TestKit project + +Create the consuming project under a JUnit `@TempDir`. Write only the settings, +build script, source, documentation, and fake assets required by the case. + +Create runners with the plugin-under-test classpath: + +```kotlin +private fun runner(vararg arguments: String): GradleRunner = + GradleRunner.create() + .withProjectDir(projectDirectory.toFile()) + .withArguments( + *arguments, + "--configuration-cache", + "--stacktrace", + ) + .withPluginClasspath() +``` + +Match the repository's established runner helper when it differs. Keep +configuration cache enabled by default so new behavior does not silently break +the plugin's compatibility claim. Disable it only in a test that explicitly +proves a non-configuration-cache path, and state why. + +Assert task outcomes and consumer-visible files or messages: + +```kotlin +val result = runner(":checkEmbedding").build() + +assertEquals(TaskOutcome.SUCCESS, result.task(":checkEmbedding")?.outcome) +assertEquals("check", Files.readString(projectDirectory.resolve("mode.txt")).trim()) +``` + +Use `buildAndFail()` only when failure is the expected contract. Assert the +actionable message rather than a complete stack trace. + +## Fake releases and HTTP + +- Build fake release assets inside the temporary project. +- Use fixed payloads and calculate expected SHA-256 values locally. +- Use `HttpServer` on `127.0.0.1` with port `0` when request behavior matters. +- Record request paths, headers, or counts in thread-safe test state. +- Return only the status and body needed by the test. +- Stop the server in `finally`, `@AfterEach`, or another guaranteed cleanup + path. +- Use a local file URI instead of HTTP when transport behavior is irrelevant. + +Never call GitHub or another public endpoint from a test. A live service makes +rate limits, credentials, network failures, mutable releases, and remote state +part of the result. + +## Filesystem and process fixtures + +- Resolve every fixture below the temporary directory. +- Use fixed file contents and explicit UTF-8 where an API requires a charset. +- Create a minimal fake executable that records arguments and produces a fixed + exit result. +- Test both the expected path and important containment or replacement + failures. +- Avoid sleeps. Wait on an observable process, file, or request condition with + a bounded timeout when synchronization is necessary. +- Apply JUnit OS conditions only to behavior that truly depends on executable + permissions or platform process semantics. + +## Compatibility cases + +Use `withGradleVersion(...)` only for versions supported by the plugin's stated +compatibility range. Keep the ordinary runner on the wrapper version. Cover a +second Gradle version when the changed API or behavior varies across Gradle +releases. + +Keep Java 17 consumer compatibility distinct from the JDK used to build the +project. A test passing on the build JDK alone does not prove that the published +plugin runs on Java 17. + +## Verification commands + +Prefer this order: + +```bash +./gradlew :gradle-plugin:test --tests io.spine.embedcode.gradle.SubjectSpec +./gradlew :gradle-plugin:functionalTest --tests io.spine.embedcode.gradle.PluginSpec +./gradlew check +``` + +Run only the applicable focused command first. Run the full check before +claiming completion. diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md new file mode 100644 index 0000000..79e78d3 --- /dev/null +++ b/.agents/skills/writer/SKILL.md @@ -0,0 +1,87 @@ +--- +name: writer +description: >- + Use when creating or revising README.md, PROJECT.md, AGENTS.md, skill routes, + Markdown, Kotlin KDoc, Gradle task descriptions, public errors, or explanatory + comments; write verified project documentation in the Spine style. +--- + +# Documentation writer + +Read the [project context](../../../PROJECT.md) and the +[writing style](../../guidelines/writing-style.md) before editing. + +## Establish the audience and owner + +- Identify the reader, the problem to solve, and the outcome that makes the text useful. +- Prefer updating the document that already owns the topic. +- Keep `README.md` focused on the user problem, setup, Kotlin DSL configuration, + tasks, and short contributor commands. +- Keep `PROJECT.md` focused on stable project context and architecture, + compatibility, and source-of-truth pointers. +- Keep `AGENTS.md`, skill descriptions, and other agent routes focused on dispatch and + repository-wide policy. Link to the owning skill instead of duplicating its guidance. +- Keep KDoc with its declaration, task descriptions with their task registrations, and + public error messages at the boundary that reports the failure. + +## Verify before writing + +1. Read the complete target file and the related source, tests, build logic, and routes. +2. Treat current repository behavior as authoritative. +3. Verify every path, command, version, default, and platform, along with every task + name, property, and failure claim, against current evidence. +4. Run the narrowest practical command when prose claims executable behavior or output. +5. Mark any remaining unverified claim explicitly; do not turn an assumption into fact. + +## Write user-facing documentation + +- Explain the user's problem before introducing implementation details. +- Use Kotlin DSL in Gradle examples. Do not add Groovy DSL examples unless requested. +- Keep examples small, copyable, and consistent with the supported Gradle and Java ranges. +- Describe exact release-tag, checksum, cache, token, and offline behavior without + weakening or overstating the trust guarantees. +- Prefer direct instructions and observable outcomes over promotional language. +- Keep one source of truth for detailed guidance and link to it from shorter entry points. + +## Write Kotlin documentation + +- Give every named type useful KDoc, regardless of visibility. Cover classes, objects, + companion objects, interfaces, enums, annotation classes, type aliases, and sealed types. +- Give every public API useful KDoc, including public constructors, functions, + properties, constants, and nested declarations. +- Document private members only when their intent, invariant, lifecycle, side effect, + failure mode, or trust boundary is not obvious from the code. +- Start with a short behavioral summary. Explain contracts and reasons, not syntax. +- Use KDoc links such as `[EmbedCodeTask]` for code symbols. +- Use `@param`, `@return`, and `@throws` only when they add information + that the signature and summary do not provide. +- Preserve useful existing constraints and explanatory comments when restructuring prose. +- Reject boilerplate that restates a declaration, assignment, or obvious return value. + +## Write operational text + +- Make task descriptions concise and action-oriented. +- Make public errors state what failed, identify the relevant user-facing value or + property, and provide a recovery path when one exists. +- Keep credentials and other sensitive values out of errors, examples, and logs. +- Use inline comments to explain why a constraint exists. Do not narrate the next statement. +- Preserve exact machine text, command output, code, generated content, and license text. + +## Compose companion skills + +- Consult [Kotlin engineer](../kotlin-engineer/SKILL.md) for declaration and API design. +- Consult [Gradle engineer](../gradle-engineer/SKILL.md) for Gradle DSL, + task behavior, and compatibility claims. +- Consult [test engineer](../test-engineer/SKILL.md) for test examples and verification. +- Consult [security engineer](../security-engineer/SKILL.md) for trust-boundary claims. +- Route read-only feedback to [reviewer](../reviewer/SKILL.md). + +## Validate the edit + +- Check heading hierarchy, links, referenced paths, terminology, code fences, and wrapping. +- Check that changed Markdown and KDoc follow the shared writing style. +- Leave no orphans: reflow or rewrite a paragraph, list item, table cell, or KDoc block + whose final source line contains one word or an unusually short fragment. +- Run `git diff --check`. +- Run focused Gradle verification when a changed example or comment depends on behavior. +- Report the evidence used, commands run, and any claim that remains unverified. diff --git a/.agents/skills/writer/agents/openai.yaml b/.agents/skills/writer/agents/openai.yaml new file mode 100644 index 0000000..3703617 --- /dev/null +++ b/.agents/skills/writer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Writer" + short_description: "Write clear project docs and Kotlin KDoc" + default_prompt: "Use $writer to write or update this repository documentation." 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/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b81f2d2 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,35 @@ +# GitHub Copilot instructions + +This is the Copilot-specific route for `embed-code-gradle-plugin`. Keep detailed policy in its +owning repository document instead of duplicating it here. + +## Read first + +- Read [`AGENTS.md`](../AGENTS.md) for repository-wide operating policy. +- Read [`PROJECT.md`](../PROJECT.md) for the project map, runtime flow, compatibility + constraints, test strategy, and trust boundaries. +- Use matching repository skills under [`.agents/skills/`](../.agents/skills/). + +## Skill routing + +- Use `kotlin-engineer` for Kotlin language, API, interoperability, and KDoc decisions. +- Use `gradle-engineer` for build scripts, plugin wiring, tasks, lazy configuration, + configuration cache, compatibility, and publication metadata. +- Use `test-engineer` for JUnit Jupiter unit tests and Gradle TestKit functional tests. +- Use `writer` for Markdown, KDoc, comments, examples, task descriptions, and public errors. +- Use `reviewer` only for non-writing, findings-first review and verification. +- Use `security-engineer` for release assets, checksums, downloads, caches, archives, tokens, + offline behavior, containment, and symbolic-link safety. + +Use multiple skills when a change crosses boundaries. A Kotlin Gradle task generally needs +`kotlin-engineer` and `gradle-engineer`; download or cache logic also needs +`security-engineer`. + +## Repository invariants + +- Preserve Kotlin DSL, JUnit Jupiter, Gradle TestKit, configuration-cache compatibility, and + the Gradle-supplied Kotlin runtime. +- Keep tests deterministic and independent of live external services. +- Verify documentation and compatibility claims against current source and build files. +- Do not write, commit, push, publish, reply to review threads, or resolve them while acting as + `reviewer`. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f5116ba --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,63 @@ +# Agent instructions + +These instructions apply to the whole repository. Keep this file focused on +repository-wide operating policy; keep task-specific procedures in the skills under +`.agents/skills/`. + +## Orientation + +- Read [PROJECT.md](PROJECT.md) before making a non-trivial change. +- Use every discovered skill whose description matches the task. Kotlin code inside Gradle + plugins normally needs both `kotlin-engineer` and `gradle-engineer`. +- Treat each skill's frontmatter description as the routing source of truth. +- Read the affected implementation, nearby tests, and relevant build configuration before + editing. + +## Operating policy + +- Preserve unrelated local changes and untracked files. +- Keep changes narrowly scoped. Do not combine opportunistic cleanup with the requested work. +- Ask a question only when an unresolved decision would materially change the result. +- Do not commit, push, tag, merge, rebase, cherry-pick, reply to review threads, or resolve + threads unless the user's current request explicitly asks for that action. +- Do not publish plugins, change release versions, or use credentials unless explicitly asked. +- Do not add telemetry, analytics, hidden network access, or automatic dependency updates. + +## Project defaults + +- Use Kotlin and Gradle Kotlin DSL. Do not add Groovy build scripts. +- Use the Gradle wrapper and preserve the compatibility targets declared by the build. +- Preserve Gradle's lazy configuration model, configuration-cache compatibility, and the + Gradle-supplied Kotlin runtime. +- Preserve JUnit Jupiter and Gradle TestKit. Do not introduce Kotest or a mocking framework + without an explicit dependency and style decision. +- Use local fixtures or local HTTP servers in tests; do not make the test suite depend on live + external services. +- Apply `.agents/skills/security-engineer/SKILL.md` to download, checksum, cache, archive, + token, path-containment, symlink, and offline-mode changes. + +## Documentation + +- Follow `.agents/guidelines/writing-style.md` for Markdown, KDoc, comments, and user-facing + text. +- Give every named Kotlin type useful KDoc, regardless of visibility. +- Give every public API useful KDoc. Document private members only when they carry a + non-obvious contract, invariant, side effect, or failure mode. +- Verify documentation claims against current code, tests, build files, or command output. + +## Verification + +- Start with the narrowest Gradle task or test that proves the change. +- Run `./gradlew check` before handing off a completed code or test change unless the task is + documentation-only or a narrower check is explicitly sufficient. +- Run `git diff --check` after editing. +- Report every command actually run and distinguish failures caused by the change from + environment or unrelated failures. + +## Agent entry points + +- Codex reads this `AGENTS.md` and discovers repository skills under `.agents/skills/`. +- Claude reads [CLAUDE.md](CLAUDE.md), which routes back to this file. +- GitHub Copilot reads + [`.github/copilot-instructions.md`](.github/copilot-instructions.md), which routes back to + this file and the matching skills. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..21eb8ad --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,11 @@ +@AGENTS.md + +# Claude-specific routing + +- Read `PROJECT.md` for the project map, runtime flow, compatibility constraints, test + strategy, and trust boundaries. +- Load every matching skill through `.claude/skills/`, which routes to the canonical + `.agents/skills/` directory; keep skill bodies agent-neutral. +- Treat `AGENTS.md` as the repository-wide policy owner. Do not duplicate its rules here. +- Do not commit, push, publish, or mutate review-thread state unless the user's current request + explicitly asks for that action. diff --git a/PROJECT.md b/PROJECT.md new file mode 100644 index 0000000..5c15276 --- /dev/null +++ b/PROJECT.md @@ -0,0 +1,118 @@ +# Project + +This document gives agents and contributors the project map, runtime flow, compatibility +constraints, test strategy, and trust boundaries. For repository-wide operating policy, read +[AGENTS.md](AGENTS.md). + +## Overview + +`embed-code-gradle-plugin` publishes the `io.spine.embed-code` Gradle plugin. The plugin +downloads an authenticated Embed Code release for the current platform and exposes Gradle +tasks that either check documentation snippets or update them. + +Consumers configure the plugin through Gradle Kotlin DSL. They do not maintain a separate +Embed Code configuration file and do not need to install the executable or Kotlin. + +## Project map + +- `build.gradle.kts`: root group and version wiring. +- `settings.gradle.kts`: plugin and dependency repositories and the `gradle-plugin` module. +- `version.gradle.kts`: the plugin version and default Embed Code application version. +- `gradle.properties`: configuration-cache, parallel-build, Kotlin-style, and standard-library + settings. +- `buildSrc/`: build settings, dependency coordinates, and the shared `jvm-module` convention. +- `gradle-plugin/build.gradle.kts`: plugin declaration, generated version source, functional + test source set, publication metadata, and Plugin Portal configuration. +- `gradle-plugin/src/main/kotlin/`: extension, plugin, task, platform, version, JSON, checksum, + download, installation, and execution logic. +- `gradle-plugin/src/main/templates/`: generated default-version source template. +- `gradle-plugin/src/test/kotlin/`: focused unit specifications. +- `gradle-plugin/src/functionalTest/kotlin/`: Gradle TestKit specifications against temporary + consumer builds. +- `.github/workflows/check.yml`: Ubuntu and Windows build verification. +- `.agents/skills/`: repository-specific implementation, test, writing, review, and security + workflows for agents. + +## Runtime flow + +1. `EmbedCodePlugin` creates the `embedCode` extension and registers installation, check, and + update tasks lazily. +2. `InstallEmbedCodeTask` selects the platform asset, establishes its expected SHA-256 digest, + and installs or restores a verified executable under `build/embed-code/`. +3. `EmbedCodeTask` validates the configured source roots and documentation root, generates the + temporary JSON configuration, and launches the executable in `check` or `embed` mode. +4. `checkEmbedding` reports stale documentation without rewriting it. `embedCode` updates + selected documentation files. + +When behavior changes, trace the complete extension → plugin → task → generated configuration +→ executable flow instead of patching only the first visible symptom. + +## Compatibility and dependency policy + +- The Gradle wrapper version and checksum are defined in + `gradle/wrapper/gradle-wrapper.properties`. +- The public minimum Gradle version is documented in `README.md` and must remain covered by + compatibility tests. +- `BuildSettings` owns the build JDK and emitted bytecode versions. Do not infer one from the + other. +- `jvm-module.gradle.kts` owns Kotlin language/API compatibility, explicit API mode, Java + release settings, and the test launcher. +- `gradle-plugin/build.gradle.kts` deliberately uses Gradle's Kotlin runtime instead of + publishing `kotlin-stdlib`. +- Dependency versions live in Kotlin objects under `buildSrc`; do not introduce a version + catalog as an unrelated refactor. + +Read these source files before changing a version or compatibility claim. Avoid copying +version numbers into agent guidance where a durable source path is sufficient. + +## Test strategy + +- Use unit specifications for pure parsing, validation, mapping, checksum, platform, and + version behavior. +- Use TestKit functional specifications for plugin application, extension wiring, task + registration, generated configuration, logging, configuration-cache reuse, process + execution, compatibility, filesystem effects, download/cache behavior, and failures visible + to a consumer build. +- Keep tests deterministic and offline-capable with temporary directories, fake release + assets, and local HTTP servers. +- Use `./gradlew test` for unit tests, `./gradlew functionalTest` for TestKit tests, and + `./gradlew check` for both plus plugin validation. +- CI runs the build and publishes the plugin to Maven Local on Ubuntu and Windows. Preserve + cross-platform paths, permissions, line endings, and process behavior. + +## Trust boundaries + +Treat executable acquisition and reuse as security-sensitive: + +- Authenticate release assets before extraction or execution. +- Bind cached metadata to the release source, exact tag, platform asset, and digest. +- Re-hash installed executables before reuse and fail closed when offline trust cannot be + established. +- Keep all derived installation paths inside the locked installation root. +- Reject symbolic-link or junction paths that can redirect writes outside that root. +- Keep tokens explicit, secret, and absent from logs, task inputs, cache keys, and persisted + metadata. +- Use unpredictable temporary files and safe replacement when installing assets. + +Apply both `gradle-engineer` and `security-engineer` to changes that cross Gradle lifecycle and +trust boundaries. + +## Documentation ownership + +- `README.md`: user-facing purpose, requirements, Kotlin DSL configuration, execution, and + development commands. +- `PROJECT.md`: project map, runtime flow, compatibility, test strategy, and trust boundaries. +- `AGENTS.md`: repository-wide agent operating policy and routing. +- `.agents/guidelines/writing-style.md`: shared Spine writing and typography rules. +- `.agents/skills/*/SKILL.md`: task-specific workflows and project constraints. + +Keep user instructions in `README.md`, contributor and architecture context here, and agent +procedures in the matching skill. + +## Agent routes + +- Codex: `AGENTS.md` → `PROJECT.md` → matching `.agents/skills/` entries. +- Claude: `CLAUDE.md` → `AGENTS.md` → matching `.agents/skills/` entries. +- GitHub Copilot: `.github/copilot-instructions.md` → `AGENTS.md` → matching skills. + +Keep routes thin. Update the owning document instead of duplicating policy across clients. From 4700dd9b187f6898ce9f182defee186faf16cd50 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 23 Jul 2026 15:55:57 +0200 Subject: [PATCH 02/11] Improve skills routing. --- .agents/skills/gradle-engineer/SKILL.md | 25 ++++------- .agents/skills/kotlin-engineer/SKILL.md | 22 ++++----- .agents/skills/reviewer/SKILL.md | 15 +++---- .agents/skills/security-engineer/SKILL.md | 26 ++++------- .agents/skills/test-engineer/SKILL.md | 34 +++++--------- .agents/skills/writer/SKILL.md | 14 ++---- .github/copilot-instructions.md | 27 ++---------- AGENTS.md | 54 +++++++++++++++-------- CLAUDE.md | 5 +-- 9 files changed, 87 insertions(+), 135 deletions(-) diff --git a/.agents/skills/gradle-engineer/SKILL.md b/.agents/skills/gradle-engineer/SKILL.md index b58915d..d6231b4 100644 --- a/.agents/skills/gradle-engineer/SKILL.md +++ b/.agents/skills/gradle-engineer/SKILL.md @@ -16,8 +16,8 @@ behavior as public contracts. ## Start 1. Read [`PROJECT.md`](../../../PROJECT.md) for the project map and supported behavior. -2. Read the affected build scripts, plugin API, task types, and nearby unit or - functional tests in full. +2. Read the affected build scripts, plugin API, and task types, along with + nearby unit and functional tests in full. 3. Trace values from the extension through `Provider` wiring into task inputs, execution behavior, outputs, and local state. 4. Read [Gradle practices](references/gradle-practices.md) when changing task @@ -25,16 +25,12 @@ behavior as public contracts. 5. Clarify only material ambiguity in the public DSL, compatibility floor, or verification target before editing. -## Coordinate responsibilities +## Cross-domain work - Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin source and build-logic implementation quality. -- Apply [`test-engineer`](../test-engineer/SKILL.md) to test structure, - assertions, fixtures, and regression design. - Apply [`security-engineer`](../security-engineer/SKILL.md) to release downloads, integrity metadata, offline reuse, tokens, archives, and paths. -- Apply [`writer`](../writer/SKILL.md) to public DSL documentation and examples. -- Apply [`reviewer`](../reviewer/SKILL.md) for findings-first review output. ## Apply the Gradle model @@ -42,18 +38,16 @@ behavior as public contracts. eager realization and configuration-time filesystem or network work. - Model configurable values with `Property`, `ListProperty`, `MapProperty`, `DirectoryProperty`, `RegularFileProperty`, and provider transformations. -- Wire producer-backed files through providers or file collections so Gradle - retains task dependencies. +- Wire producer-backed files through providers or file collections to preserve task dependencies. - Declare every task input, output, local-state file, and internal value according to its actual semantics. Choose path sensitivity deliberately. -- Keep secrets internal. Never expose a token as an input, log value, cache - identity, or publication value. +- Keep tokens internal and out of task inputs, logs, cache identities, and publication values. - Keep task actions configuration-cache safe. Inject execution services and avoid accessing `Project` or mutable configuration state at execution time. - Disable caching or up-to-date reuse when in-place writes, mutable external sources, or validation of local state make reuse unsound. -- Prefer Kotlin DSL and existing convention plugins over ad hoc Groovy or - duplicated module configuration. +- Prefer Kotlin DSL and existing convention plugins; do not duplicate module + configuration or add ad hoc Groovy. ## Preserve repository contracts @@ -71,9 +65,8 @@ behavior as public contracts. coordinates, POM metadata, and license packaging aligned. - Allow the install task to remain ungrouped and hidden from the standard task listing. Do not flag or change that behavior without a user-facing reason. -- Do not import organization-wide configuration-repository filtering or add a - rule requiring dependency groups to contain `spine`; neither is a repository - policy. +- Do not copy organization-wide repository filtering or dependency-group naming rules + unless this repository adopts them explicitly. ## Verify diff --git a/.agents/skills/kotlin-engineer/SKILL.md b/.agents/skills/kotlin-engineer/SKILL.md index 254b486..894ce4f 100644 --- a/.agents/skills/kotlin-engineer/SKILL.md +++ b/.agents/skills/kotlin-engineer/SKILL.md @@ -34,20 +34,17 @@ description: > uses a newer JDK. Do not call runtime APIs introduced after Java 17. - Preserve the Gradle-supplied Kotlin runtime model. Do not package a separate Kotlin runtime or raise the configured language/API compatibility casually. -- Prefer immutable values and read-only interfaces. Limit mutation to the - narrowest implementation scope. +- Prefer immutable values and read-only interfaces; keep mutation narrowly scoped. - Express invalid user configuration with clear validation at the boundary. Keep internal failures distinct from actionable Gradle user errors. - Avoid `!!`. Use it only for a proven invariant that cannot be represented more safely, and explain that invariant next to the use. -- Keep comments useful: explain constraints, invariants, or surprising choices - rather than restating code. +- Use comments for constraints, invariants, or surprising choices, not code narration. ## Documentation contract - Write useful KDoc for every named type, regardless of visibility: classes, - interfaces, objects, companion objects, enum classes, annotation classes, - and type aliases. + interfaces, objects, companion objects, enum and annotation classes, and type aliases. - Write useful KDoc for every public constructor, property, and function. - Document every public enum entry. - Document private and internal non-type declarations only when their contract @@ -56,12 +53,9 @@ description: > lifecycle semantics, and compatibility constraints where applicable. - Update KDoc when code changes invalidate an existing statement. -## Companion skills +## Cross-domain work -- Use [gradle-engineer](../gradle-engineer/SKILL.md) for Provider API, task - modeling, configuration-cache, and plugin wiring decisions. -- Use [test-engineer](../test-engineer/SKILL.md) for unit and TestKit tests. -- Use [security-engineer](../security-engineer/SKILL.md) when changing downloads, - checksums, archives, paths, caches, processes, or credentials. -- Use [writer](../writer/SKILL.md) for README and other user-facing prose. -- Use [reviewer](../reviewer/SKILL.md) for an independent findings-first review. +- Apply [gradle-engineer](../gradle-engineer/SKILL.md) when Kotlin declarations + participate in Gradle APIs, task modeling, providers, or plugin lifecycle. +- Apply [security-engineer](../security-engineer/SKILL.md) when Kotlin changes affect + downloads, checksums, archives, paths, caches, processes, or credentials. diff --git a/.agents/skills/reviewer/SKILL.md b/.agents/skills/reviewer/SKILL.md index 49b7b5a..7dd5906 100644 --- a/.agents/skills/reviewer/SKILL.md +++ b/.agents/skills/reviewer/SKILL.md @@ -22,8 +22,8 @@ Read the [project context](../../../PROJECT.md) before reviewing. 2. Inspect repository status without cleaning, staging, switching branches, or fetching. 3. Read the complete diff and every changed file in full. 4. Identify the acceptance source: request, issue, review comment, contract, test, or docs. -5. Trace affected callers, task wiring, data flows, tests, and documentation far enough to - establish the actual impact. +5. Trace affected callers, task wiring, data flows, tests, and documentation + far enough to establish the actual impact. 6. Inspect resolved or outdated review threads when the request includes prior comments. ## Gather evidence @@ -32,18 +32,16 @@ Read the [project context](../../../PROJECT.md) before reviewing. - Run focused tests and read-only diagnostics when they materially improve confidence. - Distinguish observed facts from inference. - Confirm reachability and user impact before reporting a correctness or security issue. -- Omit speculative findings. Do not promote an incomplete probe or a theoretical concern - into a defect. +- Omit speculation; do not turn an incomplete probe or theoretical concern into a defect. - Reconcile historical findings against the current checkout before repeating them. - Preserve unrelated local changes and untracked files. -## Compose companion skills +## Select review lenses Load only the guidance relevant to the changed files. Apply it during review instead of copying its policy into this skill. -- Use [Kotlin engineer](../kotlin-engineer/SKILL.md) for Kotlin implementation and API - conventions. +- Use [Kotlin engineer](../kotlin-engineer/SKILL.md) for Kotlin implementation and API conventions. - Use [Gradle engineer](../gradle-engineer/SKILL.md) for build logic, task modeling, configuration cache, compatibility, and publication configuration. - Use [test engineer](../test-engineer/SKILL.md) for test design and verification depth. @@ -61,8 +59,7 @@ Lead with findings. For each finding: - Cite the tightest file and line range. - State the evidence, affected scenario, and concrete impact. - Explain the smallest safe correction without writing or applying it. -- Avoid praise, summaries of correct code, and style preferences unsupported by project - policy. +- Avoid praise, summaries of correct code, or style preferences unsupported by project policy. Return these sections in order: diff --git a/.agents/skills/security-engineer/SKILL.md b/.agents/skills/security-engineer/SKILL.md index 413a2e5..09dae31 100644 --- a/.agents/skills/security-engineer/SKILL.md +++ b/.agents/skills/security-engineer/SKILL.md @@ -19,22 +19,17 @@ behavior when authenticity or filesystem ownership cannot be established. installation, platform, and functional-test code. 2. Identify the attacker-controlled value, trusted value, filesystem boundary, online/offline state, and final executable before proposing a change. -3. Reproduce a reported weakness with a concrete regression probe. Do not - present a hypothetical path as exploitable when the probe is unavailable or - blocked by an existing guard. +3. Reproduce a reported weakness with a concrete regression probe. Do not claim + exploitability when the probe is unavailable or an existing guard blocks it. 4. Read [Trust boundaries](references/trust-boundaries.md) for download, cache, redirect, archive, token, or path work. -## Coordinate responsibilities +## Cross-domain work - Apply [`gradle-engineer`](../gradle-engineer/SKILL.md) to task properties, local state, configuration cache, and TestKit wiring. -- Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin and Java - filesystem or network API usage. -- Apply [`test-engineer`](../test-engineer/SKILL.md) to regression structure and - deterministic fixtures. -- Apply [`reviewer`](../reviewer/SKILL.md) to severity, evidence, and review - scope. +- Use [`test-engineer`](../test-engineer/SKILL.md) for + deterministic regression fixtures and assertions. - Apply [`writer`](../writer/SKILL.md) when security behavior changes public configuration, diagnostics, or release documentation. @@ -42,8 +37,7 @@ behavior when authenticity or filesystem ownership cannot be established. - Accept exact validated release tags. Keep rolling or path-shaped identifiers out of release URLs and cache paths. -- Authenticate every downloaded or retained asset with a trusted SHA-256 before - extraction or execution. +- Authenticate downloaded or retained assets with trusted SHA-256 before extraction or execution. - Bind reusable state to release base URL, exact tag, and platform asset. Treat a source-identity mismatch as unverified state. - Rehash the installed executable before reuse. Never treat file existence, @@ -54,9 +48,8 @@ behavior when authenticity or filesystem ownership cannot be established. promoted only after verification. - Extract only the expected executable into a controlled staging file. Never resolve arbitrary archive entry paths into the installation tree. -- Validate normalized path containment and reject symbolic links, junctions, - redirecting entries, and non-directory components from the installation root - to every destination. +- Validate normalized path containment from the installation root to every destination; + reject symbolic links, junctions, redirecting entries, and non-directory components. - Keep tokens opt-in, internal, and absent from logs, errors, cache metadata, task inputs, and requests to untrusted hosts. - Keep authenticated metadata requests from following redirects. Verify asset @@ -72,8 +65,7 @@ behavior when authenticity or filesystem ownership cannot be established. whether authorization could cross a host boundary. - Probe crafted archives against the actual extraction strategy and assert no write outside controlled staging and installation paths. -- Assert failure leaves no trusted executable or valid-looking integrity - metadata behind. +- Assert failures leave no trusted executable or valid-looking integrity metadata. ## Verify diff --git a/.agents/skills/test-engineer/SKILL.md b/.agents/skills/test-engineer/SKILL.md index 8ded58b..3c2f3db 100644 --- a/.agents/skills/test-engineer/SKILL.md +++ b/.agents/skills/test-engineer/SKILL.md @@ -14,29 +14,23 @@ description: > and the closest specs in the same source set. 2. Reproduce a reported defect before changing production code whenever a deterministic reproduction is possible. -3. Classify the behavior as a focused unit contract or a consuming-build - contract. Use [the test patterns](references/test-patterns.md) to choose the - source set and fixture. +3. Classify the behavior as a focused unit contract or a consuming-build contract. + Use [the test patterns](references/test-patterns.md) to choose the source set and fixture. 4. Add the smallest test that proves the requested behavior and its important failure boundary. Keep unrelated coverage work out of scope. -5. Run the focused spec or source-set task first. Run the full repository check - after the focused test passes. -6. Report the behavior covered, the test location, and every verification - command with its result. +5. After the focused spec or source-set task passes, run the full repository check. +6. Report the covered behavior, test location, and result of every verification command. ## Test style - Use JUnit Jupiter for test structure and assertions. - Do not add Kotest, a mocking framework, or another assertion dependency. - Name a suite for subject `X` as `XSpec`; make Kotlin suites `internal`. -- Annotate every suite with `@DisplayName` describing what the subject should - do. -- Name test functions as backticked behavioral phrases that read as - specifications. +- Annotate every suite with `@DisplayName` describing the subject's expected behavior. +- Name test functions as backticked behavioral specifications. - Prefer hand-written stubs, fixed inputs, and observable outputs over mocks or implementation-detail verification. -- Keep setup close to the behavior unless a helper removes substantial, - repeated noise. +- Keep setup close to the behavior; extract a helper only when it removes substantial noise. ## Test boundary @@ -53,8 +47,7 @@ description: > - Use temporary directories and fixed fixture contents. - Replace release services with local files or a loopback fake HTTP server. -- Never depend on live GitHub, release assets, DNS, credentials, or public - network availability. +- Never depend on live GitHub, release assets, DNS, credentials, or public network availability. - Bind fake servers to loopback on an ephemeral port and stop them reliably. - Use fixed clocks, versions, digests, and environment values where relevant. - Gate genuinely platform-specific behavior with JUnit conditions; do not hide @@ -70,12 +63,9 @@ description: > [gradle-engineer](../gradle-engineer/SKILL.md) when build logic, plugin metadata, compatibility, or publication behavior changes. -## Companion skills +## Cross-domain work -- Use [kotlin-engineer](../kotlin-engineer/SKILL.md) for Kotlin and KDoc style - inside tests and production code. -- Use [gradle-engineer](../gradle-engineer/SKILL.md) for Gradle task and plugin - semantics. -- Use [security-engineer](../security-engineer/SKILL.md) to identify adversarial +- Apply [gradle-engineer](../gradle-engineer/SKILL.md) when expected behavior depends + on Gradle lifecycle, task annotations, configuration cache, or compatibility. +- Apply [security-engineer](../security-engineer/SKILL.md) to identify adversarial path, archive, download, checksum, cache, process, and credential cases. -- Use [reviewer](../reviewer/SKILL.md) for an independent findings-first review. diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 79e78d3..9d80d9c 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -30,8 +30,9 @@ Read the [project context](../../../PROJECT.md) and the 2. Treat current repository behavior as authoritative. 3. Verify every path, command, version, default, and platform, along with every task name, property, and failure claim, against current evidence. -4. Run the narrowest practical command when prose claims executable behavior or output. -5. Mark any remaining unverified claim explicitly; do not turn an assumption into fact. +4. Apply the specialist skill that owns each technical claim being changed. +5. Run the narrowest practical command when prose claims executable behavior or output. +6. Mark any remaining unverified claim explicitly; do not turn an assumption into fact. ## Write user-facing documentation @@ -67,15 +68,6 @@ Read the [project context](../../../PROJECT.md) and the - Use inline comments to explain why a constraint exists. Do not narrate the next statement. - Preserve exact machine text, command output, code, generated content, and license text. -## Compose companion skills - -- Consult [Kotlin engineer](../kotlin-engineer/SKILL.md) for declaration and API design. -- Consult [Gradle engineer](../gradle-engineer/SKILL.md) for Gradle DSL, - task behavior, and compatibility claims. -- Consult [test engineer](../test-engineer/SKILL.md) for test examples and verification. -- Consult [security engineer](../security-engineer/SKILL.md) for trust-boundary claims. -- Route read-only feedback to [reviewer](../reviewer/SKILL.md). - ## Validate the edit - Check heading hierarchy, links, referenced paths, terminology, code fences, and wrapping. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b81f2d2..807531f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,28 +8,7 @@ owning repository document instead of duplicating it here. - Read [`AGENTS.md`](../AGENTS.md) for repository-wide operating policy. - Read [`PROJECT.md`](../PROJECT.md) for the project map, runtime flow, compatibility constraints, test strategy, and trust boundaries. -- Use matching repository skills under [`.agents/skills/`](../.agents/skills/). +- Follow the skill-routing map in `AGENTS.md` and load every matching repository skill from + [`.agents/skills/`](../.agents/skills/). -## Skill routing - -- Use `kotlin-engineer` for Kotlin language, API, interoperability, and KDoc decisions. -- Use `gradle-engineer` for build scripts, plugin wiring, tasks, lazy configuration, - configuration cache, compatibility, and publication metadata. -- Use `test-engineer` for JUnit Jupiter unit tests and Gradle TestKit functional tests. -- Use `writer` for Markdown, KDoc, comments, examples, task descriptions, and public errors. -- Use `reviewer` only for non-writing, findings-first review and verification. -- Use `security-engineer` for release assets, checksums, downloads, caches, archives, tokens, - offline behavior, containment, and symbolic-link safety. - -Use multiple skills when a change crosses boundaries. A Kotlin Gradle task generally needs -`kotlin-engineer` and `gradle-engineer`; download or cache logic also needs -`security-engineer`. - -## Repository invariants - -- Preserve Kotlin DSL, JUnit Jupiter, Gradle TestKit, configuration-cache compatibility, and - the Gradle-supplied Kotlin runtime. -- Keep tests deterministic and independent of live external services. -- Verify documentation and compatibility claims against current source and build files. -- Do not write, commit, push, publish, reply to review threads, or resolve them while acting as - `reviewer`. +Keep repository policy in its owning document instead of duplicating it here. diff --git a/AGENTS.md b/AGENTS.md index f5116ba..21a0f9b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,17 +1,40 @@ # Agent instructions -These instructions apply to the whole repository. Keep this file focused on -repository-wide operating policy; keep task-specific procedures in the skills under -`.agents/skills/`. +These instructions apply to the whole repository. Keep this file focused on repository-wide +operating policy; keep task-specific procedures in the skills under `.agents/skills/`. ## Orientation - Read [PROJECT.md](PROJECT.md) before making a non-trivial change. -- Use every discovered skill whose description matches the task. Kotlin code inside Gradle - plugins normally needs both `kotlin-engineer` and `gradle-engineer`. +- Use every discovered skill whose description matches the task. - Treat each skill's frontmatter description as the routing source of truth. -- Read the affected implementation, nearby tests, and relevant build configuration before - editing. +- Read the affected implementation, nearby tests, and relevant build configuration before editing. + +## Skill routing + +Use all skills whose scope matches the requested work: + +- [`kotlin-engineer`](.agents/skills/kotlin-engineer/SKILL.md): Kotlin source, + Kotlin DSL, public API design, interoperability, and implementation quality. +- [`gradle-engineer`](.agents/skills/gradle-engineer/SKILL.md): build scripts, + plugin and task APIs, lazy configuration, compatibility, and publication metadata. +- [`test-engineer`](.agents/skills/test-engineer/SKILL.md): JUnit Jupiter unit tests, + Gradle TestKit functional tests, reproductions, fixtures, and regression coverage. +- [`writer`](.agents/skills/writer/SKILL.md): Markdown, KDoc, comments, examples, + task descriptions, public errors, and agent instructions. +- [`security-engineer`](.agents/skills/security-engineer/SKILL.md): release downloads, + integrity, caches, offline reuse, archives, tokens, redirects, and path containment. +- [`reviewer`](.agents/skills/reviewer/SKILL.md): read-only review of diffs, commits, + and pull requests, including relevant test execution and specialist review lenses. + +Combine skills only when their scopes overlap: + +- Kotlin in plugin or build logic normally requires both `kotlin-engineer` and `gradle-engineer`. +- Any behavioral change requires `test-engineer` with the owning implementation skill. +- Security-sensitive behavior requires the affected implementation skill, + `security-engineer`, and `test-engineer`. +- Documentation and public API text require `writer` plus the skill owning the contract. +- Review uses `reviewer` plus matching specialist skills; the reviewer remains read-only. ## Operating policy @@ -27,19 +50,15 @@ repository-wide operating policy; keep task-specific procedures in the skills un - Use Kotlin and Gradle Kotlin DSL. Do not add Groovy build scripts. - Use the Gradle wrapper and preserve the compatibility targets declared by the build. -- Preserve Gradle's lazy configuration model, configuration-cache compatibility, and the - Gradle-supplied Kotlin runtime. +- Preserve Gradle's lazy configuration and supplied Kotlin runtime, along with + configuration-cache compatibility. - Preserve JUnit Jupiter and Gradle TestKit. Do not introduce Kotest or a mocking framework without an explicit dependency and style decision. -- Use local fixtures or local HTTP servers in tests; do not make the test suite depend on live - external services. -- Apply `.agents/skills/security-engineer/SKILL.md` to download, checksum, cache, archive, - token, path-containment, symlink, and offline-mode changes. +- Use local fixtures or loopback HTTP servers so tests remain independent of live services. ## Documentation -- Follow `.agents/guidelines/writing-style.md` for Markdown, KDoc, comments, and user-facing - text. +- Follow `.agents/guidelines/writing-style.md` for Markdown, KDoc, comments, and user-facing text. - Give every named Kotlin type useful KDoc, regardless of visibility. - Give every public API useful KDoc. Document private members only when they carry a non-obvious contract, invariant, side effect, or failure mode. @@ -58,6 +77,5 @@ repository-wide operating policy; keep task-specific procedures in the skills un - Codex reads this `AGENTS.md` and discovers repository skills under `.agents/skills/`. - Claude reads [CLAUDE.md](CLAUDE.md), which routes back to this file. -- GitHub Copilot reads - [`.github/copilot-instructions.md`](.github/copilot-instructions.md), which routes back to - this file and the matching skills. +- [GitHub Copilot instructions](.github/copilot-instructions.md) route Copilot back + to this file and the matching skills. diff --git a/CLAUDE.md b/CLAUDE.md index 21eb8ad..ddd0e48 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,10 +2,7 @@ # Claude-specific routing -- Read `PROJECT.md` for the project map, runtime flow, compatibility constraints, test - strategy, and trust boundaries. +- Read `PROJECT.md` for architecture, compatibility, tests, and trust boundaries. - Load every matching skill through `.claude/skills/`, which routes to the canonical `.agents/skills/` directory; keep skill bodies agent-neutral. - Treat `AGENTS.md` as the repository-wide policy owner. Do not duplicate its rules here. -- Do not commit, push, publish, or mutate review-thread state unless the user's current request - explicitly asks for that action. From 1f6fb72cd0d2547f5876c60d29523c9e77eceb73 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 23 Jul 2026 21:02:23 +0200 Subject: [PATCH 03/11] Apply writer skills over written skills. --- .agents/guidelines/writing-style.md | 13 +- .agents/skills/gradle-engineer/SKILL.md | 59 ++------ .../references/gradle-practices.md | 91 ++++++------ .agents/skills/kotlin-engineer/SKILL.md | 33 +---- .../references/kotlin-policy.md | 30 ++-- .agents/skills/reviewer/SKILL.md | 24 ++-- .agents/skills/security-engineer/SKILL.md | 45 +----- .../references/trust-boundaries.md | 129 ++++++++---------- .agents/skills/test-engineer/SKILL.md | 42 +----- .../test-engineer/references/test-patterns.md | 53 +++---- .agents/skills/writer/SKILL.md | 11 +- .github/copilot-instructions.md | 5 +- AGENTS.md | 28 ++-- CLAUDE.md | 5 +- PROJECT.md | 78 +++++------ 15 files changed, 233 insertions(+), 413 deletions(-) diff --git a/.agents/guidelines/writing-style.md b/.agents/guidelines/writing-style.md index 5f77463..119c06d 100644 --- a/.agents/guidelines/writing-style.md +++ b/.agents/guidelines/writing-style.md @@ -13,12 +13,10 @@ structure when it is more specific. ## Format prose consistently -- Keep changed Markdown and KDoc prose at no more than 100 characters per source line - when the syntax permits. +- Keep changed Markdown and KDoc prose at no more than 100 characters per source line. - Use sentence case for headings. - Use one top-level heading and do not skip heading levels. -- Format paths, identifiers, Gradle tasks, properties, flags, commands, and literal values - as code. +- Format paths, identifiers, Gradle tasks, properties, flags, commands, and literals as code. - Put multiline commands, configuration, source, output, and other machine text in fenced code blocks with a language identifier when one applies. - Use relative links for repository files. @@ -26,12 +24,11 @@ structure when it is more specific. ## Use typography deliberately -- Use typographic quotation marks only for actual page or section titles, such as the - “Requirements” section. +- Reserve typographic quotation marks for actual page or section titles, such as “Requirements”. - Do not use quotation marks for emphasis, identifiers, invented labels, or technical terms. Use plain prose, italics, or code formatting as appropriate. -- Avoid runts and orphans. Reflow or rewrite a paragraph, list item, or table cell whose - final source line contains one word or an unusually short fragment. +- Do not leave runts or orphans. Reflow or rewrite a paragraph, list item, or table cell + whose final source line contains one word or an unusually short fragment. - Avoid widows in rendered or paginated material. Keep headings with their following content and avoid isolating a paragraph's opening or closing line. - Avoid rivers when a rendered layout shows repeated aligned gaps through adjacent lines. diff --git a/.agents/skills/gradle-engineer/SKILL.md b/.agents/skills/gradle-engineer/SKILL.md index d6231b4..a2135ee 100644 --- a/.agents/skills/gradle-engineer/SKILL.md +++ b/.agents/skills/gradle-engineer/SKILL.md @@ -9,9 +9,8 @@ description: >- # Gradle engineering -Preserve Gradle semantics across the plugin implementation and its build. Treat -configuration avoidance, task state, consumer compatibility, and cross-platform -behavior as public contracts. +Preserve Gradle semantics across the plugin and its build. Treat configuration avoidance, +task state, consumer compatibility, and cross-platform behavior as public contracts. ## Start @@ -20,54 +19,16 @@ behavior as public contracts. nearby unit and functional tests in full. 3. Trace values from the extension through `Provider` wiring into task inputs, execution behavior, outputs, and local state. -4. Read [Gradle practices](references/gradle-practices.md) when changing task - modeling, compatibility, build logic, publication, or platform behavior. -5. Clarify only material ambiguity in the public DSL, compatibility floor, or - verification target before editing. +4. Read [Gradle practices](references/gradle-practices.md) before editing; use it + as the source of truth for Gradle implementation and verification policy. +5. Clarify only material ambiguity in the public DSL, compatibility floor, or test target. ## Cross-domain work -- Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin source and - build-logic implementation quality. +- Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin source and build logic. - Apply [`security-engineer`](../security-engineer/SKILL.md) to release downloads, integrity metadata, offline reuse, tokens, archives, and paths. -## Apply the Gradle model - -- Register tasks lazily and retain `TaskProvider` or `Provider` values. Avoid - eager realization and configuration-time filesystem or network work. -- Model configurable values with `Property`, `ListProperty`, `MapProperty`, - `DirectoryProperty`, `RegularFileProperty`, and provider transformations. -- Wire producer-backed files through providers or file collections to preserve task dependencies. -- Declare every task input, output, local-state file, and internal value - according to its actual semantics. Choose path sensitivity deliberately. -- Keep tokens internal and out of task inputs, logs, cache identities, and publication values. -- Keep task actions configuration-cache safe. Inject execution services and - avoid accessing `Project` or mutable configuration state at execution time. -- Disable caching or up-to-date reuse when in-place writes, mutable external - sources, or validation of local state make reuse unsound. -- Prefer Kotlin DSL and existing convention plugins; do not duplicate module - configuration or add ad hoc Groovy. - -## Preserve repository contracts - -- Keep consumer builds compatible with Gradle 8.14.4 and newer. Do not infer - the compatibility floor from the current wrapper version. -- Compile against the Kotlin runtime supplied by supported Gradle versions. - Do not publish a conflicting Kotlin standard library with the plugin. -- Preserve Java 17 bytecode compatibility unless the supported Gradle floor is - intentionally changed with tests and documentation. -- Keep the public configuration Gradle-native. Treat generated Embed Code - configuration as an internal implementation detail. -- Preserve Linux and Windows behavior for paths, executable names, permissions, - process arguments, and task outputs. -- Keep plugin ID, implementation class, website, VCS URL, tags, artifact - coordinates, POM metadata, and license packaging aligned. -- Allow the install task to remain ungrouped and hidden from the standard task - listing. Do not flag or change that behavior without a user-facing reason. -- Do not copy organization-wide repository filtering or dependency-group naming rules - unless this repository adopts them explicitly. - ## Verify Run the narrowest relevant command first, then broaden: @@ -78,10 +39,10 @@ Run the narrowest relevant command first, then broaden: 4. `./gradlew :gradle-plugin:validatePlugins` 5. `./gradlew check` -Add or run a Gradle 8.14.4 TestKit scenario when compatibility-sensitive code -changes. Exercise configuration-cache reuse when plugin or task wiring changes. -Exercise platform-specific behavior on the relevant operating system; report an -unavailable Windows or Linux probe instead of treating another host as proof. +Add or run the minimum-version TestKit scenario when compatibility-sensitive +code changes. Exercise configuration-cache reuse when plugin or task wiring changes. +Exercise platform-specific behavior on the relevant operating system; report any +unavailable supported-platform probe instead of treating another host as proof. ## Report diff --git a/.agents/skills/gradle-engineer/references/gradle-practices.md b/.agents/skills/gradle-engineer/references/gradle-practices.md index 0400a50..236eaeb 100644 --- a/.agents/skills/gradle-engineer/references/gradle-practices.md +++ b/.agents/skills/gradle-engineer/references/gradle-practices.md @@ -18,15 +18,12 @@ Verify current versions and paths in the checkout before relying on this map. ## Project ownership -- Keep repository composition and dependency repositories in - `settings.gradle.kts`. -- Keep shared coordinates and version application in the root build and - `version.gradle.kts`. +- Keep repository composition and dependency repositories in `settings.gradle.kts`. +- Keep shared coordinates and version application in the root build and `version.gradle.kts`. - Keep build-only constants and dependency coordinates in `buildSrc/`. -- Keep shared JVM compilation and test setup in the `jvm-module` convention - plugin. -- Keep plugin declaration, generated sources, functional-test source set, and - publication metadata in `gradle-plugin/build.gradle.kts`. +- Keep shared JVM compilation and test setup in the `jvm-module` convention plugin. +- Keep plugin declarations, generated sources, and functional-test sources in + `gradle-plugin/build.gradle.kts`, together with publication metadata. - Keep extension defaults and task registration in `EmbedCodePlugin`. - Keep user-configurable values in `EmbedCodeExtension`. - Keep execution behavior in typed task classes. Keep pure release, platform, @@ -35,17 +32,15 @@ Verify current versions and paths in the checkout before relying on this map. ## Lazy configuration - Use `tasks.register`, `tasks.named`, provider mapping, and `flatMap`. -- Pass `TaskProvider` and provider-backed files through the graph instead of - calling `get()` during configuration. +- Pass `TaskProvider` and provider-backed files through the graph; avoid `get()` + and other eager reads during configuration. - Use conventions for defaults and preserve later user configuration. -- Use `disallowChanges()` only for values owned entirely by the plugin after - wiring. -- Preserve task dependencies carried by `Provider` and - `ConfigurableFileCollection`. +- Use `disallowChanges()` only for values owned entirely by the plugin after wiring. +- Preserve task dependencies carried by `Provider` and `ConfigurableFileCollection`. - Avoid configuration-time downloads, process execution, directory creation, and file reads whose result belongs to task execution. -- Keep collision handling lazy. Preserve underscore-prefixed fallback task - names when preferred names are occupied. +- Select underscore-prefixed fallback task names from names already present when + the plugin is applied. Preserve the documented limitation for tasks registered later. ## Task state and caching @@ -62,8 +57,8 @@ Verify current versions and paths in the checkout before relying on this map. `PathSensitivity.NONE` when only file bytes matter. - Disable build caching for in-place document mutation, external mutable release state, or tasks whose primary purpose is validating local state. -- Override up-to-date behavior only with an explicit invariant. The install - task intentionally reruns to authenticate local installation state. +- Override up-to-date behavior only with an explicit invariant; keep the install + task configured to rerun so it authenticates local installation state. ## Configuration cache @@ -78,25 +73,22 @@ Verify current versions and paths in the checkout before relying on this map. ## Compatibility and Kotlin runtime -- Preserve the documented consumer floor of Gradle 8.14.4. -- Treat the wrapper as the development runtime, not as the minimum supported - consumer version. -- Keep Java bytecode at version 17 while supporting Gradle 8.14.4 and Gradle 9. -- Keep Kotlin language and API usage compatible with Kotlin 2.0.21 supplied by - Gradle 8.14.4. -- Keep Kotlin standard library dependencies `compileOnly` for plugin - publication. Supply explicit test runtime dependencies where unit tests need - Gradle and Kotlin classes. +- Read the minimum supported Gradle version from `README.md` and keep it covered + by a versioned TestKit scenario. +- Treat the wrapper as the development runtime, not as the consumer floor. +- Read the build JDK and bytecode targets from `BuildSettings`; read Kotlin + language and API levels from `jvm-module.gradle.kts`. +- Keep plugin standard-library dependencies `compileOnly`. Supply explicit test + runtime dependencies where tests need Gradle and Kotlin classes. - Avoid Kotlin or Gradle APIs introduced after the supported consumer floor - unless guarded by a compatible alternative and covered by versioned TestKit. -- Change the floor only together with README, build settings, compatibility - tests, and publication claims. + unless a compatible alternative and versioned TestKit coverage protect their use. +- Change the floor together with `README.md`, compatibility tests, relevant + build settings, and publication claims. ## Kotlin DSL and build logic - Prefer typed Kotlin DSL accessors and typed task registration. -- Keep reusable module policy in convention plugins rather than copying blocks - between projects. +- Keep reusable module policy in convention plugins rather than copying blocks between projects. - Keep dependency coordinates centralized only when that improves ownership; avoid abstraction for a single opaque use. - Keep public plugin configuration in `build.gradle.kts`. Do not require users @@ -106,23 +98,18 @@ Verify current versions and paths in the checkout before relying on this map. ## Cross-platform behavior -- Use Gradle file properties and Java path APIs instead of string concatenation - for filesystem paths. -- Preserve Windows `.exe` naming and Linux executable permissions. -- Keep path comparisons case and separator aware. Do not infer Windows safety - from a Linux-only test. +- Use Gradle file properties and Java path APIs instead of string concatenation for paths. +- Preserve Windows `.exe` naming and Linux/macOS executable permissions. +- Keep path comparisons case- and separator-aware. Do not use a probe on one + supported operating system as proof for another. - Avoid shell-specific execution; pass executable and arguments separately. -- Preserve stable argument ordering and messages when tests or users rely on - them. -- Add platform conditions only for genuinely unsupported host behavior, not to - hide portable failures. +- Preserve stable argument ordering and messages when tests or users rely on them. +- Add platform conditions only for unsupported host behavior; do not hide portable failures. ## Publication -- Keep `io.spine.embed-code` and its implementation class stable unless a - migration is explicitly requested. -- Keep display name, description, website, VCS URL, and tags consistent with - README terminology. +- Keep `io.spine.embed-code` and its implementation class stable unless migration is requested. +- Keep display name, description, website, VCS URL, and tags consistent with README terminology. - Keep Maven artifact ID, POM name, description, license, developers, and SCM coordinates aligned with the plugin declaration. - Keep `LICENSE` in published JARs and keep sources and Javadoc artifacts. @@ -135,17 +122,15 @@ Verify current versions and paths in the checkout before relying on this map. - Use TestKit functional tests for plugin application, DSL wiring, task dependencies, task-name collisions, help output, configuration-cache reuse, publication-facing behavior, and real Gradle failures. -- Keep a TestKit probe on Gradle 8.14.4 for the minimum consumer contract. +- Keep a versioned TestKit probe on the minimum supported Gradle version. - Assert task outcomes, generated files, process arguments, and actionable output rather than internal registration details alone. -- Run focused test classes while iterating, then unit tests, functional tests, - `validatePlugins`, and `check`. +- Run focused tests while iterating, then unit and functional tests, `validatePlugins`, and `check`. ## Repository exclusions -- Do not add configuration-repository filtering copied from a shared - organization build unless this repository adopts that policy explicitly. +- Preserve `FAIL_ON_PROJECT_REPOS` in `settings.gradle.kts`; do not add + organization-wide repository content filters unless this repository explicitly adopts them. - Do not require dependency group names to contain `spine`. -- Do not expose `installEmbedCode` in the standard grouped task list merely for - consistency. Its hidden helper status is intentional; `checkEmbedding` and - `embedCode` are the user-facing tasks. +- Keep `installEmbedCode` ungrouped and absent from the standard task list; keep + `checkEmbedding` and `embedCode` as the user-facing tasks. diff --git a/.agents/skills/kotlin-engineer/SKILL.md b/.agents/skills/kotlin-engineer/SKILL.md index 894ce4f..5d10e6c 100644 --- a/.agents/skills/kotlin-engineer/SKILL.md +++ b/.agents/skills/kotlin-engineer/SKILL.md @@ -14,8 +14,8 @@ description: > their nearest tests, and the build configuration that defines their runtime. 2. Identify whether the change affects a public Gradle API, task execution, build configuration, serialization, file handling, or network behavior. -3. Load [the Kotlin policy](references/kotlin-policy.md) for declaration, - compatibility, style, and KDoc patterns. +3. Read [the Kotlin policy](references/kotlin-policy.md) before editing; use it + as the source of truth for compatibility, declarations, style, failures, and KDoc. 4. Preserve the existing architecture and behavior outside the requested scope. Prefer the smallest idiomatic change that makes the contract explicit. 5. Add or update tests through @@ -24,35 +24,6 @@ description: > required by the changed surface. 7. Report the affected contract, files changed, and verification performed. -## Required policy - -- Follow the official Kotlin coding conventions and the established local - formatting, naming, and file organization. -- Preserve explicit API mode. Declare visibility and API types deliberately; - avoid accidental public surface expansion. -- Keep published plugin bytecode compatible with Java 17 even when the build - uses a newer JDK. Do not call runtime APIs introduced after Java 17. -- Preserve the Gradle-supplied Kotlin runtime model. Do not package a separate - Kotlin runtime or raise the configured language/API compatibility casually. -- Prefer immutable values and read-only interfaces; keep mutation narrowly scoped. -- Express invalid user configuration with clear validation at the boundary. - Keep internal failures distinct from actionable Gradle user errors. -- Avoid `!!`. Use it only for a proven invariant that cannot be represented - more safely, and explain that invariant next to the use. -- Use comments for constraints, invariants, or surprising choices, not code narration. - -## Documentation contract - -- Write useful KDoc for every named type, regardless of visibility: classes, - interfaces, objects, companion objects, enum and annotation classes, and type aliases. -- Write useful KDoc for every public constructor, property, and function. -- Document every public enum entry. -- Document private and internal non-type declarations only when their contract - or implementation is not obvious. -- Describe behavior, defaults, side effects, failure conditions, Gradle - lifecycle semantics, and compatibility constraints where applicable. -- Update KDoc when code changes invalidate an existing statement. - ## Cross-domain work - Apply [gradle-engineer](../gradle-engineer/SKILL.md) when Kotlin declarations diff --git a/.agents/skills/kotlin-engineer/references/kotlin-policy.md b/.agents/skills/kotlin-engineer/references/kotlin-policy.md index c8df6a4..9011c86 100644 --- a/.agents/skills/kotlin-engineer/references/kotlin-policy.md +++ b/.agents/skills/kotlin-engineer/references/kotlin-policy.md @@ -5,17 +5,15 @@ repository. Prefer nearby established code when it is more specific. ## Compatibility boundary -- Build with the configured toolchain, but emit Java 17-compatible bytecode for - the published plugin. +- Build with the configured toolchain while emitting Java 17-compatible plugin bytecode. - Treat Java 17 as the maximum runtime API surface. A successful build on a newer JDK does not prove consumer compatibility. -- Preserve the configured Kotlin language and API versions. The plugin runs - with Kotlin supplied by Gradle; consumers must not need to install Kotlin or - apply the Kotlin plugin. +- Preserve the configured Kotlin language and API versions and Gradle-supplied runtime. + Consumers must not need to install Kotlin or apply its plugin. - Keep the Kotlin standard library non-published as configured. Do not replace `compileOnly` with `implementation` without an explicit compatibility reason. -- Use project-relative paths and Gradle abstractions instead of environment- - specific absolute paths. +- Avoid hard-coded environment-specific absolute paths. Use Gradle file + properties and resolve absolute paths only at the task or generated-configuration boundary. ## Declarations and APIs @@ -24,9 +22,9 @@ repository. Prefer nearby established code when it is more specific. - Add explicit return and property types to public APIs. Prefer an explicit type internally when it communicates a Gradle or domain contract. - Avoid exposing mutable collections or implementation-specific mutable state. -- Prefer Gradle managed properties for configurable values: - `Property`, `ListProperty`, `MapProperty`, - `DirectoryProperty`, and `RegularFileProperty`. +- Prefer these Gradle managed properties for configuration: + `Property`, `ListProperty`, `MapProperty`, `DirectoryProperty`, + and `RegularFileProperty` instead of mutable or eagerly resolved values. - Accept `Provider` when callers should retain lazy evaluation. Do not realize a provider merely to convert it into an eager value. - Keep task and extension APIs declarative. Put execution work in task actions, @@ -48,8 +46,7 @@ repository. Prefer nearby established code when it is more specific. - Keep Java interop intentional. Do not add `@JvmStatic`, `@JvmOverloads`, or other bridge annotations unless a verified consumer needs them. - Preserve local constant naming instead of normalizing unrelated declarations. -- Do not introduce unrelated coroutine, Flow, multiplatform, Android, - Protobuf, or framework conventions. +- Do not introduce unrelated coroutines, Flow, multiplatform, Android, Protobuf, or frameworks. ## Nullability and failures @@ -58,8 +55,8 @@ repository. Prefer nearby established code when it is more specific. invariant unrepresentable, document the invariant next to the assertion. - Catch the narrowest useful exception. Preserve the original cause when wrapping an internal failure in an actionable Gradle error. -- Do not expose credentials, tokens, internal paths, or low-level exception - details in user-facing messages. +- Keep credentials and tokens out of user-facing messages. Include a filesystem + path only when it identifies an actionable value; do not echo low-level exception text. ## KDoc @@ -88,9 +85,8 @@ Avoid repeating the declaration in prose: public abstract val version: Property ``` -Use KDoc links only for symbols visible to the documented source set. Do not -link published API documentation to `.agents/`, `buildSrc`, issues, branches, -or transient implementation notes. +Use KDoc links only for symbols visible to the documented source set. Do not link published +API documentation to `.agents/`, `buildSrc`, issues, branches, or transient implementation notes. For private and internal non-type declarations, add KDoc only when it explains a non-obvious contract. Prefer a short inline comment for a local invariant. diff --git a/.agents/skills/reviewer/SKILL.md b/.agents/skills/reviewer/SKILL.md index 7dd5906..87a8cbd 100644 --- a/.agents/skills/reviewer/SKILL.md +++ b/.agents/skills/reviewer/SKILL.md @@ -8,11 +8,10 @@ description: >- # Repository reviewer -Remain read-only with respect to source, configuration, documentation, Git state, and -remote state. Running tests and read-only diagnostics is allowed; normal ignored build -and test outputs are their only permitted filesystem side effect. Never edit files, -apply patches, stage, commit, push, publish, reply to review comments, or resolve review -threads. Route implementation to a companion skill. +Keep source, configuration, documentation, Git state, and remote state read-only. Run tests +and read-only diagnostics only when useful, and limit their filesystem side effects to normal +ignored build and test outputs. Never edit files, apply patches, stage, commit, push, publish, +reply to review comments, or resolve review threads. Route implementation to a separate task. Read the [project context](../../../PROJECT.md) before reviewing. @@ -38,8 +37,9 @@ Read the [project context](../../../PROJECT.md) before reviewing. ## Select review lenses -Load only the guidance relevant to the changed files. Apply it during review instead of -copying its policy into this skill. +Load only the guidance relevant to the changed files. Use companion skills as review lenses, +not as permission to implement. Skip their implementation workflows and any command that +exceeds this skill's read-only boundary. - Use [Kotlin engineer](../kotlin-engineer/SKILL.md) for Kotlin implementation and API conventions. - Use [Gradle engineer](../gradle-engineer/SKILL.md) for build logic, task modeling, @@ -47,9 +47,9 @@ copying its policy into this skill. - Use [test engineer](../test-engineer/SKILL.md) for test design and verification depth. - Use [security engineer](../security-engineer/SKILL.md) for executable, checksum, cache, path, archive, token, network, and offline trust boundaries. -- Use [writer](../writer/SKILL.md) and the - [writing style](../../guidelines/writing-style.md) for prose, KDoc, examples, errors, - comments, and agent instructions. +- Use [writer](../writer/SKILL.md) and the shared + [writing style](../../guidelines/writing-style.md) when reviewing prose, KDoc, + examples, errors, comments, or agent instructions. ## Report only actionable findings @@ -71,8 +71,10 @@ Return these sections in order: Write `None.` under an empty section. -End with one verdict: +Conclude the findings with one verdict: - `REQUEST CHANGES` when **Must fix** is non-empty. - `APPROVE WITH CHANGES` when only **Should fix** is non-empty. - `APPROVE` when only **Nits** or no findings remain. + +After the verdict, append the [required `Used skills` section](../../../AGENTS.md#reporting). diff --git a/.agents/skills/security-engineer/SKILL.md b/.agents/skills/security-engineer/SKILL.md index 09dae31..19ff127 100644 --- a/.agents/skills/security-engineer/SKILL.md +++ b/.agents/skills/security-engineer/SKILL.md @@ -11,7 +11,7 @@ description: >- Protect the boundary between remote release data and an executable run by a consumer build. Require evidence for security claims and preserve fail-closed -behavior when authenticity or filesystem ownership cannot be established. +behavior when authenticity or filesystem containment cannot be established. ## Start @@ -21,8 +21,9 @@ behavior when authenticity or filesystem ownership cannot be established. online/offline state, and final executable before proposing a change. 3. Reproduce a reported weakness with a concrete regression probe. Do not claim exploitability when the probe is unavailable or an existing guard blocks it. -4. Read [Trust boundaries](references/trust-boundaries.md) for download, cache, - redirect, archive, token, or path work. +4. Verify the current flow against source and tests, then apply + [Trust boundaries](references/trust-boundaries.md) to download, cache, redirect, + archive, token, or path work. ## Cross-domain work @@ -33,40 +34,6 @@ behavior when authenticity or filesystem ownership cannot be established. - Apply [`writer`](../writer/SKILL.md) when security behavior changes public configuration, diagnostics, or release documentation. -## Preserve trust invariants - -- Accept exact validated release tags. Keep rolling or path-shaped identifiers - out of release URLs and cache paths. -- Authenticate downloaded or retained assets with trusted SHA-256 before extraction or execution. -- Bind reusable state to release base URL, exact tag, and platform asset. Treat - a source-identity mismatch as unverified state. -- Rehash the installed executable before reuse. Never treat file existence, - executable permission, or an old marker alone as authenticity. -- Fail closed offline unless a locally verified executable is intact or a - cached asset can be authenticated with an already trusted digest. -- Keep temporary files unpredictable, contained, cleaned in `finally`, and - promoted only after verification. -- Extract only the expected executable into a controlled staging file. Never - resolve arbitrary archive entry paths into the installation tree. -- Validate normalized path containment from the installation root to every destination; - reject symbolic links, junctions, redirecting entries, and non-directory components. -- Keep tokens opt-in, internal, and absent from logs, errors, cache metadata, - task inputs, and requests to untrusted hosts. -- Keep authenticated metadata requests from following redirects. Verify asset - bytes after any allowed unauthenticated download redirect. - -## Build regression evidence - -- Prove digest mismatch, stale source identity, modified executable, and - unauthenticated offline cache behavior. -- Probe `..` and encoded path input, absolute destination overrides, symlinked - files or directories, and Windows junctions where the host supports them. -- Probe metadata redirects separately from release-asset redirects, including - whether authorization could cross a host boundary. -- Probe crafted archives against the actual extraction strategy and assert no - write outside controlled staging and installation paths. -- Assert failures leave no trusted executable or valid-looking integrity metadata. - ## Verify Run the smallest relevant unit or TestKit test first. Then run: @@ -80,6 +47,6 @@ cover. Report an unrun Windows junction or Linux permission probe explicitly. ## Report -For implementation, state the trust invariant, regression probe, and verification -result. For review, report only reproducible vulnerabilities or concrete +For implementation, state the trust invariant, local-write assumptions, regression probe, +and verification result. For review, report only reproducible vulnerabilities or concrete defense-in-depth gaps; omit speculative exploitability and identify unrun probes. diff --git a/.agents/skills/security-engineer/references/trust-boundaries.md b/.agents/skills/security-engineer/references/trust-boundaries.md index cbeec1f..7e590ea 100644 --- a/.agents/skills/security-engineer/references/trust-boundaries.md +++ b/.agents/skills/security-engineer/references/trust-boundaries.md @@ -5,7 +5,7 @@ Re-read current implementation and tests before assuming the flow is unchanged. ## Contents -- [Installation flow](#installation-flow) +- [Fresh installation or rebuild flow](#fresh-installation-or-rebuild-flow) - [Trust inputs](#trust-inputs) - [Release identity](#release-identity) - [SHA-256 trust](#sha-256-trust) @@ -17,27 +17,29 @@ Re-read current implementation and tests before assuming the flow is unchanged. - [Regression probes](#regression-probes) - [Review standard](#review-standard) -## Installation flow +## Fresh installation or rebuild flow -Preserve this order: +When installing or rebuilding from an asset, preserve this order: 1. Validate the exact release tag and select the platform asset. -2. Normalize the release base URL and derive source identity from the base URL, - exact tag, and asset name. -3. Resolve a trusted SHA-256 from explicit configuration or supported GitHub - release metadata. +2. Normalize the release base URL and derive source identity from its URL, exact tag, and asset. +3. Resolve a trusted SHA-256 from explicit configuration or supported GitHub release metadata. 4. Download into an unpredictable task-temporary file, or stage a retained cached asset without following links. 5. Hash staged bytes and compare them with the trusted digest. 6. Extract or copy into a separate staged executable. 7. Mark the staged file executable and calculate its digest. 8. Move it into a checked installation path. -9. Write asset digest, executable digest, and source identity through checked - temporary files. -10. Rehash and compare the installed executable before every reuse. +9. Write asset and executable digests plus source identity through checked temporary files. -Do not execute, expose as valid, or persist trusted-looking metadata before the -corresponding verification step succeeds. +Do not execute or expose an artifact as valid, and do not persist its trust metadata, +until the corresponding verification succeeds. + +For direct reuse, require the stored source identity to match current release inputs and require +a valid stored asset digest. If SHA-256 is configured, it must match that digest. Rehash the +installed executable and compare it with its stored digest. This path requires no network. +It relies on markers from a prior verified installation and detects executable changes only +while those markers remain intact. It does not resist replacement of both executable and markers. ## Trust inputs @@ -55,9 +57,10 @@ Treat these values as untrusted until validated: Treat these values as trust anchors only within their intended scope: - an explicitly configured normalized SHA-256; -- the digest for the named asset returned by the supported GitHub Releases API; -- a stored executable digest only when source identity and asset metadata still - match and the marker path is safe. +- the digest for the named asset returned by the supported GitHub Releases API. + +Stored source-identity and digest markers record prior verification; they are not independent +trust anchors against an actor who can replace both the executable and its markers. ## Release identity @@ -67,8 +70,7 @@ Treat these values as trust anchors only within their intended scope: - Use a filesystem-portable hash of the tag for cache directories; do not use the raw tag as a directory name. - Bind cache state to release base URL, exact tag, and asset name. -- Invalidate reuse when any identity component changes, even if the destination - executable already exists. +- Invalidate reuse when any identity component changes, regardless of an existing executable. - Preserve explicit checksum configuration as authoritative for custom mirrors. ## SHA-256 trust @@ -77,50 +79,40 @@ Treat these values as trust anchors only within their intended scope: - Prefer an explicitly configured digest when present. - Resolve automatic digests only for the supported HTTPS `github.com` release URL shape and the exact named asset from `api.github.com`. -- Require explicit SHA-256 configuration for unsupported custom release - sources. +- Require explicit SHA-256 configuration for unsupported custom release sources. - Hash staged bytes with no-follow file access before extraction. -- Hash the prepared executable separately; asset and executable digests serve - different reuse checks. -- Reject mismatch with expected and observed values that aid diagnosis but - contain no secret. +- Hash the prepared executable separately; asset and executable digests guard different reuse paths. +- Report expected and observed mismatch values only when they contain no secrets. ## Cache and offline reuse -- Treat cached assets and all metadata as attacker-modifiable local state. -- Reuse an installed executable only after source identity matches and its - current bytes match the stored executable digest. -- Reauthenticate a cached asset before rebuilding a missing or modified - executable. -- In offline mode, accept an intact locally verified executable. -- In offline mode, rebuild from a cached asset only when a trusted configured - digest authenticates it. -- Fail closed when neither path can establish authenticity. -- Never convert offline mode into “trust whatever is already in the build - directory.” +- Treat cached assets and integrity markers as mutable local state. +- Use source-identity and executable-digest markers to detect stale or altered files. +- Do not claim that direct reuse resists an actor who can replace both + the executable and its markers. +- Treat write access to the installation directory as part of the local trust boundary. +- Reauthenticate a cached asset before rebuilding a missing or modified executable. +- In offline mode, reuse an executable only when current state matches prior local markers. +- Rebuild from cache offline only when a trusted configured digest authenticates the asset. +- Fail closed when neither permitted offline path verifies its required state. +- Never treat a file in the build directory as trusted merely because Gradle is offline. ## Temporary files and promotion -- Create download and preparation files with unpredictable names under the - task temporary directory. -- Create metadata replacement files with unpredictable names beside their - checked destinations. +- Create unpredictable download and preparation filenames in the task temporary directory. +- Create metadata replacement files with unpredictable names beside checked destinations. - Open retained sources with `NOFOLLOW_LINKS`. - Verify staged data before moving it into the installation tree. -- Prefer atomic replacement and retain a safe fallback when atomic moves are - unsupported. +- Prefer atomic replacement and retain a safe fallback when atomic moves are unsupported. - Recheck destination components before and after promotion. - Delete temporary files in `finally`, including failure paths. -- Write integrity metadata only after the executable has been prepared and - promoted successfully. +- Write integrity metadata only after preparing and promoting the executable successfully. ## Archive extraction - Treat every ZIP entry name as untrusted. -- Search for the expected platform executable and stream its bytes into a - caller-selected staging file. -- Never resolve an entry path against the installation directory or extract - arbitrary archive structure. +- Stream the expected platform executable's bytes into a caller-selected staging file. +- Never resolve archive paths into the installation directory or extract arbitrary structure. - Reject archives that lack the expected executable. - Add explicit handling and probes before accepting archives with ambiguous duplicate basenames, unexpected link metadata, or resource-exhaustion risk. @@ -138,19 +130,16 @@ Treat these values as trust anchors only within their intended scope: entries, and non-directory intermediate components. - Create missing directories one component at a time and verify their real paths remain below the real installation root. -- Apply the same checks to executable, cached asset, checksums, source identity, - and metadata temporary destinations. +- Check containment for executable, cached asset, digest, source identity, and metadata paths. - Recheck after moves to reduce time-of-check/time-of-use exposure. -- Test Windows junction semantics on Windows; Unix symlink coverage is not a - substitute. +- Test Windows junction semantics on Windows; Unix symlink tests do not replace that probe. ## Tokens and redirects - Read a GitHub token only from explicit plugin configuration. - Mark it internal and keep it out of task fingerprints, cache identity, persisted metadata, logs, exception messages, and generated configuration. -- Attach authorization only to the exact trusted GitHub API host used for - checksum metadata. +- Attach authorization only to the exact trusted GitHub API host for checksum metadata. - Disable redirects for authenticated metadata requests. - If redirect handling changes, validate every hop and never forward authorization across a host boundary. @@ -162,23 +151,21 @@ Treat these values as trust anchors only within their intended scope: - **Tag:** Try `../../escaped`, slash, encoded slash, empty, and `latest`. Reject them before URL or path use. -- **Digest:** Serve bytes that differ from the trusted SHA-256. Fail without - installing anything trusted. +- **Digest:** Serve bytes that differ from the trusted SHA-256; install no trusted output. - **Identity:** Change the base URL, tag case, or platform asset with a cache present. Reject stale reuse or reauthenticate it. - **Executable:** Modify installed bytes after verification. Reject direct reuse. -- **Offline:** Remove metadata or provide a modified cache without a configured - digest. Fail closed. +- **Local markers:** Replace the executable and its markers together. Do not claim + resistance unless an independent trust anchor authenticates the resulting state. +- **Offline:** Remove metadata or modify the cache without a configured digest; fail closed. - **Temporary files:** Pre-create likely names and force a mid-install failure. Avoid collisions and clean staging files. -- **Archive:** Include traversal entries and the expected executable. Write only - the controlled staged file. -- **Output path:** Reconfigure executable or metadata outside the root. Reject - it before writing. +- **Archive:** Include traversal entries and the expected executable; write only + to the controlled staging file. +- **Output path:** Reconfigure executable or metadata outside the root; reject it before writing. - **Symbolic link:** Link an intermediate directory, asset, marker, or destination. Reject it without touching the target. -- **Junction:** Redirect the installation root or a child on Windows. Reject it - without touching the target. +- **Junction:** Redirect the installation root or a child on Windows; leave the target untouched. - **Metadata redirect:** Return a `3xx` response from the authenticated API endpoint. Do not follow it or forward the token. - **Asset redirect:** Redirect to bytes with a bad digest. Follow only as @@ -190,15 +177,9 @@ does not prove containment or cleanup. ## Review standard -- Reproduce against the current checkout and the actual public configuration - path. -- Identify the trust boundary, attacker capability, reachable operation, and - concrete impact. -- Distinguish a vulnerability from hardening, misleading diagnostics, or an - unreachable edge. -- Omit exploitability claims when the required host-specific or network probe - cannot be run. -- Prefer a minimal regression fixture that fails before the fix and passes - afterward. -- Preserve unrelated cache, release, and compatibility behavior while fixing a - confirmed issue. +- Reproduce against the current checkout and actual public configuration path. +- Identify the trust boundary, attacker capability, reachable operation, and concrete impact. +- Distinguish a vulnerability from hardening, misleading diagnostics, or an unreachable edge. +- Omit exploitability claims when the required host-specific or network probe is unavailable. +- Prefer a minimal regression fixture that fails before the fix and passes afterward. +- Preserve unrelated cache, release, and compatibility behavior when fixing confirmed issues. diff --git a/.agents/skills/test-engineer/SKILL.md b/.agents/skills/test-engineer/SKILL.md index 3c2f3db..a66b6d8 100644 --- a/.agents/skills/test-engineer/SKILL.md +++ b/.agents/skills/test-engineer/SKILL.md @@ -12,10 +12,10 @@ description: > 1. Read [the project context](../../../PROJECT.md), the production code in full, and the closest specs in the same source set. -2. Reproduce a reported defect before changing production code whenever a - deterministic reproduction is possible. -3. Classify the behavior as a focused unit contract or a consuming-build contract. - Use [the test patterns](references/test-patterns.md) to choose the source set and fixture. +2. Reproduce a reported defect deterministically before changing production code when possible. +3. Classify the behavior as a focused unit contract or a consuming-build contract. Use + [the test patterns](references/test-patterns.md) for source-set selection, + deterministic fixtures, TestKit setup, and verification commands. 4. Add the smallest test that proves the requested behavior and its important failure boundary. Keep unrelated coverage work out of scope. 5. After the focused spec or source-set task passes, run the full repository check. @@ -28,41 +28,9 @@ description: > - Name a suite for subject `X` as `XSpec`; make Kotlin suites `internal`. - Annotate every suite with `@DisplayName` describing the subject's expected behavior. - Name test functions as backticked behavioral specifications. -- Prefer hand-written stubs, fixed inputs, and observable outputs over mocks or - implementation-detail verification. +- Prefer hand-written stubs, fixed inputs, and observable outputs to mocks and call-order checks. - Keep setup close to the behavior; extract a helper only when it removes substantial noise. -## Test boundary - -- Put pure functions, validation, parsing, value transformations, and isolated - class behavior under `src/test/kotlin`. -- Put plugin application, task registration and execution, consuming-build - configuration, configuration-cache behavior, Gradle compatibility, and - process/file integration under `src/functionalTest/kotlin` with TestKit. -- Assert outcomes visible to a consumer: task outcomes, files, messages, - generated configuration, requests, and exit behavior. -- Avoid asserting Gradle internals or private call order. - -## Determinism and isolation - -- Use temporary directories and fixed fixture contents. -- Replace release services with local files or a loopback fake HTTP server. -- Never depend on live GitHub, release assets, DNS, credentials, or public network availability. -- Bind fake servers to loopback on an ephemeral port and stop them reliably. -- Use fixed clocks, versions, digests, and environment values where relevant. -- Gate genuinely platform-specific behavior with JUnit conditions; do not hide - portable failures behind an OS condition. - -## Verification - -- Run a focused unit spec with `:gradle-plugin:test --tests `. -- Run a focused functional spec with - `:gradle-plugin:functionalTest --tests `. -- Run `./gradlew check` after focused verification. -- Run the broader build or publication checks required by - [gradle-engineer](../gradle-engineer/SKILL.md) when build logic, plugin - metadata, compatibility, or publication behavior changes. - ## Cross-domain work - Apply [gradle-engineer](../gradle-engineer/SKILL.md) when expected behavior depends diff --git a/.agents/skills/test-engineer/references/test-patterns.md b/.agents/skills/test-engineer/references/test-patterns.md index 034704a..028bd83 100644 --- a/.agents/skills/test-engineer/references/test-patterns.md +++ b/.agents/skills/test-engineer/references/test-patterns.md @@ -2,26 +2,34 @@ Match the nearest existing spec before introducing a new helper or layout. +## Contents + +- [Unit spec](#unit-spec) +- [Unit or functional](#unit-or-functional) +- [TestKit project](#testkit-project) +- [Fake releases and HTTP](#fake-releases-and-http) +- [Filesystem and process fixtures](#filesystem-and-process-fixtures) +- [Compatibility cases](#compatibility-cases) +- [Verification commands](#verification-commands) + ## Unit spec Use JUnit Jupiter structure and assertions: ```kotlin -import org.junit.jupiter.api.Assertions.assertEquals +import org.gradle.api.InvalidUserDataException import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -@DisplayName("`ReleaseTag` should") -internal class ReleaseTagSpec { +@DisplayName("Embed Code release-tag support should") +internal class EmbedCodeVersionSpec { @Test - fun `reject a blank value`() { - val error = assertThrows(IllegalArgumentException::class.java) { - validateReleaseTag(" ") + fun `reject a missing exact release tag`() { + assertThrows(InvalidUserDataException::class.java) { + validateVersion(" ") } - - assertEquals("Release tag must not be blank.", error.message) } } ``` @@ -87,22 +95,18 @@ actionable message rather than a complete stack trace. - Use `HttpServer` on `127.0.0.1` with port `0` when request behavior matters. - Record request paths, headers, or counts in thread-safe test state. - Return only the status and body needed by the test. -- Stop the server in `finally`, `@AfterEach`, or another guaranteed cleanup - path. +- Stop the server in `finally`, `@AfterEach`, or another guaranteed cleanup path. - Use a local file URI instead of HTTP when transport behavior is irrelevant. -Never call GitHub or another public endpoint from a test. A live service makes -rate limits, credentials, network failures, mutable releases, and remote state -part of the result. +Never call GitHub or another public endpoint from a test. A live service makes the result +depend on rate limits, credentials, network failures, mutable releases, and remote state. ## Filesystem and process fixtures - Resolve every fixture below the temporary directory. - Use fixed file contents and explicit UTF-8 where an API requires a charset. -- Create a minimal fake executable that records arguments and produces a fixed - exit result. -- Test both the expected path and important containment or replacement - failures. +- Create a minimal fake executable with fixed output that records its arguments and exit status. +- Test the expected path and important containment or replacement failures. - Avoid sleeps. Wait on an observable process, file, or request condition with a bounded timeout when synchronization is necessary. - Apply JUnit OS conditions only to behavior that truly depends on executable @@ -110,14 +114,12 @@ part of the result. ## Compatibility cases -Use `withGradleVersion(...)` only for versions supported by the plugin's stated -compatibility range. Keep the ordinary runner on the wrapper version. Cover a -second Gradle version when the changed API or behavior varies across Gradle -releases. +Use `withGradleVersion(...)` only within the plugin's stated compatibility range. +Keep the ordinary runner on the wrapper version. Cover another Gradle version when +the changed API or behavior varies between releases. -Keep Java 17 consumer compatibility distinct from the JDK used to build the -project. A test passing on the build JDK alone does not prove that the published -plugin runs on Java 17. +Keep Java 17 consumer compatibility distinct from the JDK used to build the project. +A test passing on the build JDK alone does not prove Java 17 consumer compatibility. ## Verification commands @@ -129,5 +131,4 @@ Prefer this order: ./gradlew check ``` -Run only the applicable focused command first. Run the full check before -claiming completion. +Run the applicable focused command first, then run the full check before claiming completion. diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 9d80d9c..7dcdc08 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -1,15 +1,15 @@ --- name: writer description: >- - Use when creating or revising README.md, PROJECT.md, AGENTS.md, skill routes, - Markdown, Kotlin KDoc, Gradle task descriptions, public errors, or explanatory - comments; write verified project documentation in the Spine style. + Use when creating, reviewing, or revising README.md, PROJECT.md, AGENTS.md, + skill routes, Markdown, Kotlin KDoc, Gradle task descriptions, public errors, + or explanatory comments; write verified project documentation in the Spine style. --- # Documentation writer Read the [project context](../../../PROJECT.md) and the -[writing style](../../guidelines/writing-style.md) before editing. +[writing style](../../guidelines/writing-style.md) before reviewing or editing. ## Establish the audience and owner @@ -17,8 +17,7 @@ Read the [project context](../../../PROJECT.md) and the - Prefer updating the document that already owns the topic. - Keep `README.md` focused on the user problem, setup, Kotlin DSL configuration, tasks, and short contributor commands. -- Keep `PROJECT.md` focused on stable project context and architecture, - compatibility, and source-of-truth pointers. +- Keep `PROJECT.md` focused on stable architecture, compatibility, context, and source pointers. - Keep `AGENTS.md`, skill descriptions, and other agent routes focused on dispatch and repository-wide policy. Link to the owning skill instead of duplicating its guidance. - Keep KDoc with its declaration, task descriptions with their task registrations, and diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 807531f..484f6de 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,7 +8,4 @@ owning repository document instead of duplicating it here. - Read [`AGENTS.md`](../AGENTS.md) for repository-wide operating policy. - Read [`PROJECT.md`](../PROJECT.md) for the project map, runtime flow, compatibility constraints, test strategy, and trust boundaries. -- Follow the skill-routing map in `AGENTS.md` and load every matching repository skill from - [`.agents/skills/`](../.agents/skills/). - -Keep repository policy in its owning document instead of duplicating it here. +- Load every matching [repository skill](../.agents/skills/) through the `AGENTS.md` routing map. diff --git a/AGENTS.md b/AGENTS.md index 21a0f9b..812a16a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,8 +31,8 @@ Combine skills only when their scopes overlap: - Kotlin in plugin or build logic normally requires both `kotlin-engineer` and `gradle-engineer`. - Any behavioral change requires `test-engineer` with the owning implementation skill. -- Security-sensitive behavior requires the affected implementation skill, - `security-engineer`, and `test-engineer`. +- Security work requires `security-engineer` and `test-engineer`, plus the skill + responsible for the affected implementation. - Documentation and public API text require `writer` plus the skill owning the contract. - Review uses `reviewer` plus matching specialist skills; the reviewer remains read-only. @@ -50,18 +50,18 @@ Combine skills only when their scopes overlap: - Use Kotlin and Gradle Kotlin DSL. Do not add Groovy build scripts. - Use the Gradle wrapper and preserve the compatibility targets declared by the build. -- Preserve Gradle's lazy configuration and supplied Kotlin runtime, along with - configuration-cache compatibility. +- Preserve lazy Gradle configuration, its supplied Kotlin runtime, and configuration-cache support. - Preserve JUnit Jupiter and Gradle TestKit. Do not introduce Kotest or a mocking framework without an explicit dependency and style decision. - Use local fixtures or loopback HTTP servers so tests remain independent of live services. ## Documentation -- Follow `.agents/guidelines/writing-style.md` for Markdown, KDoc, comments, and user-facing text. -- Give every named Kotlin type useful KDoc, regardless of visibility. -- Give every public API useful KDoc. Document private members only when they carry a - non-obvious contract, invariant, side effect, or failure mode. +- Apply the [writer skill](.agents/skills/writer/SKILL.md) and + [writing style](.agents/guidelines/writing-style.md) to documentation and user-facing text. +- Follow the + [Kotlin KDoc policy](.agents/skills/kotlin-engineer/references/kotlin-policy.md#kdoc) + for declarations and public APIs. - Verify documentation claims against current code, tests, build files, or command output. ## Verification @@ -70,8 +70,16 @@ Combine skills only when their scopes overlap: - Run `./gradlew check` before handing off a completed code or test change unless the task is documentation-only or a narrower check is explicitly sufficient. - Run `git diff --check` after editing. -- Report every command actually run and distinguish failures caused by the change from - environment or unrelated failures. +- Report every command actually run. Separate failures caused by the change from failures + caused by the environment or unrelated work. + +## Reporting + +- Include a `## Used skills` section in every final response. +- List all materially applied skills, including repository and platform-provided skills. +- Do not list skills that were merely discovered, inspected, or considered. +- Write `- None.` when no skill was used. +- Place the section last unless a required machine-readable trailer must remain last. ## Agent entry points diff --git a/CLAUDE.md b/CLAUDE.md index ddd0e48..f88edc4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,4 @@ # Claude-specific routing -- Read `PROJECT.md` for architecture, compatibility, tests, and trust boundaries. -- Load every matching skill through `.claude/skills/`, which routes to the canonical - `.agents/skills/` directory; keep skill bodies agent-neutral. -- Treat `AGENTS.md` as the repository-wide policy owner. Do not duplicate its rules here. +Load every matching skill through `.claude/skills/`, which maps to `.agents/skills/`. diff --git a/PROJECT.md b/PROJECT.md index 5c15276..3c0f4de 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -1,8 +1,7 @@ # Project -This document gives agents and contributors the project map, runtime flow, compatibility -constraints, test strategy, and trust boundaries. For repository-wide operating policy, read -[AGENTS.md](AGENTS.md). +This document gives agents and contributors the project map, runtime flow, compatibility, +test strategy, and trust boundaries. Read [AGENTS.md](AGENTS.md) for operating policy. ## Overview @@ -18,8 +17,8 @@ Embed Code configuration file and do not need to install the executable or Kotli - `build.gradle.kts`: root group and version wiring. - `settings.gradle.kts`: plugin and dependency repositories and the `gradle-plugin` module. - `version.gradle.kts`: the plugin version and default Embed Code application version. -- `gradle.properties`: configuration-cache, parallel-build, Kotlin-style, and standard-library - settings. +- `gradle.properties`: Gradle runtime, parallelism, and configuration-cache settings, + plus Kotlin style and dependency defaults. - `buildSrc/`: build settings, dependency coordinates, and the shared `jvm-module` convention. - `gradle-plugin/build.gradle.kts`: plugin declaration, generated version source, functional test source set, publication metadata, and Plugin Portal configuration. @@ -27,38 +26,33 @@ Embed Code configuration file and do not need to install the executable or Kotli download, installation, and execution logic. - `gradle-plugin/src/main/templates/`: generated default-version source template. - `gradle-plugin/src/test/kotlin/`: focused unit specifications. -- `gradle-plugin/src/functionalTest/kotlin/`: Gradle TestKit specifications against temporary - consumer builds. +- `gradle-plugin/src/functionalTest/kotlin/`: TestKit consumer-build specifications. - `.github/workflows/check.yml`: Ubuntu and Windows build verification. -- `.agents/skills/`: repository-specific implementation, test, writing, review, and security - workflows for agents. +- `.agents/skills/`: repository engineering, test, writing, review, and security workflows. ## Runtime flow -1. `EmbedCodePlugin` creates the `embedCode` extension and registers installation, check, and - update tasks lazily. +1. `EmbedCodePlugin` creates the `embedCode` extension and lazily registers + installation, check, and update tasks. 2. `InstallEmbedCodeTask` selects the platform asset, establishes its expected SHA-256 digest, and installs or restores a verified executable under `build/embed-code/`. -3. `EmbedCodeTask` validates the configured source roots and documentation root, generates the - temporary JSON configuration, and launches the executable in `check` or `embed` mode. -4. `checkEmbedding` reports stale documentation without rewriting it. `embedCode` updates - selected documentation files. +3. `EmbedCodeTask` validates the configured source and documentation roots. It passes direct + source settings as command-line arguments or writes temporary JSON for named sources, + then launches the executable in `check` or `embed` mode. +4. `checkEmbedding` reports stale documentation, while `embedCode` updates selected files. -When behavior changes, trace the complete extension → plugin → task → generated configuration -→ executable flow instead of patching only the first visible symptom. +When behavior changes, trace the complete extension → plugin → task → arguments or generated +configuration → executable flow instead of patching only the first visible symptom. ## Compatibility and dependency policy -- The Gradle wrapper version and checksum are defined in - `gradle/wrapper/gradle-wrapper.properties`. -- The public minimum Gradle version is documented in `README.md` and must remain covered by - compatibility tests. -- `BuildSettings` owns the build JDK and emitted bytecode versions. Do not infer one from the - other. +- The Gradle wrapper version and checksum are defined in `gradle/wrapper/gradle-wrapper.properties`. +- Document the minimum Gradle version in `README.md` and cover it with compatibility tests. +- `BuildSettings` independently defines the build JDK and emitted bytecode versions. - `jvm-module.gradle.kts` owns Kotlin language/API compatibility, explicit API mode, Java release settings, and the test launcher. -- `gradle-plugin/build.gradle.kts` deliberately uses Gradle's Kotlin runtime instead of - publishing `kotlin-stdlib`. +- `gradle-plugin/build.gradle.kts` uses the Kotlin runtime supplied by Gradle; + it does not publish `kotlin-stdlib`. - Dependency versions live in Kotlin objects under `buildSrc`; do not introduce a version catalog as an unrelated refactor. @@ -67,14 +61,11 @@ version numbers into agent guidance where a durable source path is sufficient. ## Test strategy -- Use unit specifications for pure parsing, validation, mapping, checksum, platform, and - version behavior. -- Use TestKit functional specifications for plugin application, extension wiring, task - registration, generated configuration, logging, configuration-cache reuse, process - execution, compatibility, filesystem effects, download/cache behavior, and failures visible - to a consumer build. -- Keep tests deterministic and offline-capable with temporary directories, fake release - assets, and local HTTP servers. +- Use unit specifications for pure parsing, validation, mapping, checksums, platforms, and versions. +- Use TestKit functional specifications for plugin application, extension and task wiring, + generated configuration, logging, configuration-cache reuse, process execution, + compatibility, filesystem and cache behavior, and consumer-visible failures. +- Keep tests offline with deterministic fixtures, temporary directories, and loopback HTTP servers. - Use `./gradlew test` for unit tests, `./gradlew functionalTest` for TestKit tests, and `./gradlew check` for both plus plugin validation. - CI runs the build and publishes the plugin to Maven Local on Ubuntu and Windows. Preserve @@ -86,21 +77,19 @@ Treat executable acquisition and reuse as security-sensitive: - Authenticate release assets before extraction or execution. - Bind cached metadata to the release source, exact tag, platform asset, and digest. -- Re-hash installed executables before reuse and fail closed when offline trust cannot be - established. -- Keep all derived installation paths inside the locked installation root. +- Rehash installed executables before reuse. Local markers detect changes only while intact. +- Fail closed offline unless one path succeeds: reuse state that matches prior markers, + or rebuild from a cached asset verified by a configured digest. +- Keep all derived installation paths inside the fixed installation root. - Reject symbolic-link or junction paths that can redirect writes outside that root. -- Keep tokens explicit, secret, and absent from logs, task inputs, cache keys, and persisted - metadata. +- Keep tokens explicit and secret; exclude them from logs, task inputs, cache keys, and metadata. - Use unpredictable temporary files and safe replacement when installing assets. -Apply both `gradle-engineer` and `security-engineer` to changes that cross Gradle lifecycle and -trust boundaries. +Apply `gradle-engineer` and `security-engineer` to work spanning Gradle and security boundaries. ## Documentation ownership -- `README.md`: user-facing purpose, requirements, Kotlin DSL configuration, execution, and - development commands. +- `README.md`: user-facing purpose, requirements, Kotlin DSL, execution, and development commands. - `PROJECT.md`: project map, runtime flow, compatibility, test strategy, and trust boundaries. - `AGENTS.md`: repository-wide agent operating policy and routing. - `.agents/guidelines/writing-style.md`: shared Spine writing and typography rules. @@ -112,7 +101,8 @@ procedures in the matching skill. ## Agent routes - Codex: `AGENTS.md` → `PROJECT.md` → matching `.agents/skills/` entries. -- Claude: `CLAUDE.md` → `AGENTS.md` → matching `.agents/skills/` entries. -- GitHub Copilot: `.github/copilot-instructions.md` → `AGENTS.md` → matching skills. +- Claude: `CLAUDE.md` → `AGENTS.md` → `PROJECT.md` → matching `.claude/skills/` aliases. +- GitHub Copilot: `.github/copilot-instructions.md` → `AGENTS.md` → `PROJECT.md` + → matching repository skills under `.agents/skills/`. Keep routes thin. Update the owning document instead of duplicating policy across clients. From 222b2e3cb4d09b3fe79647544edfa1f71bd267dc Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 23 Jul 2026 21:07:43 +0200 Subject: [PATCH 04/11] Provide rule for tables alignment. --- .agents/guidelines/writing-style.md | 2 ++ .../test-engineer/references/test-patterns.md | 18 +++++++++--------- .agents/skills/writer/SKILL.md | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.agents/guidelines/writing-style.md b/.agents/guidelines/writing-style.md index 119c06d..6edb8d1 100644 --- a/.agents/guidelines/writing-style.md +++ b/.agents/guidelines/writing-style.md @@ -19,6 +19,8 @@ structure when it is more specific. - Format paths, identifiers, Gradle tasks, properties, flags, commands, and literals as code. - Put multiline commands, configuration, source, output, and other machine text in fenced code blocks with a language identifier when one applies. +- Align Markdown tables in source: pad every cell so column pipes line up vertically, + and size separator cells to the padded column widths. Re-align the full table after any edit. - Use relative links for repository files. - Prefer reference-style links for external destinations. diff --git a/.agents/skills/test-engineer/references/test-patterns.md b/.agents/skills/test-engineer/references/test-patterns.md index 028bd83..8842f36 100644 --- a/.agents/skills/test-engineer/references/test-patterns.md +++ b/.agents/skills/test-engineer/references/test-patterns.md @@ -39,15 +39,15 @@ failure would leave the broken contract unclear. ## Unit or functional -| Behavior | Source set | -|---|---| -| Parse, normalize, hash, validate, or map a value | `src/test/kotlin` | -| Select a platform name without executing Gradle | `src/test/kotlin` | -| Apply the plugin or configure its extension | `src/functionalTest/kotlin` | -| Register, wire, execute, or inspect a task | `src/functionalTest/kotlin` | -| Verify configuration-cache reuse | `src/functionalTest/kotlin` | -| Verify behavior across Gradle versions | `src/functionalTest/kotlin` | -| Launch the fake Embed Code executable | `src/functionalTest/kotlin` | +| Behavior | Source set | +|--------------------------------------------------|-----------------------------| +| Parse, normalize, hash, validate, or map a value | `src/test/kotlin` | +| Select a platform name without executing Gradle | `src/test/kotlin` | +| Apply the plugin or configure its extension | `src/functionalTest/kotlin` | +| Register, wire, execute, or inspect a task | `src/functionalTest/kotlin` | +| Verify configuration-cache reuse | `src/functionalTest/kotlin` | +| Verify behavior across Gradle versions | `src/functionalTest/kotlin` | +| Launch the fake Embed Code executable | `src/functionalTest/kotlin` | Do not use TestKit when a direct unit call proves the contract. Do not replace a consuming-build test with a unit test when Gradle wiring is the behavior. diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 7dcdc08..4c24651 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -69,7 +69,7 @@ Read the [project context](../../../PROJECT.md) and the ## Validate the edit -- Check heading hierarchy, links, referenced paths, terminology, code fences, and wrapping. +- Check heading hierarchy, links, paths, terminology, code fences, table alignment, and wrapping. - Check that changed Markdown and KDoc follow the shared writing style. - Leave no orphans: reflow or rewrite a paragraph, list item, table cell, or KDoc block whose final source line contains one word or an unusually short fragment. From 96deeb183553e1bf6191dcb2d1f8977a1cf423d7 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Thu, 23 Jul 2026 21:30:44 +0200 Subject: [PATCH 05/11] Improve windows behavior. --- AGENTS.md | 22 ++++++++-------------- CLAUDE.md | 2 +- PROJECT.md | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 812a16a..bb7d960 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,20 +12,14 @@ operating policy; keep task-specific procedures in the skills under `.agents/ski ## Skill routing -Use all skills whose scope matches the requested work: - -- [`kotlin-engineer`](.agents/skills/kotlin-engineer/SKILL.md): Kotlin source, - Kotlin DSL, public API design, interoperability, and implementation quality. -- [`gradle-engineer`](.agents/skills/gradle-engineer/SKILL.md): build scripts, - plugin and task APIs, lazy configuration, compatibility, and publication metadata. -- [`test-engineer`](.agents/skills/test-engineer/SKILL.md): JUnit Jupiter unit tests, - Gradle TestKit functional tests, reproductions, fixtures, and regression coverage. -- [`writer`](.agents/skills/writer/SKILL.md): Markdown, KDoc, comments, examples, - task descriptions, public errors, and agent instructions. -- [`security-engineer`](.agents/skills/security-engineer/SKILL.md): release downloads, - integrity, caches, offline reuse, archives, tokens, redirects, and path containment. -- [`reviewer`](.agents/skills/reviewer/SKILL.md): read-only review of diffs, commits, - and pull requests, including relevant test execution and specialist review lenses. +Use all matching skills. The links are an index; each frontmatter description defines its scope: + +- [`kotlin-engineer`](.agents/skills/kotlin-engineer/SKILL.md) +- [`gradle-engineer`](.agents/skills/gradle-engineer/SKILL.md) +- [`test-engineer`](.agents/skills/test-engineer/SKILL.md) +- [`writer`](.agents/skills/writer/SKILL.md) +- [`security-engineer`](.agents/skills/security-engineer/SKILL.md) +- [`reviewer`](.agents/skills/reviewer/SKILL.md) Combine skills only when their scopes overlap: diff --git a/CLAUDE.md b/CLAUDE.md index f88edc4..b705f7c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,4 +2,4 @@ # Claude-specific routing -Load every matching skill through `.claude/skills/`, which maps to `.agents/skills/`. +Load every matching skill directly from `.agents/skills/`. diff --git a/PROJECT.md b/PROJECT.md index 3c0f4de..1dd55f8 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -101,7 +101,7 @@ procedures in the matching skill. ## Agent routes - Codex: `AGENTS.md` → `PROJECT.md` → matching `.agents/skills/` entries. -- Claude: `CLAUDE.md` → `AGENTS.md` → `PROJECT.md` → matching `.claude/skills/` aliases. +- Claude: `CLAUDE.md` → `AGENTS.md` → `PROJECT.md` → matching `.agents/skills/` entries. - GitHub Copilot: `.github/copilot-instructions.md` → `AGENTS.md` → `PROJECT.md` → matching repository skills under `.agents/skills/`. From 072e2da32ea15acd2580050b385acb39e214f0b1 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 24 Jul 2026 10:09:42 +0200 Subject: [PATCH 06/11] Improve source of truth reliability. --- .agents/skills/gradle-engineer/SKILL.md | 14 +++++++++++--- .agents/skills/gradle-engineer/agents/openai.yaml | 4 ++-- .../test-engineer/references/test-patterns.md | 4 ++-- .agents/skills/writer/SKILL.md | 15 +++------------ 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.agents/skills/gradle-engineer/SKILL.md b/.agents/skills/gradle-engineer/SKILL.md index a2135ee..57d5fb4 100644 --- a/.agents/skills/gradle-engineer/SKILL.md +++ b/.agents/skills/gradle-engineer/SKILL.md @@ -2,9 +2,10 @@ name: gradle-engineer description: >- Apply repository-specific Gradle engineering policy when changing or reviewing - build scripts, buildSrc, Kotlin DSL, plugin, extension, or task APIs, lazy - configuration, task state, configuration-cache behavior, TestKit coverage, - consumer compatibility, or publication metadata. + build scripts, buildSrc, Kotlin DSL, GitHub Actions workflows, CI configuration, + plugin, extension, or task APIs, lazy configuration, task state, + configuration-cache behavior, TestKit coverage, consumer compatibility, + or publication metadata. --- # Gradle engineering @@ -23,6 +24,13 @@ task state, consumer compatibility, and cross-platform behavior as public contra as the source of truth for Gradle implementation and verification policy. 5. Clarify only material ambiguity in the public DSL, compatibility floor, or test target. +## Preserve CI behavior + +- Read each changed workflow and the Gradle configuration it invokes in full. +- Preserve least-privilege permissions, supported operating-system coverage, + compatibility toolchains, and wrapper-based execution unless the request changes them. +- Keep CI commands aligned with local verification and publication tasks. + ## Cross-domain work - Apply [`kotlin-engineer`](../kotlin-engineer/SKILL.md) to Kotlin source and build logic. diff --git a/.agents/skills/gradle-engineer/agents/openai.yaml b/.agents/skills/gradle-engineer/agents/openai.yaml index c39a294..665a83e 100644 --- a/.agents/skills/gradle-engineer/agents/openai.yaml +++ b/.agents/skills/gradle-engineer/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Gradle Engineer" - short_description: "Build robust Gradle plugin and build logic" - default_prompt: "Use $gradle-engineer to implement this Gradle plugin or build-logic change." + short_description: "Build robust Gradle plugin, build logic, and CI" + default_prompt: "Use $gradle-engineer to implement this Gradle, build-logic, or CI change." diff --git a/.agents/skills/test-engineer/references/test-patterns.md b/.agents/skills/test-engineer/references/test-patterns.md index 8842f36..335003c 100644 --- a/.agents/skills/test-engineer/references/test-patterns.md +++ b/.agents/skills/test-engineer/references/test-patterns.md @@ -126,8 +126,8 @@ A test passing on the build JDK alone does not prove Java 17 consumer compatibil Prefer this order: ```bash -./gradlew :gradle-plugin:test --tests io.spine.embedcode.gradle.SubjectSpec -./gradlew :gradle-plugin:functionalTest --tests io.spine.embedcode.gradle.PluginSpec +./gradlew :gradle-plugin:test --tests io.spine.embedcode.gradle.EmbedCodeVersionSpec +./gradlew :gradle-plugin:functionalTest --tests io.spine.embedcode.gradle.EmbedCodePluginSpec ./gradlew check ``` diff --git a/.agents/skills/writer/SKILL.md b/.agents/skills/writer/SKILL.md index 4c24651..0485284 100644 --- a/.agents/skills/writer/SKILL.md +++ b/.agents/skills/writer/SKILL.md @@ -45,18 +45,9 @@ Read the [project context](../../../PROJECT.md) and the ## Write Kotlin documentation -- Give every named type useful KDoc, regardless of visibility. Cover classes, objects, - companion objects, interfaces, enums, annotation classes, type aliases, and sealed types. -- Give every public API useful KDoc, including public constructors, functions, - properties, constants, and nested declarations. -- Document private members only when their intent, invariant, lifecycle, side effect, - failure mode, or trust boundary is not obvious from the code. -- Start with a short behavioral summary. Explain contracts and reasons, not syntax. -- Use KDoc links such as `[EmbedCodeTask]` for code symbols. -- Use `@param`, `@return`, and `@throws` only when they add information - that the signature and summary do not provide. -- Preserve useful existing constraints and explanatory comments when restructuring prose. -- Reject boilerplate that restates a declaration, assignment, or obvious return value. +Use the [KDoc policy](../kotlin-engineer/references/kotlin-policy.md#kdoc) as the source of +truth for Kotlin documentation, and apply +[kotlin-engineer](../kotlin-engineer/SKILL.md) to affected Kotlin source. ## Write operational text From bc03aca0bdd870ffb485763e4150776630ebfcba Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 24 Jul 2026 10:12:24 +0200 Subject: [PATCH 07/11] Improve style. --- .agents/skills/kotlin-engineer/SKILL.md | 2 +- .agents/skills/test-engineer/SKILL.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.agents/skills/kotlin-engineer/SKILL.md b/.agents/skills/kotlin-engineer/SKILL.md index 5d10e6c..e7cf06e 100644 --- a/.agents/skills/kotlin-engineer/SKILL.md +++ b/.agents/skills/kotlin-engineer/SKILL.md @@ -1,6 +1,6 @@ --- name: kotlin-engineer -description: > +description: >- Use for implementing, refactoring, explaining, or reviewing Kotlin source and Kotlin DSL files (`.kt` and `.kts`) in this repository, especially public plugin APIs, Gradle task types, extensions, and build logic. diff --git a/.agents/skills/test-engineer/SKILL.md b/.agents/skills/test-engineer/SKILL.md index a66b6d8..ea0f71c 100644 --- a/.agents/skills/test-engineer/SKILL.md +++ b/.agents/skills/test-engineer/SKILL.md @@ -1,6 +1,6 @@ --- name: test-engineer -description: > +description: >- Use for adding, changing, diagnosing, or reviewing Kotlin tests in this repository, including JUnit Jupiter unit specs, Gradle TestKit functional specs, bug reproductions, and security or compatibility regressions. From 97c8a9ab03869bca1fd44ab0011d7792e729fe15 Mon Sep 17 00:00:00 2001 From: Vladyslav Kuksiuk Date: Fri, 24 Jul 2026 11:06:01 +0200 Subject: [PATCH 08/11] Provide skills validation wrkflows. --- .github/workflows/check.yml | 19 + .gitignore | 2 + AGENTS.md | 4 + PROJECT.md | 8 +- scripts/check_agent_config.py | 681 +++++++++++++++++++++++ scripts/tests/test_check_agent_config.py | 317 +++++++++++ 6 files changed, 1028 insertions(+), 3 deletions(-) create mode 100644 scripts/check_agent_config.py create mode 100644 scripts/tests/test_check_agent_config.py diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 87eb376..732e4f0 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -11,6 +11,25 @@ concurrency: cancel-in-progress: true jobs: + agents-lint: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + + - name: Set Up Python 3.14 + uses: actions/setup-python@v7 + with: + python-version: "3.14" + + - name: Test Agent Configuration Validator + run: python -m unittest discover -s scripts/tests -p "test_*.py" + + - name: Validate Agent Configuration + run: python scripts/check_agent_config.py + build: strategy: fail-fast: false diff --git a/.gitignore b/.gitignore index f29958d..843673d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ .idea/ *.iml **/build/ +__pycache__/ +*.py[cod] local.properties diff --git a/AGENTS.md b/AGENTS.md index bb7d960..6b0156b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,6 +60,10 @@ Combine skills only when their scopes overlap: ## Verification +- After changing agent documentation, routes, or skills, run + `python3 scripts/check_agent_config.py`. +- After changing the validator, run + `python3 -m unittest discover -s scripts/tests -p 'test_*.py'`. - Start with the narrowest Gradle task or test that proves the change. - Run `./gradlew check` before handing off a completed code or test change unless the task is documentation-only or a narrower check is explicitly sufficient. diff --git a/PROJECT.md b/PROJECT.md index 1dd55f8..85db818 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -27,7 +27,8 @@ Embed Code configuration file and do not need to install the executable or Kotli - `gradle-plugin/src/main/templates/`: generated default-version source template. - `gradle-plugin/src/test/kotlin/`: focused unit specifications. - `gradle-plugin/src/functionalTest/kotlin/`: TestKit consumer-build specifications. -- `.github/workflows/check.yml`: Ubuntu and Windows build verification. +- `scripts/check_agent_config.py` and `scripts/tests/`: deterministic validation and tests. +- `.github/workflows/check.yml`: agent configuration and Ubuntu and Windows build verification. - `.agents/skills/`: repository engineering, test, writing, review, and security workflows. ## Runtime flow @@ -68,8 +69,9 @@ version numbers into agent guidance where a durable source path is sufficient. - Keep tests offline with deterministic fixtures, temporary directories, and loopback HTTP servers. - Use `./gradlew test` for unit tests, `./gradlew functionalTest` for TestKit tests, and `./gradlew check` for both plus plugin validation. -- CI runs the build and publishes the plugin to Maven Local on Ubuntu and Windows. Preserve - cross-platform paths, permissions, line endings, and process behavior. +- CI validates agent configuration on Ubuntu and independently runs the build and publishes + the plugin to Maven Local on Ubuntu and Windows. Preserve cross-platform paths, permissions, + line endings, and process behavior. ## Trust boundaries diff --git a/scripts/check_agent_config.py b/scripts/check_agent_config.py new file mode 100644 index 0000000..0a51f06 --- /dev/null +++ b/scripts/check_agent_config.py @@ -0,0 +1,681 @@ +#!/usr/bin/env python3 +"""Validate agent skills, routes, links, anchors, and Markdown formatting.""" + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path +from urllib.parse import unquote, urlsplit + + +MAX_MARKDOWN_LINE_LENGTH = 100 +SKILLS_DIRECTORY = Path(".agents/skills") +AGENT_DOCUMENTS = ( + Path("AGENTS.md"), + Path("PROJECT.md"), + Path("CLAUDE.md"), + Path(".github/copilot-instructions.md"), +) +SKILL_ROUTE_PATTERN = re.compile( + r"^\s*-\s+\[`(?P