Agent skill

Java Patterns

by softspark in softspark/ai-toolkit

Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads.

Apache-2.0Auto-check passedBackend & APIs

Install Java Patterns

skills CLI
$ npx skills add softspark/ai-toolkit --skill java-patterns -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install softspark/ai-toolkit java-patterns --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/softspark/ai-toolkit.git skills-src && mkdir -p .claude/skills && cp -r skills-src/app/skills/java-patterns .claude/skills/java-patterns && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
java-patterns
GitHub stars
179
Token cost
~3.4k tokens
SKILL.md length
272 words
Files
1
Skills in repo
112
Repo updated
First seen
Licence
Apache-2.0

At a glance

Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads.

  • Tasks that involve ORMs and data access
  • SKILL.md covers Project Structure, Idioms / Code Style, Error Handling and Testing Patterns, plus 3 more sections
  • Calls java and mvn
  • Tasks that involve Backend development

What it does

Java Patterns is an agent skill from softspark/ai-toolkit. Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class.

Its SKILL.md is about 3.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Backend & APIs, covering ORMs and data access and Backend development. It works with Java, Gradle and Spring Boot. The repository describes itself as: Professional-grade AI coding toolkit: 94 skills, 44 agents, multi-platform (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo Code, Aider, Augment, Antigravity, Codex CLI… The licence is Apache-2.0.

When your agent uses it

  • Tasks that involve ORMs and data access
  • Tasks that involve Backend development

Example prompts

  • “/java-patterns”

Requirements

  • Pre-approved tools (allowed-tools): Read

What it can do on your machine

Read from SKILL.md and the folder at commit d64db2b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves these tools, so the agent can use them without asking each time:

    • Read

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • java
    • mvn

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Java Patterns loads about 3.4k tokens when it runs. Until then it costs about 48 tokens; SKILL.md has 272 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~48
When it runs · the whole SKILL.md, loaded when a task matches
~3.4k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from softspark/ai-toolkit at commit d64db2b, republished under its Apache-2.0 licence (© softspark). 272 words, ~3,430 tokens.

Download SKILL.mdSave it as .claude/skills/java-patterns/SKILL.md (or your agent's skills folder).
name
java-patterns
description
Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class.
allowed-tools
Read
effort
medium
user-invocable
false

Java Patterns Skill

Project Structure

Maven / Gradle Standard Layout
my-app/
├── pom.xml (or build.gradle.kts + settings.gradle.kts)
├── src/
│   ├── main/
│   │   ├── java/com/example/myapp/
│   │   │   ├── MyApplication.java
│   │   │   ├── config/
│   │   │   ├── controller/
│   │   │   ├── service/
│   │   │   ├── repository/
│   │   │   ├── model/
│   │   │   │   ├── entity/
│   │   │   │   └── dto/
│   │   │   └── exception/
│   │   └── resources/
│   │       ├── application.yml
│   │       └── db/migration/
│   └── test/
│       ├── java/com/example/myapp/
│       └── resources/application-test.yml
└── target/ (or build/)
Multi-Module
parent/
├── pom.xml              (packaging=pom)
├── common/              (shared utilities)
├── domain/              (entities, business rules)
├── api/                 (REST controllers, DTOs)
└── app/                 (Spring Boot main, wiring)

Idioms / Code Style

Records (Java 16+)
java
public record UserDto(Long id, String name, String email) {
    public UserDto {  // compact constructor for validation
        Objects.requireNonNull(name, "name must not be null");
        Objects.requireNonNull(email, "email must not be null");
    }
}
Sealed Classes (Java 17+)
java
public sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}
public record Circle(double radius) implements Shape {
    public double area() { return Math.PI * radius * radius; }
}
public record Rectangle(double w, double h) implements Shape {
    public double area() { return w * h; }
}
public record Triangle(double base, double height) implements Shape {
    public double area() { return 0.5 * base * height; }
}
Switch Expressions (Java 14+)
java
String describe(Shape shape) {
    return switch (shape) {
        case Circle c    -> "Circle r=" + c.radius();
        case Rectangle r -> "Rect %sx%s".formatted(r.w(), r.h());
        case Triangle t  -> "Triangle base=" + t.base();
    };
}
// Guard patterns (Java 21+)
String classify(Shape shape) {
    return switch (shape) {
        case Circle c when c.radius() > 100 -> "large circle";
        case Circle c                        -> "small circle";
        case Rectangle r                     -> "rectangle";
        case Triangle t                      -> "triangle";
    };
}
var, Streams, Optional
java
// var -- use when RHS makes type obvious
var users = new ArrayList<User>();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Avoid: var result = service.process(data); -- type unclear

// Streams
List<String> names = users.stream()
    .filter(User::isActive)
    .map(User::name)
    .sorted()
    .toList();  // Java 16+, unmodifiable

Map<Department, List<User>> byDept = users.stream()
    .collect(Collectors.groupingBy(User::department));

// Optional -- return type only, never as field or parameter
String city = findByEmail(email)
    .map(User::address)
    .map(Address::city)
    .orElse("Unknown");
// Never call .get() without guard -- use orElse/orElseThrow

// Text blocks (Java 15+)
String json = """
    {"name": "%s", "email": "%s"}
    """.formatted(name, email);

Error Handling

TypeWhenExamples
CheckedRecoverable I/O the caller must handleIOException, SQLException
UncheckedProgramming errors, business rule violationsIllegalArgumentException, custom domain exceptions
Custom Exception Hierarchy
java
public abstract class DomainException extends RuntimeException {
    private final String errorCode;
    protected DomainException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public String errorCode() { return errorCode; }
}

public class EntityNotFoundException extends DomainException {
    public EntityNotFoundException(String entity, Object id) {
        super("NOT_FOUND", "%s with id %s not found".formatted(entity, id));
    }
}
Try-With-Resources
java
try (var conn = dataSource.getConnection();
     var stmt = conn.prepareStatement(sql);
     var rs = stmt.executeQuery()) {
    while (rs.next()) { results.add(mapRow(rs)); }
}
Global Handler (Spring)
java
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(EntityNotFoundException ex) {
        return ResponseEntity.status(404).body(new ErrorResponse(ex.errorCode(), ex.getMessage()));
    }
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
        var errors = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage()).toList();
        return ResponseEntity.badRequest().body(new ErrorResponse("VALIDATION_ERROR", errors.toString()));
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneric(Exception ex) {
        log.error("Unhandled exception", ex);
        return ResponseEntity.internalServerError()
            .body(new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred"));
    }
}
public record ErrorResponse(String code, String message) {}

