Skip to content

Commit cce9081

Browse files
authored
Release/2.0.2 (#25)
1 parent cdfb199 commit cce9081

25 files changed

Lines changed: 862 additions & 75 deletions

.claude/CLAUDE.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Project
2+
3+
PHP library (tiny-blocks ecosystem). Self-contained package: immutable models, zero infrastructure
4+
dependencies in core, small public surface area. Public API at `src/` root; implementation details
5+
under `src/Internal/`.
6+
7+
## Rules
8+
9+
All coding standards, architecture, naming, testing, and documentation conventions
10+
are defined in `rules/`. Read the applicable rule files before generating any code or documentation.
11+
12+
## Commands
13+
14+
- `make test` — run tests with coverage.
15+
- `make mutation-test` — run mutation testing (Infection).
16+
- `make review` — run lint.
17+
- `make help` — list all available commands.
18+
19+
## Post-change validation
20+
21+
After any code change, run `make review`, `make test`, and `make mutation-test`.
22+
If any fails, iterate on the fix while respecting all project rules until all pass.
23+
Never deliver code that breaks lint, tests, or leaves surviving mutants.
24+
25+
## File formatting
26+
27+
Every file produced or modified must:
28+
29+
- Use **LF** line endings. Never CRLF.
30+
- Have no trailing whitespace on any line.
31+
- End with a single trailing newline.
32+
- Have no consecutive blank lines (max one blank line between blocks).

.claude/rules/github-workflows.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
description: Naming, ordering, inputs, security, and structural rules for all GitHub Actions workflow files.
3+
paths:
4+
- ".github/workflows/**/*.yml"
5+
- ".github/workflows/**/*.yaml"
6+
---
7+
8+
# Workflows
9+
10+
Structural and stylistic rules for GitHub Actions workflow files. Refer to `shell-scripts.md` for Bash conventions used
11+
inside `run:` steps, and to `terraforms.md` for Terraform conventions used in `terraform/`.
12+
13+
## Pre-output checklist
14+
15+
Verify every item before producing any workflow YAML. If any item fails, revise before outputting.
16+
17+
1. File name follows the convention: `ci-<runtime>.yml` for reusable CI, `cd-<purpose>.yml` for dispatch CD.
18+
2. `name` field follows the pattern `CI — <Context>` or `CD — <Context>`, using sentence case after the dash
19+
(e.g., `CD — Run migration`, not `CD — Run Migration`).
20+
3. Reusable workflows use `workflow_call` trigger. CD workflows use `workflow_dispatch` trigger.
21+
4. Each workflow has a single responsibility. CI tests code. CD deploys it. Never combine both.
22+
5. Every input has a `description` field. Descriptions use American English and end with a period.
23+
6. Input names use `kebab-case`: `service-name`, `dry-run`, `skip-build`.
24+
7. Inputs are ordered: required first, then optional. Each group by **name length ascending**.
25+
8. Choice input options are in **alphabetical order**.
26+
9. `env`, `outputs`, and `with` entries are ordered by **key length ascending**.
27+
10. `permissions` keys are ordered by **key length ascending** (`contents` before `id-token`).
28+
11. Top-level workflow keys follow canonical order: `name`, `on`, `concurrency`, `permissions`, `env`, `jobs`.
29+
12. Job-level properties follow canonical order: `if`, `name`, `needs`, `uses`, `with`, `runs-on`,
30+
`environment`, `timeout-minutes`, `strategy`, `outputs`, `permissions`, `env`, `steps`.
31+
13. All other YAML property names within a block are ordered by **name length ascending**.
32+
14. Jobs follow execution order: `load-config``lint``test``build``deploy`.
33+
15. Step names start with a verb and use sentence case: `Setup PHP`, `Run lint`, `Resolve image tag`.
34+
16. Runtime versions are resolved from the service repo's native dependency file (`composer.json`, `go.mod`,
35+
`package.json`). No version is hardcoded in any workflow.
36+
17. Service-specific overrides live in a pipeline config file (e.g., `.pipeline.yml`) in the service repo,
37+
not in the workflows repository.
38+
18. The `load-config` job reads the pipeline config file at runtime with safe fallback to defaults when absent.
39+
19. Top-level `permissions` defaults to read-only (`contents: read`). Jobs escalate only the permissions they
40+
need.
41+
20. AWS authentication uses OIDC federation exclusively. Static access keys are forbidden.
42+
21. Secrets are passed via `secrets: inherit` from callers. No secret is hardcoded.
43+
22. Sensitive values fetched from SSM are masked with `::add-mask::` before assignment.
44+
23. Third-party actions are pinned to the latest available full commit SHA with a version comment:
45+
`uses: aws-actions/configure-aws-credentials@<latest-sha> # v4.0.2`. Always verify the latest
46+
version before generating a workflow.
47+
24. First-party actions (`actions/*`) are pinned to the latest major version tag available:
48+
`actions/checkout@v4`. Always check for the most recent major version before generating a workflow.
49+
25. Production deployments require GitHub Environments protection rules (manual approval).
50+
26. Every job sets `timeout-minutes` to prevent indefinite hangs. CI jobs: 10–15 minutes. CD jobs: 20–30
51+
minutes. Adjust only with justification in a comment.
52+
27. CI workflows set `concurrency` with `group` scoped to the PR and `cancel-in-progress: true` to avoid
53+
redundant runs.
54+
28. CD workflows set `concurrency` with `group` scoped to the environment and `cancel-in-progress: false` to
55+
prevent interrupted deployments.
56+
29. CD workflows use `if: ${{ !cancelled() }}` to allow to deploy after optional build steps.
57+
30. Inline logic longer than 3 lines is extracted to a script in `scripts/ci/` or `scripts/cd/`.
58+
59+
## Style
60+
61+
- All text (workflow names, step names, input descriptions, comments) uses American English with correct
62+
spelling and punctuation. Sentences and descriptions end with a period.
63+
64+
## Callers
65+
66+
- Callers trigger on `pull_request` targeting `main` only. No `push` trigger.
67+
- Callers in service repos are static (~10 lines) and pass only `service-name` or `app-name`.
68+
- Callers reference workflows with `@main` during development. Pin to a tag or SHA for production.
69+
70+
## Image tagging
71+
72+
- CD deploy builds: `<environment>-sha-<short-hash>` + `latest`.
73+
74+
## Migrations
75+
76+
- Migrations run **before** service deployment (schema first, code second).
77+
- `cd-migrate.yml` supports `dry-run` mode (`flyway validate`) for pre-flight checks.
78+
- Database credentials are fetched from SSM at runtime, never stored in workflow files.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
description: Pre-output checklist, naming, typing, complexity, and PHPDoc rules for all PHP files in libraries.
3+
paths:
4+
- "src/**/*.php"
5+
- "tests/**/*.php"
6+
---
7+
8+
# Code style
9+
10+
Semantic code rules for all PHP files. Formatting rules (PSR-1, PSR-4, PSR-12, line length) are enforced by `phpcs.xml`
11+
and are not repeated here. Refer to `php-library-modeling.md` for library modeling rules.
12+
13+
## Pre-output checklist
14+
15+
Verify every item before producing any PHP code. If any item fails, revise before outputting.
16+
17+
1. `declare(strict_types=1)` is present.
18+
2. All classes are `final readonly` by default. Use `class` (without `final` or `readonly`) only when the class is
19+
designed as an extension point for consumers (e.g., `Collection`, `ValueObject`). Use `final class` without
20+
`readonly` only when the parent class is not readonly (e.g., extending a third-party abstract class).
21+
3. All parameters, return types, and properties have explicit types.
22+
4. Constructor property promotion is used.
23+
5. Named arguments are used at call sites for own code, tests, and third-party library methods (e.g., tiny-blocks).
24+
Never use named arguments on native PHP functions (`array_map`, `in_array`, `preg_match`, `is_null`,
25+
`iterator_to_array`, `sprintf`, `implode`, etc.) or PHPUnit assertions (`assertEquals`, `assertSame`,
26+
`assertTrue`, `expectException`, etc.).
27+
6. No `else` or `else if` exists anywhere. Use early returns, polymorphism, or map dispatch instead.
28+
7. No abbreviations appear in identifiers. Use `$index` instead of `$i`, `$account` instead of `$acc`.
29+
8. No generic identifiers exist. Use domain-specific names instead:
30+
`$data``$payload`, `$value``$totalAmount`, `$item``$element`,
31+
`$info``$currencyDetails`, `$result``$conversionOutcome`.
32+
9. No raw arrays exist where a typed collection or value object is available. Use the `tiny-blocks/collection`
33+
fluent API (`Collection`, `Collectible`) when data is `Collectible`. Use `createLazyFrom` when elements are
34+
consumed once. Raw arrays are acceptable only for primitive configuration data, variadic pass-through, and
35+
interop at system boundaries. See "Collection usage" below for the full rule and example.
36+
10. No private methods exist except private constructors for factory patterns. Inline trivial logic at the call site
37+
or extract it to a collaborator or value object.
38+
11. Members are ordered: constants first, then constructor, then static methods, then instance methods. Within each
39+
group, order by body size ascending (number of lines between `{` and `}`). Constants and enum cases, which have
40+
no body, are ordered by name length ascending.
41+
12. Constructor parameters are ordered by parameter name length ascending (count the name only, without `$` or type),
42+
except when parameters have an implicit semantic order (e.g., `$start/$end`, `$from/$to`, `$startAt/$endAt`),
43+
which takes precedence. Parameters with default values go last, regardless of name length. The same rule
44+
applies to named arguments at call sites.
45+
Example: `$id` (2) → `$value` (5) → `$status` (6) → `$precision` (9).
46+
13. Time and space complexity are first-class design concerns.
47+
- No `O(N²)` or worse time complexity exists unless the problem inherently requires it and the cost is
48+
documented in PHPDoc on the interface method.
49+
- Space complexity is kept minimal: prefer lazy/streaming pipelines (`createLazyFrom`) over materializing
50+
intermediate collections.
51+
- Never re-iterate the same source; fuse stages when possible.
52+
- Public interface methods document time and space complexity in Big O form (see "PHPDoc" section).
53+
14. No logic is duplicated across two or more places (DRY).
54+
15. No abstraction exists without real duplication or isolation need (KISS).
55+
16. All identifiers, comments, and documentation are written in American English.
56+
17. No justification comments exist (`// NOTE:`, `// REASON:`, etc.). Code speaks for itself.
57+
18. `// TODO: <reason>` is used when implementation is unknown, uncertain, or intentionally deferred.
58+
Never leave silent gaps.
59+
19. All class references use `use` imports at the top of the file. Fully qualified names inline are prohibited.
60+
20. No dead or unused code exists. Remove unreferenced classes, methods, constants, and imports.
61+
21. Never create public methods, constants, or classes in `src/` solely to serve tests. If production code does not
62+
need it, it does not exist.
63+
22. Always use the most current and clean syntax available in the target PHP version. Prefer match to switch,
64+
first-class callables over `Closure::fromCallable()`, readonly promotion over manual assignment, enum methods
65+
over external switch/if chains, named arguments over positional ambiguity (except where excluded by rule 5),
66+
and `Collection::map` over foreach accumulation.
67+
23. No vertical alignment of types in parameter lists or property declarations. Use a single space between
68+
type and variable name. Never pad with extra spaces to align columns:
69+
`public OrderId $id` — not `public OrderId $id`.
70+
24. Opening brace `{` follows PSR-12: on a **new line** for classes, interfaces, traits, enums, and methods
71+
(including constructors); on the **same line** for closures and control structures (`if`, `for`, `foreach`,
72+
`while`, `switch`, `match`, `try`).
73+
25. Never pass an argument whose value equals the parameter's default. Omit the argument entirely.
74+
Example — `toArray(KeyPreservation $keyPreservation = KeyPreservation::PRESERVE)`:
75+
`$collection->toArray(keyPreservation: KeyPreservation::PRESERVE)``$collection->toArray()`.
76+
Only pass the argument when the value differs from the default.
77+
26. No trailing comma in any multi-line list. This applies to parameter lists (constructors, methods,
78+
closures), argument lists at call sites, array literals, match arms, and any other comma-separated
79+
multi-line structure. The last element never has a comma after it. PHP accepts trailing commas in
80+
parameter lists, but this project prohibits them for visual consistency.
81+
Example — correct:
82+
```
83+
new Precision(
84+
value: 2,
85+
rounding: RoundingMode::HALF_UP
86+
);
87+
```
88+
Example — prohibited:
89+
```
90+
new Precision(
91+
value: 2,
92+
rounding: RoundingMode::HALF_UP,
93+
);
94+
```
95+
96+
## Casing conventions
97+
98+
- Internal code (variables, methods, classes): **`camelCase`**.
99+
- Constants and enum-backed values when representing codes: **`SCREAMING_SNAKE_CASE`**.
100+
101+
## Naming
102+
103+
- Names describe **what** in domain terms, not **how** technically: `$monthlyRevenue` instead of `$calculatedValue`.
104+
- Generic technical verbs are avoided. See `php-library-modeling.md` — Nomenclature.
105+
- Booleans use predicate form: `isActive`, `hasPermission`, `wasProcessed`.
106+
- Collections are always plural: `$orders`, `$lines`.
107+
- Methods returning bool use prefixes: `is`, `has`, `can`, `was`, `should`.
108+
109+
## Comparisons
110+
111+
1. Null checks: use `is_null($variable)`, never `$variable === null`.
112+
2. Empty string checks on typed `string` parameters: use `$variable === ''`. Avoid `empty()` on typed strings
113+
because `empty('0')` returns `true`.
114+
3. Mixed or untyped checks (value may be `null`, empty string, `0`, or `false`): use `empty($variable)`.
115+
116+
## American English
117+
118+
All identifiers, enum values, comments, and error codes use American English spelling:
119+
`canceled` (not `cancelled`), `organization` (not `organisation`), `initialize` (not `initialise`),
120+
`behavior` (not `behaviour`), `modeling` (not `modelling`), `labeled` (not `labelled`),
121+
`fulfill` (not `fulfil`), `color` (not `colour`).
122+
123+
## PHPDoc
124+
125+
- PHPDoc is restricted to interfaces only, documenting obligations, `@throws`, and complexity.
126+
- Never add PHPDoc to concrete classes.
127+
- Document `@throws` for every exception the method may raise.
128+
- Document time and space complexity in Big O form. When a method participates in a fused pipeline (e.g., collection
129+
pipelines), express cost as a two-part form: call-site cost + fused-pass contribution. Include a legend defining
130+
variables (e.g., `N` for input size, `K` for number of stages).
131+
132+
## Collection usage
133+
134+
When a property or parameter is `Collectible`, use its fluent API. Never break out to raw array functions such as
135+
`array_map`, `array_filter`, `iterator_to_array`, or `foreach` + accumulation. The same applies to `filter()`,
136+
`reduce()`, `each()`, and all other `Collectible` operations. Chain them fluently. Never materialize with
137+
`iterator_to_array` to then pass into a raw `array_*` function.
138+
139+
**Prohibited — `array_map` + `iterator_to_array` on a Collectible:**
140+
141+
```php
142+
$names = array_map(
143+
static fn(Element $element): string => $element->name(),
144+
iterator_to_array($collection)
145+
);
146+
```
147+
148+
**Correct — fluent chain with `map()` + `toArray()`:**
149+
150+
```php
151+
$names = $collection
152+
->map(transformations: static fn(Element $element): string => $element->name())
153+
->toArray(keyPreservation: KeyPreservation::DISCARD);
154+
```
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
description: Standards for README files and all project documentation in PHP libraries.
3+
paths:
4+
- "**/*.md"
5+
---
6+
7+
# Documentation
8+
9+
## README
10+
11+
1. Include an anchor-linked table of contents.
12+
2. Start with a concise one-line description of what the library does.
13+
3. Include a **badges** section (license, build status, coverage, latest version, PHP version).
14+
4. Provide an **Overview** section explaining the problem the library solves and its design philosophy.
15+
5. **Installation** section: Composer command (`composer require vendor/package`).
16+
6. **How to use** section: complete, runnable code examples covering the primary use cases. Each example
17+
includes a brief heading describing what it demonstrates.
18+
7. If the library exposes multiple entry points, strategies, or container types, document each with its own
19+
subsection and example.
20+
8. **FAQ** section: include entries for common pitfalls, non-obvious behaviors, or design decisions that users
21+
frequently ask about. Each entry is a numbered question as heading (e.g., `### 01. Why does X happen?`)
22+
followed by a concise explanation. Only include entries that address real confusion points.
23+
9. **License** and **Contributing** sections at the end.
24+
10. Write strictly in American English. See `php-library-code-style.md` American English section for spelling
25+
conventions.
26+
27+
## Structured data
28+
29+
1. When documenting constructors, factory methods, or configuration options with more than 3 parameters,
30+
use tables with columns: Parameter, Type, Required, Description.
31+
2. Prefer tables to prose for any structured information.
32+
33+
## Style
34+
35+
1. Keep language concise and scannable.
36+
2. Never include placeholder content (`TODO`, `TBD`).
37+
3. Code examples must be syntactically correct and self-contained.
38+
4. Code examples include every `use` statement needed to compile. Each example stands alone — copyable into
39+
a fresh file without modification.
40+
5. Do not document `Internal/` classes or private API. Only document what consumers interact with.

0 commit comments

Comments
 (0)