Testing Patterns

JUnit 5 + Mockito + AssertJ
java
@DisplayName("UserService")
class UserServiceTest {
    private UserRepository repository;
    private UserService service;

    @BeforeEach
    void setUp() {
        repository = mock(UserRepository.class);
        service = new UserService(repository);
    }

    @Test
    void createsUserWithValidData() {
        var request = new CreateUserRequest("Alice", "alice@example.com");
        when(repository.save(any())).thenAnswer(inv -> inv.getArgument(0));

        var user = service.createUser(request);

        assertThat(user.name()).isEqualTo("Alice");
        verify(repository).save(any(User.class));
    }

    @Test
    void throwsWhenEmailExists() {
        when(repository.existsByEmail("taken@test.com")).thenReturn(true);
        assertThatThrownBy(() -> service.createUser(new CreateUserRequest("Bob", "taken@test.com")))
            .isInstanceOf(BusinessRuleViolationException.class)
            .hasMessageContaining("email already exists");
    }
}
Parameterized Tests
java
@ParameterizedTest
@CsvSource({"1,1,2", "0,0,0", "-1,1,0", "100,200,300"})
void addReturnsSumOfArguments(int a, int b, int expected) {
    assertThat(calculator.add(a, b)).isEqualTo(expected);
}

@ParameterizedTest
@MethodSource("invalidEmails")
void rejectsInvalidEmail(String email) {
    assertThatThrownBy(() -> new Email(email)).isInstanceOf(IllegalArgumentException.class);
}
static Stream<String> invalidEmails() {
    return Stream.of("", "no-at-sign", "@no-local", "spaces in@email.com");
}
Mockito Extras
java
// Argument captor
var captor = ArgumentCaptor.forClass(User.class);
verify(repository).save(captor.capture());
assertThat(captor.getValue().name()).isEqualTo("Alice");

// BDD style
given(repository.findById(1L)).willReturn(Optional.of(user));
then(repository).should().findById(1L);
Testcontainers (Integration)
java
@Testcontainers
@SpringBootTest
class UserRepositoryIT {
    @Container
    static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16-alpine");

    @DynamicPropertySource
    static void props(DynamicPropertyRegistry r) {
        r.add("spring.datasource.url", pg::getJdbcUrl);
        r.add("spring.datasource.username", pg::getUsername);
        r.add("spring.datasource.password", pg::getPassword);
    }

    @Autowired UserRepository repository;

    @Test
    void savesAndRetrievesUser() {
        var saved = repository.save(new User("Alice", "alice@test.com"));
        assertThat(repository.findById(saved.getId())).isPresent()
            .get().extracting("name").isEqualTo("Alice");
    }
}

Common Frameworks

Spring Boot (Controller / Service / Repository)
java
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;

    @GetMapping
    public List<UserDto> list(@RequestParam(defaultValue = "0") int page,
                              @RequestParam(defaultValue = "20") int size) {
        return userService.list(PageRequest.of(page, size));
    }

    @PostMapping @ResponseStatus(HttpStatus.CREATED)
    public UserDto create(@Valid @RequestBody CreateUserRequest request) {
        return userService.create(request);
    }
}

@Service @Transactional(readOnly = true) @RequiredArgsConstructor
public class UserService {
    private final UserRepository repository;
    private final UserMapper mapper;

    @Transactional
    public UserDto create(CreateUserRequest request) {
        if (repository.existsByEmail(request.email()))
            throw new BusinessRuleViolationException("email already exists");
        return mapper.toDto(repository.save(mapper.toEntity(request)));
    }
}

public interface UserRepository extends JpaRepository<User, Long> {
    boolean existsByEmail(String email);
    @Query("SELECT u FROM User u WHERE u.active = true AND u.role = :role")
    List<User> findActiveByRole(@Param("role") Role role);
}
Quarkus

Uses Jakarta REST (@Path, @GET, @POST) + CDI (@Inject, @ApplicationScoped). Panache simplifies JPA: implements PanacheRepository<User> gives find(), persist(), list() out of the box. Use @Transactional on mutating endpoints.

Jackson, Lombok, MapStruct
java
// Jackson -- snake_case response
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record ApiResponse<T>(T data, @JsonInclude(Include.NON_NULL) String error) {}

// Lombok -- JPA entities only (use records for DTOs)
@Entity @Getter @Setter @NoArgsConstructor(access = PROTECTED)
@Builder @ToString(exclude = "password") @EqualsAndHashCode(of = "id")
public class User {
    @Id @GeneratedValue(strategy = IDENTITY) private Long id;
    private String name;
    private String email;
    private String password;
}

// MapStruct
@Mapper(componentModel = "spring")
public interface UserMapper {
    UserDto toDto(User entity);
    User toEntity(CreateUserRequest request);
}

Performance Tips

JVM Tuning
bash
# Container-friendly (Java 17+)
java -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -jar app.jar

# GC: -XX:+UseZGC (low latency) | -XX:+UseG1GC (balanced, default) | -XX:+UseParallelGC (throughput)
Virtual Threads (Java 21+)
java
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    var futures = urls.stream().map(url -> executor.submit(() -> fetch(url))).toList();
    var results = futures.stream().map(f -> { try { return f.get(); }
        catch (Exception e) { throw new RuntimeException(e); } }).toList();
}
// Spring Boot 3.2+: spring.threads.virtual.enabled=true
Profiling with JFR
bash
java -XX:StartFlightRecording=duration=60s,filename=profile.jfr -jar app.jar
jfr print --events jdk.CPULoad,jdk.GCHeapSummary profile.jfr
Common Pitfalls
PitfallFix
String concat in loopStringBuilder or String.join()
Unbounded cachesCaffeine with maximumSize + expireAfterWrite
Autoboxing in hot pathPrimitive streams (mapToInt) or primitive collections
Synchronized everythingConcurrentHashMap, StampedLock, virtual threads
Reflection in tight loopCache MethodHandle or use code generation

Build / Package Management

Maven (key elements)
xml
<properties>
  <java.version>21</java.version>
</properties>
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.3.0</version>
</parent>
<!-- Use dependencyManagement + BOM for non-Boot projects -->
Gradle Kotlin DSL
kotlin
plugins { java; id("org.springframework.boot") version "3.3.0" }
java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.test { useJUnitPlatform() }
Dependency Management
ConcernApproach
Version alignmentBOM import via dependencyManagement / platform()
Vulnerability scanOWASP plugin (dependencyCheckAnalyze)
Unused depsmvn dependency:analyze or Gradle dependency-analysis plugin
Reproducible buildsPin plugin versions, maven-enforcer-plugin

Anti-Patterns

Anti-PatternProblemSolution
God service class1000+ linesSplit by domain concern
Anemic domain modelLogic only in servicesPut behavior on domain objects
Catching ExceptionHides bugsCatch specific types
@Autowired on fieldsHidden deps, untestableConstructor injection
Mutable DTOsThread-safety issuesUse records
Raw JDBC everywhereInjection risk, boilerplateJPA/jOOQ with parameterized queries
Missing @TransactionalInconsistent dataAnnotate service methods
N+1 queriesPerformance deathJOIN FETCH or @EntityGraph

© softspark, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in app/skills/java-patterns of softspark/ai-toolkit.

Open the folder on GitHubat commit d64db2b

Compare with similar skills

Java Patterns next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Java Patterns compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Java Patterns this skillsoftspark/ai-toolkit179—~3.4kAutomated safety check: PassApache-2.0
Java ArchitectJeffallan/claude-skills12k—~1.5kAutomated safety check: PassMIT
Grails Developer Guideapache/grails-core2.9k—~4.9kAutomated safety check: PassApache-2.0
Progensivaprasadreddy/sivalabs-agent-skills188—~2.7kAutomated safety check: NotesMIT
Starter Domain Revieweraxelixlabs/axelix148—~3.3kAutomated safety check: PassLGPL-3.0
Spring Mvc To Boot MigratorArabelaTso/Skills-4-SE253—~2.2kAutomated safety check: PassApache-2.0

Similar skills

  • Java Architect

    Jeffallan/claude-skills

    Builds Spring Boot 3.x services on Java 21 with domain-driven design, WebFlux, JPA tuning and Spring Security using OAuth2 and JWT, verified by Maven or Gradle builds.

    12k GitHub stars~1.5k tokensUpdated 4 days ago
    Backend & APIsAuto-check passed
  • Grails Developer Guide

    apache/grails-core

    Guides building Grails web applications and REST APIs with GORM, controllers, services, views, plugins and Spock and Geb testing.

    2.9k GitHub stars~4.9k tokensUpdated today
    Backend & APIsAuto-check passed
  • Progen

    sivaprasadreddy/sivalabs-agent-skills

    A skill your agent uses when the user wants to create/generate/scaffold a new Spring Boot project (Maven or Gradle, REST API / Web App / Spring Boot + Angular full stack).

    188 GitHub stars~2.7k tokensUpdated 3 days ago
    Backend & APIsAuto-check: notes
  • Starter Domain Reviewer

    axelixlabs/axelix

    Reviews changes in sbs/starter-domain for technology-agnostic domain logic, forbidden production dependencies, and Java 11+ compatibility.

    148 GitHub stars~3.3k tokensUpdated today
    Backend & APIsAuto-check passed
  • Spring Mvc To Boot Migrator

    ArabelaTso/Skills-4-SE

    Automatically migrate Spring MVC applications to Spring Boot.

    253 GitHub stars~2.2k tokensUpdated 1 mo ago
    Backend & APIsAuto-check passed
  • Spring Boot Engineer

    Jeffallan/claude-skills

    Builds Spring Boot 3.x services with REST controllers, Spring Data JPA, Spring Security 6, WebFlux and Spring Cloud, verified by tests and Actuator health checks.

    12k GitHub stars~1.9k tokensUpdated 4 days ago
    Backend & APIsAuto-check passed

More from softspark/ai-toolkit

All 112 skills in this repo
  • Prepare Test Env

    softspark/ai-toolkit

    Prepare or verify a project QA environment with source identity, readiness, browser access, evidence paths and owned cleanup.

    179 GitHub stars~1.8k tokensUpdated yesterday
    Auto-check: notes
  • A11y Validate

    softspark/ai-toolkit

    Accessibility validator: WCAG 2.1 AA, EN 301 549, EAA. An agent skill from softspark/ai-toolkit.

    179 GitHub stars~3.8k tokensUpdated yesterday
    Auto-check: notes
  • Analyze

    softspark/ai-toolkit

    Analyzes code quality, complexity, patterns across codebase.

    179 GitHub stars~1k tokensUpdated yesterday
    Auto-check passed
  • Autonomous Dev

    softspark/ai-toolkit

    Drives a brief, specification, issue or existing PR through implementation, review, tests and QA to a ready PR.

    179 GitHub stars~2.6k tokensUpdated yesterday
    Auto-check: notes
  • Brand Voice

    softspark/ai-toolkit

    Direct technical voice for docs, README, user-facing text. An agent skill from softspark/ai-toolkit.

    179 GitHub stars~2.1k tokensUpdated yesterday
    Auto-check passed
  • CI

    softspark/ai-toolkit

    Detect/generate/debug CI pipeline config (GitHub Actions, GitLab CI).

    179 GitHub stars~1.1k tokensUpdated yesterday
    Auto-check: notes

Questions about Java Patterns

What does Java Patterns do?

Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Java Patterns is an agent skill from softspark/ai-toolkit. Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads.

When should I use Java Patterns?

Java Patterns fits situations like: tasks that involve ORMs and data access; tasks that involve Backend development.

How do I install Java Patterns in Claude Code?

Run `npx skills add softspark/ai-toolkit --skill java-patterns -a claude-code`. Or copy the skill folder (app/skills/java-patterns in softspark/ai-toolkit) into .claude/skills/java-patterns in your project. Claude Code loads it when a task matches its description.

How do I install Java Patterns in Codex?

Run `npx skills add softspark/ai-toolkit --skill java-patterns -a codex`. Or copy the skill folder (app/skills/java-patterns in softspark/ai-toolkit) into .agents/skills/java-patterns in your project. Codex loads it when a task matches its description.

Can I use Java Patterns in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add softspark/ai-toolkit --skill java-patterns -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/java-patterns, .gemini/skills/java-patterns, .github/skills/java-patterns and .opencode/skills/java-patterns in your project.

What does Java Patterns need to run?

Going by SKILL.md and its folder, Java Patterns needs the command-line tools its instructions call (java and mvn). Its frontmatter pre-approves these tools: Read.

Does Java Patterns access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Java Patterns safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Java Patterns use?

Java Patterns is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Java Patterns use?

About 3.4k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Java Patterns?

Skills that share tags, products or a category with Java Patterns: Java Architect (Jeffallan/claude-skills, 12k stars), Grails Developer Guide (apache/grails-core, 2.9k stars), Progen (sivaprasadreddy/sivalabs-agent-skills, 188 stars) and Starter Domain Reviewer (axelixlabs/axelix, 148 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Java Patterns?

softspark (a GitHub user) maintains it in softspark/ai-toolkit, which has 179 GitHub stars. The repository holds 112 skills in this directory. The repository was last updated on October 7, 2026.

Source: softspark/ai-toolkit on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.