417d17478f
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 18s
CI / build (pull_request) Successful in 20s
CI / security (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 32s
CI / integration_tests (pull_request) Successful in 3m12s
CI / unit_tests (pull_request) Successful in 6m51s
CI / docker (pull_request) Successful in 15s
CI / benchmark-regression (pull_request) Successful in 16m15s
CI / coverage (pull_request) Successful in 23m13s
CI / lint (push) Successful in 13s
CI / quality (push) Successful in 18s
CI / build (push) Successful in 21s
CI / typecheck (push) Successful in 33s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 36s
CI / integration_tests (push) Successful in 2m33s
CI / unit_tests (push) Successful in 6m58s
CI / docker (push) Successful in 8s
CI / benchmark-publish (push) Successful in 10m17s
CI / coverage (push) Successful in 22m57s
1177 lines
66 KiB
Markdown
1177 lines
66 KiB
Markdown
# Contributing
|
|
|
|
[](http://commitizen.github.io/cz-cli/)
|
|
[](https://www.w3.org/submissions/semantic-versioning/)
|
|
[](https://matrix.to/#/#CleverThis:qoto.org)
|
|
|
|
When contributing to this repository, it is usually a good idea to first discuss the change you
|
|
wish to make via issue, email, or any other method with the owners of this repository before
|
|
making a change. This could potentially save a lot of wasted hours.
|
|
|
|
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
|
|
|
## File Organization
|
|
|
|
All project files should be organized in appropriate subdirectories rather than placed in the
|
|
project root. The root directory should be reserved for standard ecosystem files that
|
|
conventionally live there (e.g., project manifests, lock files, CI configurations,
|
|
`.gitignore`, `README`, `CONTRIBUTING`, `LICENSE`, and similar well-known files). Any file that
|
|
is not a widely recognized root-level convention belongs in a subdirectory.
|
|
|
|
Each directory should have a clear, single purpose — do not mix concerns. For example, source
|
|
code directories must not contain test files, documentation, mocking code, or examples. Test
|
|
directories must not contain production source code. Documentation, configuration, scripts, and
|
|
examples should each have their own designated locations.
|
|
|
|
### BDD Test Organization Guidelines
|
|
|
|
- **Group new steps with related ones.** Before adding a new step definition file, check for an
|
|
existing file that covers the same behavior and extend it instead of creating a duplicate.
|
|
- **Name feature-specific step files after their feature.** Steps used only by a particular
|
|
feature file should live in a correspondingly named step definition file.
|
|
- **Keep shared steps in purpose-driven modules.** Steps used across multiple features belong in
|
|
clearly named, reusable files; prefer updating an existing shared file when it already fits the
|
|
purpose before creating another one.
|
|
- **Ship features with complete implementations.** Every feature file must arrive with all of its
|
|
steps fully implemented — never add placeholder steps or commit a feature without its supporting
|
|
step code already in place.
|
|
|
|
## Development
|
|
|
|
### Testing
|
|
|
|
#### Testing Philosophy
|
|
|
|
- **Behavior-Driven Development (BDD):** All unit-level and scenario tests must follow the
|
|
Behavior-Driven Development approach using a framework that adheres to the Cucumber/Gherkin
|
|
standard. Feature files written in Gherkin syntax are the primary vehicle for expressing test
|
|
scenarios. Integration and end-to-end tests may use a separate framework suited to that purpose.
|
|
- **Prefer BDD Over xUnit:** Do not write xUnit-style unit tests (e.g., JUnit, pytest, NUnit,
|
|
xUnit.net). All unit-level tests should be expressed as BDD scenarios in Gherkin. This ensures
|
|
tests are readable, behavior-focused, and serve as living documentation.
|
|
- **Multi-Level Testing Mandate:** Every coding task must include or update tests at multiple
|
|
levels: unit tests, integration tests, and performance benchmarks. Testing is non-optional and
|
|
is part of the definition of done for any task.
|
|
- **Task Runner Execution:** Run all test suites exclusively through the project's designated task
|
|
runner. Do not invoke test frameworks directly from the command line. If the task runner is
|
|
missing a required session or dependency, add it to the task runner configuration before
|
|
proceeding.
|
|
- **Coverage Thresholds:** Test coverage must remain above the project-defined threshold at all
|
|
times. Coverage is measured as part of the standard test pipeline and enforced automatically.
|
|
- **Failure Capture and Remediation:** Any test failure during development immediately becomes a
|
|
blocking task. Document the failure context and resolve it before progressing to other work.
|
|
Never leave a known-failing test unaddressed.
|
|
- **Quality Gates:** Do not consider work complete until all tests pass, all documentation is
|
|
updated, and all associated quality checks (linting, type checking, security scanning, etc.)
|
|
succeed. All quality gates must pass before a task is marked done.
|
|
|
|
#### Running Tests
|
|
|
|
Tests are run through the project's task runner. Refer to the
|
|
[Project-Specific Guidelines](#project-specific-guidelines) section for exact commands and
|
|
session names.
|
|
|
|
### Commit Scope and Quality
|
|
|
|
A clean version control history is a cornerstone of maintainable, collaborative development.
|
|
Every commit should be an atomic, self-contained unit of change that can be understood, built,
|
|
tested, and if necessary reverted or cherry-picked with confidence. For a deeper discussion of
|
|
the principles behind these guidelines, see
|
|
[Writing High Quality, Well Scoped, Commits](https://jeffreyfreeman.me/blog/writing-high-quality-well-scoped-commits/).
|
|
|
|
#### Atomic Commits
|
|
|
|
- **One logical change per commit.** Each commit must encapsulate a single, focused change — one
|
|
bug fix, one refactoring, one feature increment. If you are fixing two separate bugs or adding
|
|
two independent features, use separate commits for each.
|
|
- **Related changes together, nothing more.** A commit should be a wrapper for related changes
|
|
only. If one logical change requires edits across multiple files, those edits belong in one
|
|
commit. Conversely, unrelated edits that happen to touch the same file should be split into
|
|
separate commits.
|
|
- **Do not mix concerns.** Never bundle cosmetic changes (reformatting, renaming, whitespace
|
|
fixes) with functional changes in the same commit. If you need to reformat code, do so in a
|
|
dedicated commit containing no semantic changes. Similarly, isolate large-scale code moves or
|
|
renames into their own commits — perform a pure move in one commit, then make modifications in
|
|
the next. This keeps diffs readable and preserves accurate file history.
|
|
|
|
#### Commit Completeness
|
|
|
|
- **Include tests with the change.** If your change introduces or modifies behavior, the tests
|
|
covering that behavior belong in the same commit. A feature and its tests are one logical unit
|
|
of work — they either both go in, or neither does. This ensures that anyone checking out any
|
|
commit in history can run the test suite and see it pass, including the new tests.
|
|
- **Include documentation with the change.** If your commit changes how something works, update
|
|
the relevant documentation (README, user guide, code comments, API docs) in the same commit.
|
|
Do not defer documentation to a later commit — if someone checked out your commit, they should
|
|
have everything needed to understand and use the change.
|
|
- **Update ancillary files.** Configuration, build scripts, changelogs, and other metadata
|
|
affected by the change should be updated in the same commit. The project should never be in a
|
|
state where the code says one thing but the documentation or configuration says another.
|
|
- **Do not commit half-done work.** Only commit when a piece of functionality is fully
|
|
implemented and tested. Incomplete code — partial features, failing tests, placeholder
|
|
implementations — does not belong in the commit history. Use feature branches or stashing to
|
|
save work in progress.
|
|
|
|
#### Build and Test Integrity
|
|
|
|
- **Each commit must build and pass all tests.** The repository should be in a working state at
|
|
every commit. If someone checks out any commit from history, the code must compile (or run) and
|
|
all tests must pass. This is essential for tools like `git bisect` and for safely reverting
|
|
individual commits.
|
|
- **Test before committing.** Run the project's test suite before each commit to verify nothing
|
|
is broken. Do not commit code you *think* is complete — verify it.
|
|
- **Self-review the diff.** Before committing, review the staged diff to confirm only the
|
|
intended changes are included and that no debugging statements, temporary code, or unrelated
|
|
edits have crept in.
|
|
|
|
#### Independence and Revertibility
|
|
|
|
- **Each commit must be cleanly revertible.** If a commit were reverted, the codebase should
|
|
still build correctly and make sense. This happens naturally when commits are atomic and
|
|
self-contained — reverting one change does not entangle unrelated code.
|
|
- **Minimize hidden interdependencies.** Commits should not secretly rely on subsequent commits
|
|
to function. If commit A prepares for commit B, A should stand on its own without leaving the
|
|
system in a broken state.
|
|
- **Keep history bisect-friendly.** Since each commit represents a valid, testable state of the
|
|
project, `git bisect` can reliably pinpoint where a regression was introduced. Broken
|
|
intermediate commits defeat this capability.
|
|
|
|
#### Commit Hygiene
|
|
|
|
- **Commit often to keep changes small.** Frequent, small commits are easier to review, easier
|
|
to merge, and easier to debug than infrequent large ones. It is always easier to squash
|
|
several small commits together than to split one large tangled commit apart.
|
|
- **Use interactive staging.** Use your version control's staging tools (e.g., `git add -p`) to
|
|
selectively stage changes, splitting mixed edits into separate focused commits.
|
|
- **Use topic branches.** Develop features and fixes on separate branches. This isolates work
|
|
until it is complete and prevents half-finished commits from entering the main history.
|
|
- **Clean up history before merging.** Use interactive rebase or amend to fix typos, consolidate
|
|
fixup commits, and polish the commit series before pushing to shared branches. The goal is a
|
|
tidy history where every commit is meaningful, not a trail of corrections patching the previous
|
|
commit.
|
|
- **Reference issues and tickets.** When a commit relates to a bug report, feature request, or
|
|
discussion, include a reference (e.g., `Fixes #123`, `Refs: PROJ-456`) in the commit message
|
|
footer. This provides traceability and can automatically close issues when commits are merged.
|
|
|
|
### Commit Message Format
|
|
|
|
All commits on this repository must follow the
|
|
[Conventional Changelog standard](https://github.com/conventional-changelog/conventional-changelog-eslint/blob/master/convention.md).
|
|
It is a very simple format so you can still write commit messages by hand. However it is
|
|
highly recommended developers install [Commitizen](https://commitizen.github.io/cz-cli/),
|
|
it extends the git command and will make writing commit messages a breeze. All CleverThis
|
|
repositories are configured with local Commitizen configuration scripts.
|
|
|
|
Getting Commitizen installed is usually trivial, just install it via npm. You will also
|
|
need to install the cz-customizable adapter which CleverThis repositories are configured
|
|
to use.
|
|
|
|
```bash
|
|
npm install -g commitizen@2.8.6 cz-customizable@4.0.0
|
|
```
|
|
|
|
Below is an example of Commitizen in action. It replaces your usual `git commit` command
|
|
with `git cz` instead. The new command takes all the same arguments however it leads you
|
|
through an interactive process to generate the commit message.
|
|
|
|

|
|
|
|
Commit messages are used to automatically generate our changelogs, and to ensure
|
|
commits are searchable in a useful way. So please use the Commitizen tool and adhere to
|
|
the commit message standard or else we cannot accept Pull Requests without editing
|
|
them first.
|
|
|
|
Below is an example of a properly formated commit message.
|
|
|
|
```
|
|
chore(Commitizen): Made repository Commitizen friendly.
|
|
|
|
Added standard Commitizen configuration files to the repo along with all the custom rules.
|
|
|
|
ISSUES CLOSED: #31
|
|
```
|
|
|
|
#### First Line vs. Commit Body
|
|
|
|
A commit message has two distinct parts separated by a blank line:
|
|
|
|
1. **First line (subject line):** A short, structured summary following the Conventional
|
|
Changelog format (e.g., `feat(cli): add tool and validation commands`). When an issue
|
|
specifies a commit message in its Metadata section, that prescribed text is the first line
|
|
and must be used exactly as written.
|
|
2. **Body (all subsequent lines after the blank line):** Free-form text where the contributor
|
|
describes implementation details, rationale, trade-offs, and any other relevant context.
|
|
The body is at the contributor's discretion and should be appropriate in length for the
|
|
scope of the change — detailed enough to explain *what* was done and *why*, but not
|
|
excessively long. The body should also include the issue reference footer (e.g.,
|
|
`ISSUES CLOSED: #45`).
|
|
|
|
Example with a prescribed first line from an issue's Metadata:
|
|
|
|
```
|
|
feat(cli): add tool and validation commands
|
|
|
|
Implemented `agents tool add/remove/list/show` with YAML config input and
|
|
schema validation. Added `agents validation attach/detach` for resource-scoped
|
|
validation attachments. Updated CLI reference documentation.
|
|
|
|
ISSUES CLOSED: #280
|
|
```
|
|
|
|
### Pull Request Process
|
|
|
|
> **Terminology:** "Pull Request" (PR) and "Merge Request" (MR) are used interchangeably in
|
|
> this project and its documentation. This project is hosted on Forgejo, which uses the term
|
|
> "Pull Request."
|
|
|
|
Every Pull Request must meet the following requirements before it is submitted for review:
|
|
|
|
1. **Provide a detailed description.** Every PR must include a clear, descriptive body that
|
|
explains the purpose of the change, summarizes what was done and why, and provides enough
|
|
context for a reviewer to understand the PR without reading every line of code. At a
|
|
minimum, the description must contain:
|
|
- A **summary** of the changes and the motivation behind them.
|
|
- An **issue reference** using a closing keyword that Forgejo recognizes (e.g.,
|
|
`Closes #45`, `Fixes #45`) so that the linked issue is automatically closed when the PR
|
|
is merged. If the PR addresses multiple issues, include a closing keyword for each.
|
|
- A **dependency link**: in addition to the textual reference, add the linked issue as a
|
|
Forgejo dependency on the PR **with the correct direction**: the PR must be marked as
|
|
**blocking** the issue, and the issue must **depend on** the PR. This means that on the
|
|
PR you add the issue under "blocks", and on the issue the PR appears under "depends on."
|
|
Getting this direction right is critical — if the dependency is reversed (the issue
|
|
blocking the PR), Forgejo will prevent the PR from being merged or closed until the
|
|
issue is resolved, which is the opposite of the intended workflow. See
|
|
[Linking and Dependencies](#linking-and-dependencies) for a full explanation of
|
|
dependency direction.
|
|
|
|
If your change is not associated with an existing issue, create one first — see
|
|
[Creating Issues](#creating-issues). PRs submitted without a description or without an
|
|
issue reference will not be reviewed.
|
|
2. **One Epic scope per PR.** Each PR must be associated with a single Epic. Do not combine
|
|
work from multiple unrelated Epics in one PR. If your changes span multiple Epics, split
|
|
them into separate PRs.
|
|
3. **Atomic, well-scoped commits.** All commits in the PR must follow the
|
|
[Commit Scope and Quality](#commit-scope-and-quality) guidelines. Each commit should be
|
|
small, self-contained, and independently buildable and testable.
|
|
4. **Commit messages reference tickets.** Every commit in the PR must reference the issue it
|
|
addresses in its commit message footer (e.g., `ISSUES CLOSED: #45` or `Refs: #45`). See
|
|
[Commit Message Format](#commit-message-format).
|
|
5. **Follow the Conventional Changelog standard.** All commit messages must follow the
|
|
[Conventional Changelog](https://github.com/conventional-changelog/conventional-changelog-eslint/blob/master/convention.md)
|
|
standard as described in [Commit Message Format](#commit-message-format).
|
|
6. **Update the changelog.** The PR must include an update to the changelog file. Add one new
|
|
entry per commit in the PR that describes the change from the user's perspective.
|
|
7. **No build or install artifacts.** Ensure that install or build dependencies do not appear
|
|
in any commits in the PR.
|
|
8. **Update CONTRIBUTORS.md.** Add your name to `CONTRIBUTORS.md` if it is not already listed
|
|
(one entry per person).
|
|
9. **Adjust the version number (when applicable).** If your change warrants a version bump,
|
|
update the project version to reflect the new version that this PR represents, following
|
|
[Clever Semantic Versioning](https://www.w3.org/submissions/semantic-versioning/) as
|
|
described in the [Versioning](#versioning) section. Note that most commits and Pull
|
|
Requests will not require a version change — a bump is only appropriate when the change
|
|
introduces new functionality, fixes a bug in a released version, or makes a breaking API
|
|
change. Routine work such as refactoring, documentation updates, test improvements, or
|
|
changes that have not yet been included in a release typically do not warrant a version
|
|
bump.
|
|
10. **All automated checks must pass.** Before requesting review, ensure that all CI checks
|
|
pass, including tests, linting, type checking, coverage, and security scans. PRs with
|
|
failing checks will not be reviewed.
|
|
11. **Assign a milestone.** Every PR must be assigned to the same milestone as its linked
|
|
issue(s). If the linked issues span multiple milestones, assign the PR to the milestone
|
|
of the primary issue. A PR without a milestone will not be reviewed.
|
|
12. **Apply a Type label.** Every PR must carry exactly one `Type/` label that matches the
|
|
nature of the change — the same `Type/` labels used for issues (e.g., `Type/Bug`,
|
|
`Type/Feature`, `Type/Task`). This enables filtering and searching PRs by the kind of
|
|
work they contain. No other label scopes (`State/`, `Priority/`, `MoSCoW/`) are applied
|
|
to Pull Requests; those scopes are reserved for issues only. Forgejo manages PR state
|
|
natively (open, closed, merged), so `State/` labels are unnecessary on PRs.
|
|
|
|
#### After Submission
|
|
|
|
Once your PR is submitted:
|
|
|
|
- Move the associated issue(s) to `State/In review` (see
|
|
[Ticket Lifecycle](#ticket-lifecycle)).
|
|
- A maintainer will review your PR. We aim to provide initial feedback within 48 hours.
|
|
- Your PR will either be approved or you will receive specific feedback on what needs to be
|
|
changed. If changes are requested, address them and push updated commits.
|
|
- Once all requirements are met (see
|
|
[Review and Merge Requirements](#review-and-merge-requirements) below), a maintainer will
|
|
merge the PR.
|
|
- After the PR is merged, move the associated issue(s) to `State/Completed`.
|
|
|
|
### Review and Merge Requirements
|
|
|
|
For a Pull Request to be merged, **all** of the following conditions must be satisfied.
|
|
|
|
#### Automated Checks
|
|
|
|
All CI pipeline checks must pass. This includes, but is not limited to:
|
|
|
|
- All tests (unit, integration, and any other applicable test suites).
|
|
- Linting and code formatting.
|
|
- Static type checking.
|
|
- Security scanning.
|
|
- Test coverage must remain at or above the project-defined minimum threshold. The general
|
|
minimum is **85%**. Individual projects may enforce a higher threshold — see the
|
|
[Project-Specific Guidelines](#project-specific-guidelines) section for this project's exact
|
|
requirement.
|
|
|
|
#### Peer Review
|
|
|
|
- **Minimum approvals:** Every PR requires at least **two (2)** approving reviews from project
|
|
contributors who are not the PR author.
|
|
- **No unresolved objections:** At the time of merge, there must be no open "Request Changes"
|
|
or "Rejected" reviews. All requested changes must be addressed and the reviewer must either
|
|
re-approve or withdraw their objection before the PR can be merged.
|
|
- **Author exclusion:** The author of a PR cannot serve as one of its reviewers. Self-approval
|
|
does not count toward the required approvals.
|
|
- **What reviewers evaluate:** Reviewers are expected to assess the PR for:
|
|
- **Correctness:** Does the code do what it claims to do? Does it satisfy the acceptance
|
|
criteria of the linked issue(s)?
|
|
- **Readability:** Is the code clear, well-named, and easy to follow?
|
|
- **Performance:** Are there unnecessary inefficiencies, redundant operations, or potential
|
|
scalability concerns?
|
|
- **Security:** Does the code introduce any security vulnerabilities or unsafe patterns?
|
|
- **Style:** Does the code follow the project's coding standards and conventions?
|
|
- **Test coverage:** Are the tests adequate? Do they cover edge cases and failure modes?
|
|
- **Maintainer override:** In exceptional circumstances, a project owner may override the
|
|
review requirements to merge a PR with fewer approvals. This is reserved for urgent fixes
|
|
and is not the norm.
|
|
|
|
#### Merge Checklist
|
|
|
|
Before a PR is merged, confirm the following:
|
|
|
|
- [ ] PR description is detailed, explains the change, and includes closing keywords for all
|
|
linked issues (e.g., `Closes #45`)
|
|
- [ ] Linked issues are added as Forgejo dependencies on the PR with the correct direction
|
|
(the PR **blocks** the issue; the issue **depends on** the PR)
|
|
- [ ] All CI checks pass (tests, linting, type checking, security, coverage)
|
|
- [ ] Test coverage meets or exceeds the project-defined threshold
|
|
- [ ] At least two approving reviews from non-author contributors
|
|
- [ ] No open "Request Changes" or "Rejected" reviews
|
|
- [ ] All commits follow Conventional Changelog format
|
|
- [ ] Every commit references its associated issue
|
|
- [ ] Changelog has been updated
|
|
- [ ] CONTRIBUTORS.md has been updated (if applicable)
|
|
- [ ] Version number has been updated
|
|
- [ ] PR is assigned to the correct milestone (matching its linked issues)
|
|
- [ ] PR has exactly one appropriate `Type/` label
|
|
- [ ] Associated issue(s) are in `State/In review` or `State/Completed`
|
|
|
|
### Versioning
|
|
|
|
All projects must follow
|
|
[Clever Semantic Versioning](https://www.w3.org/submissions/semantic-versioning/). Version
|
|
numbers take the form `MAJOR.MINOR.PATCH` where:
|
|
|
|
- **MAJOR** is incremented for incompatible API changes.
|
|
- **MINOR** is incremented for backwards-compatible new functionality.
|
|
- **PATCH** is incremented for backwards-compatible bug fixes.
|
|
|
|
Breaking changes are only permitted in major version increments. Every release must have a
|
|
version number that accurately communicates the nature of its changes to consumers. Refer to
|
|
the project-specific [Backwards Compatibility](#backwards-compatibility) section for details on
|
|
how this project applies these rules during its current development phase.
|
|
|
|
## Code Style & Best Practices
|
|
|
|
### General Principles
|
|
|
|
- **Specification-First Development:** Treat the project specification as the authoritative source
|
|
of truth for design and architecture. Architectural changes follow an ADR (Architecture Decision
|
|
Record) process: proposed changes must first be captured in an ADR, reviewed, and approved.
|
|
Once approved, the decision is incorporated into the specification, which then serves as the
|
|
definitive source of truth for implementation. Code should only be written to reflect what the
|
|
specification describes — when there is a discrepancy between the current codebase and the
|
|
specification, always assume the specification is correct and align the code accordingly.
|
|
- **Modern Tooling:** Use modern, idiomatic build tools and workflows for the project's language
|
|
ecosystem. Avoid legacy approaches such as Makefiles or wrapper shell scripts. All tooling
|
|
should be from the current ecosystem and used as designed — not wrapped in custom scripts.
|
|
- **Prefer Existing Tooling:** When the project's current tools can solve a problem, use them
|
|
rather than introducing new dependencies. Keep the toolchain simple and consistent. Adding new
|
|
tooling should be a deliberate decision, not a default.
|
|
- **Modular Design:** Keep files under 500 lines. Break large files into focused, cohesive modules.
|
|
- **Environment Safety:** Never hardcode secrets or sensitive information.
|
|
- **Test-First Development:** Write tests before implementation.
|
|
- **Clean Architecture:** Separate concerns, maintain clear boundaries between layers.
|
|
- **Documentation:** Keep all documentation updated alongside code changes.
|
|
|
|
### Import Guidelines
|
|
|
|
- **Idiomatic Usage:** Follow the import conventions established by the project's language and
|
|
ecosystem. When the language or its style guide prescribes a standard way to declare imports,
|
|
use it consistently.
|
|
- **Narrow Imports:** Import only what is needed. Prefer importing specific symbols over broad
|
|
wildcard or module-level imports to keep dependencies explicit and avoid polluting the local
|
|
namespace.
|
|
|
|
### Programming Patterns
|
|
|
|
Use these patterns and principles liberally throughout the codebase:
|
|
|
|
#### SOLID Principles
|
|
|
|
- **Single Responsibility Principle (SRP):** Each class should have one reason to change; one responsibility only.
|
|
- **Open/Closed Principle (OCP):** Classes should be open for extension but closed for modification.
|
|
- **Liskov Substitution Principle (LSP):** Subtypes must be substitutable for their base types without altering program correctness.
|
|
- **Interface Segregation Principle (ISP):** Clients should not be forced to depend on interfaces they don't use; prefer small, specific interfaces.
|
|
- **Dependency Inversion Principle (DIP):** Depend on abstractions, not concretions; high-level modules should not depend on low-level modules.
|
|
|
|
#### Creational Patterns
|
|
|
|
- **Factory Pattern:** For object creation with complex initialization logic.
|
|
- **Abstract Factory Pattern:** For creating families of related objects without specifying concrete classes.
|
|
- **Builder Pattern:** For constructing complex objects step by step with fluent interfaces.
|
|
- **Prototype Pattern:** For cloning objects instead of creating new instances.
|
|
- **Singleton Pattern:** For single-instance resources (use sparingly, prefer dependency injection).
|
|
- **Object Pool Pattern:** For reusing expensive-to-create objects.
|
|
- **Dependency Injection:** For testability and inversion of control.
|
|
|
|
#### Structural Patterns
|
|
|
|
- **Adapter Pattern:** For interface compatibility between incompatible classes.
|
|
- **Bridge Pattern:** For separating abstraction from implementation to vary them independently.
|
|
- **Composite Pattern:** For treating individual objects and compositions uniformly in tree structures.
|
|
- **Decorator Pattern:** For extending functionality without modifying original code.
|
|
- **Facade Pattern:** For providing simplified interfaces to complex subsystems.
|
|
- **Flyweight Pattern:** For sharing common state among many objects to reduce memory usage.
|
|
- **Proxy Pattern:** For controlling access to objects (lazy loading, access control, logging).
|
|
- **Module Pattern:** For encapsulating related functionality in cohesive units.
|
|
|
|
#### Behavioral Patterns
|
|
|
|
- **Chain of Responsibility Pattern:** For passing requests along a chain of handlers.
|
|
- **Command Pattern:** For encapsulating operations as objects.
|
|
- **Interpreter Pattern:** For defining grammar and interpreting language sentences.
|
|
- **Iterator Pattern:** For sequential access to collection elements without exposing representation.
|
|
- **Mediator Pattern:** For centralizing complex communications between objects.
|
|
- **Memento Pattern:** For capturing and restoring object state without violating encapsulation.
|
|
- **Observer Pattern:** For event-driven architectures and loose coupling between publishers and subscribers.
|
|
- **State Pattern:** For altering object behavior when internal state changes.
|
|
- **Strategy Pattern:** For algorithms that can be selected at runtime.
|
|
- **Template Method Pattern:** For defining algorithm skeletons with customizable steps.
|
|
- **Visitor Pattern:** For separating algorithms from the objects they operate on.
|
|
- **Null Object Pattern:** For providing default behavior instead of null references.
|
|
|
|
#### Architectural Patterns
|
|
|
|
- **Repository Pattern:** For data access abstraction and separation of domain from data access logic.
|
|
- **Unit of Work Pattern:** For maintaining a list of objects affected by transactions and coordinating changes.
|
|
- **Service Layer Pattern:** For defining application boundaries and encapsulating business logic.
|
|
- **MVC (Model-View-Controller):** For separating data, presentation, and control logic.
|
|
- **CQRS (Command Query Responsibility Segregation):** For separating read and write operations.
|
|
- **Event Sourcing Pattern:** For storing state changes as a sequence of events.
|
|
- **Specification Pattern:** For encapsulating business rules and making them reusable and composable.
|
|
|
|
## Error and Exception Handling
|
|
|
|
### Argument Validation
|
|
|
|
**All public and protected class methods must validate arguments as the first guard.** Perform
|
|
these checks before any other logic. The purpose of argument validation is to ensure fail-fast
|
|
behavior — invalid inputs should be rejected immediately at the boundary where they enter, so
|
|
that errors surface close to their source rather than propagating silently into deeper logic
|
|
where they become difficult to diagnose.
|
|
|
|
**Checks to perform:**
|
|
- **Value Range:** Ensure numeric values are within acceptable bounds.
|
|
- **Null Checks:** Reject null values where not expected; explicitly accept them only when documented.
|
|
- **Type Verification:** In languages that support runtime type inspection, verify that arguments
|
|
are of the expected type, following the idiomatic approach for the language. Prefer catching
|
|
type errors at compile time through static type systems whenever possible; resort to runtime
|
|
type checks only when the language lacks compile-time enforcement or when interfacing with
|
|
untyped boundaries (e.g., deserialized input, dynamic plugin APIs).
|
|
- **Empty Strings:** Reject empty strings where non-empty strings are required.
|
|
- **Empty Collections:** Check for empty lists, maps, or sets if they must contain elements.
|
|
- **Invalid States:** Verify object state is valid for the operation.
|
|
|
|
### Exception Propagation
|
|
|
|
**CRITICAL: Do not suppress errors. Let exceptions propagate to top-level execution.**
|
|
|
|
- Exceptions should bubble up to the top level where they can be properly logged and handled.
|
|
- Do not catch exceptions just to log and re-raise; let them propagate naturally.
|
|
- Do not use bare catch-all exception handlers without re-raising unless you have specific
|
|
recovery logic.
|
|
|
|
**Only catch exceptions when you can meaningfully handle them** (e.g., retry logic, resource
|
|
cleanup, adding context). Otherwise, let them propagate.
|
|
|
|
### Fail-Fast Principles
|
|
|
|
**Design code to fail immediately when something is wrong:**
|
|
|
|
- **Type Safety:** In languages that support static type systems or type annotations, use them
|
|
extensively and run static type checkers as part of the standard build pipeline. In languages
|
|
without static type systems, use whatever idiomatic mechanisms the language provides for
|
|
expressing and verifying type constraints.
|
|
- **Early Validation:** Check preconditions at function entry, not deep in logic.
|
|
- **Assertions:** Use assertions for invariants that should never be violated during development.
|
|
- **No Silent Failures:** Avoid returning null or default values when an error condition
|
|
exists — raise exceptions or return explicit error types.
|
|
- **Explicit Over Implicit:** Make failure conditions explicit rather than hiding them.
|
|
|
|
**Benefits:**
|
|
- Bugs are caught closer to their source.
|
|
- Stack traces are more meaningful.
|
|
- Debugging is faster.
|
|
- System state remains consistent.
|
|
|
|
### Best Practices Summary
|
|
|
|
- Validate all arguments in public/protected methods immediately.
|
|
- Use static type annotations and type checking where the language supports it.
|
|
- Let exceptions propagate; don't suppress them.
|
|
- Fail fast when preconditions aren't met.
|
|
- Only catch exceptions when you have meaningful recovery logic.
|
|
- Never catch exceptions just to log them — let them bubble up for centralized handling.
|
|
|
|
## Type Safety
|
|
|
|
Prefer static typing whenever the language supports it. When a language offers a static type
|
|
system or type annotation mechanism, use it pervasively and enforce it with a type checker in
|
|
the build pipeline. When the language does not have a static type system but provides idiomatic
|
|
mechanisms for expressing or verifying type constraints at runtime, use those instead. In
|
|
languages that offer neither, rely on the argument validation and fail-fast principles described
|
|
above to catch type-related errors as early as possible.
|
|
|
|
- **Full Annotations:** In languages with type annotation support, every function signature,
|
|
variable declaration, and return type should be annotated with explicit types.
|
|
- **No Suppression:** When a static type checker is in use, never disable it via configuration
|
|
files and never use inline comments or annotations to suppress individual type checking errors
|
|
(e.g., no `type: ignore`, `noinspection`, `@SuppressWarnings`, or equivalent directives).
|
|
- **Continuous Enforcement:** Type checking, whether static or through other idiomatic means,
|
|
must be part of the standard test and CI pipeline. It runs on every commit and must pass
|
|
before work is considered complete.
|
|
|
|
## Test Isolation and Mock Placement
|
|
|
|
All mocks, test doubles, and mock implementations must exist only within test directories.
|
|
Production source code and utility scripts must never contain mock implementations, test data,
|
|
or conditional testing behavior.
|
|
|
|
- **Strict Separation:** Test support code (mocks, fakes, stubs, fixtures) lives exclusively in
|
|
test directories. Source code directories are reserved for production code only.
|
|
- **No Conditional Test Behavior:** Production code must not contain `if testing:` guards,
|
|
test-only code paths, or any logic that changes behavior based on whether the code is under
|
|
test.
|
|
- **Dependency Injection:** Use dependency injection to swap implementations during tests. Design
|
|
classes and functions to accept their dependencies as parameters rather than creating them
|
|
internally, making it straightforward to substitute test doubles without modifying production
|
|
code.
|
|
|
|
## Documentation Standards
|
|
|
|
- **Continuous Documentation:** Update documentation alongside code changes. Discoveries,
|
|
decisions, and workarounds must be documented promptly — no decision should remain
|
|
undocumented. If new information appears during implementation, extend the relevant
|
|
documentation immediately before proceeding.
|
|
- **Single Documentation Surface:** Each type of documentation has a canonical location. Avoid
|
|
scattering related notes across multiple files or locations. If a document exists for a
|
|
particular purpose, update it rather than creating a new file elsewhere.
|
|
- **Traceability:** When a decision impacts future work, reference the relevant code by its
|
|
logical location — for example, module path, class name, and method name (e.g.,
|
|
`cleveragents.application.services.PlanLifecycleService.use_action`). Include the commit hash
|
|
at the time of writing so that if names are later refactored, someone can trace the original
|
|
location through version control history. **Never reference code by line number**
|
|
(`file_path:line_number`) as line numbers shift with every edit and become misleading quickly.
|
|
Record substantive design decisions, pattern choices, and risk mitigations in the appropriate
|
|
documentation alongside these logical references to the code they affect.
|
|
- **Documentation Completeness:** Documentation updates are part of the definition of done for
|
|
any task. Work is not complete until all affected documentation has been reviewed and updated
|
|
to reflect the current state of the code.
|
|
|
|
## Issues and Project Management
|
|
|
|
This section describes how issues (also called tickets) are created, classified, and moved
|
|
through their lifecycle. Following these guidelines ensures that work is well-organized,
|
|
traceable, and easy for maintainers and contributors to coordinate on.
|
|
|
|
### Creating Issues
|
|
|
|
Anyone may create an issue at any time without prior approval. New issues automatically enter
|
|
the `State/Unverified` state (see [Ticket Lifecycle](#ticket-lifecycle) below). While you do
|
|
not need permission to open an issue, taking the time to write a well-structured issue greatly
|
|
increases the chances it will be acted on quickly.
|
|
|
|
Every issue must include:
|
|
|
|
- **Title:** A concise, descriptive summary of the work or problem. The title should be
|
|
specific enough that a reader can understand the scope without opening the issue body. Avoid
|
|
vague titles such as "Fix bug" or "Improve performance" — instead write something like
|
|
"Fix null pointer when parsing empty configuration file" or "Add CSV export to usage metrics
|
|
dashboard."
|
|
- **Labels:** When creating an issue, apply the appropriate labels from the
|
|
[Label System](#label-system). At minimum, every new issue must have:
|
|
- A **State** label — new issues should be labeled `State/Unverified` (this is the initial
|
|
state for all issues; see [Ticket Lifecycle](#ticket-lifecycle)).
|
|
- A **Type** label — one of `Type/Bug`, `Type/Feature`, `Type/Task`, `Type/Epic`, or
|
|
`Type/Legendary`, as appropriate for the nature of the work.
|
|
- A **Priority** label — if you have a suggested priority, apply it (e.g.,
|
|
`Priority/Medium`). If unsure, use `Priority/Backlog` and a maintainer will adjust it
|
|
during triaging.
|
|
|
|
Do **not** assign `MoSCoW/` labels — these are set exclusively by the project owner.
|
|
- **Milestone:** Every issue that has moved beyond `State/Unverified` must be assigned to a
|
|
milestone. Issues in `State/Unverified` may optionally have a milestone — it is not required
|
|
at creation time, but is typically assigned by a maintainer during triaging when the issue is
|
|
moved to `State/Verified`. Once an issue is in any active state (`State/Verified`,
|
|
`State/In Progress`, `State/Paused`, `State/In Review`, or `State/Completed`), a milestone
|
|
is mandatory. If no appropriate milestone exists, discuss with the project owner before
|
|
proceeding.
|
|
- **Branch/Tag (Ref):** Forgejo issues have a **branch/tag** field (the `Ref` field on the
|
|
issue form) that associates the issue with a specific branch or tag in the repository.
|
|
The rules for this field depend on the nature of the issue:
|
|
- **Development issues** (features, fixes, chores, refactoring — any issue where a branch
|
|
is created for the work): the Ref field must be set to the **same branch** named in the
|
|
issue body's Metadata section. This field becomes required when the issue moves to
|
|
`State/In Progress` or later, and must always reference a **branch**, not a tag. Setting
|
|
this field allows Forgejo to associate the issue with the correct branch in its UI and
|
|
link tracking.
|
|
- **Non-development issues** (bug reports, questions, support requests, and other issues
|
|
that do not have their own working branch): the Ref field may be left blank. If the
|
|
issue relates to a specific branch or tag (e.g., a bug observed on a particular release
|
|
tag, or a question about code on a specific branch), the field may optionally be set to
|
|
that branch or tag for reference.
|
|
- **Description:** The body of the issue must contain the following sections:
|
|
- **Background and context:** Why does this issue exist? What is the motivation?
|
|
- **Current behavior** (for bugs): What is happening now that is incorrect or undesirable?
|
|
Include reproduction steps, error messages, logs, or screenshots where applicable.
|
|
- **Expected behavior:** What should happen instead, or what does "done" look like?
|
|
- **Acceptance criteria:** A clear, testable list of conditions that must be met for the
|
|
issue to be considered complete. Each criterion should be unambiguous enough that any
|
|
reviewer can independently verify whether it has been satisfied.
|
|
- **Supporting information:** Links to related issues, relevant documentation, screenshots,
|
|
logs, reproduction steps, or any other material that helps clarify the issue.
|
|
- **Metadata:** A section at the top of the issue body (typically labeled `## Metadata`)
|
|
that contains:
|
|
- **Commit message:** The exact first line of the commit message that should
|
|
be used when committing work for this issue. This must follow the
|
|
[Conventional Changelog](https://github.com/conventional-changelog/conventional-changelog-eslint/blob/master/convention.md)
|
|
format (e.g., `feat(cli): add tool and validation commands`). When making the commit,
|
|
this first line is followed by a blank line, after which the contributor may freely write
|
|
additional lines in the commit body to describe the implementation details, rationale,
|
|
and any other relevant context (see
|
|
[Commit Message Format](#commit-message-format) for the full structure).
|
|
- **Branch name:** The name of the branch that should be used for work on this issue
|
|
(e.g., `feature/m3-tool-cli`).
|
|
|
|
Example:
|
|
```markdown
|
|
## Metadata
|
|
- **Commit Message**: `feat(cli): add tool and validation commands`
|
|
- **Branch**: `feature/m3-tool-cli`
|
|
```
|
|
- **Subtasks** *(optional but highly recommended)*: A section (typically labeled
|
|
`## Subtasks`) containing a checklist of discrete work items that break the issue down into
|
|
smaller, trackable steps. Each subtask should be a Markdown checkbox item (`- [ ]`). This
|
|
makes progress visible and helps contributors and reviewers understand the scope of work at
|
|
a glance. Example:
|
|
```markdown
|
|
## Subtasks
|
|
- [ ] Implement input validation for configuration parser
|
|
- [ ] Add error messages for malformed YAML files
|
|
- [ ] Tests (Behave): Add scenarios for config parsing edge cases
|
|
- [ ] Tests (Robot): Add integration test for config loading
|
|
- [ ] Verify coverage >=97% via `nox -s coverage_report`
|
|
- [ ] Run `nox` (all default sessions), fix any errors
|
|
```
|
|
- **Definition of Done:** A section (typically labeled `## Definition of Done`) that
|
|
explicitly describes when the issue should be considered satisfied and complete. This goes
|
|
beyond the acceptance criteria by specifying the full set of conditions for closure —
|
|
including that all subtasks are checked off, the commit has been created with the correct
|
|
first-line message from the Metadata section and pushed to the correct branch, and a Pull
|
|
Request has been submitted, reviewed, and merged. Example:
|
|
```markdown
|
|
## Definition of Done
|
|
This issue is complete when:
|
|
- All subtasks above are completed and checked off.
|
|
- A Git commit is created where the **first line** of the commit message
|
|
matches the Commit Message in Metadata exactly, followed by a blank line,
|
|
then additional lines providing relevant details about the implementation.
|
|
- The commit is pushed to the remote on the branch matching the **Branch** in
|
|
Metadata exactly.
|
|
- The commit is submitted as a **pull request** to `master`, reviewed, and
|
|
**merged** before this issue is marked done.
|
|
```
|
|
- **Parent link(s):** If the issue is part of a larger effort, link it to its parent Epic or
|
|
Legendary (see [Epics and Legendaries](#epics-and-legendaries)). An issue may have more than
|
|
one parent — for example, an end-to-end test issue might belong to both a testing Epic and
|
|
the thematic Epic whose feature it validates. All non-Epic, non-Legendary issues should be
|
|
linked to at least one parent when one exists. Reference parents in the issue description
|
|
(e.g., "Epic: #42" or "Part of #42 and #85").
|
|
- **Blocking link(s):** If the issue blocks or is blocked by other issues, these dependencies
|
|
must be recorded at creation time when known. Link to any issues that this issue blocks, and
|
|
any issues that block this one (e.g., "Blocked by #57", "Blocks #63"). See
|
|
[Linking and Dependencies](#linking-and-dependencies) for the full process, including when
|
|
to apply the `Blocked` label.
|
|
|
|
### Label System
|
|
|
|
Issues are classified using a structured label system organized into several scopes. Each
|
|
scope is denoted by a prefix followed by a `/` (e.g., `State/Verified`, `Priority/High`).
|
|
Labels are not free-form — use only the labels defined below.
|
|
|
|
#### State Labels (`State/`)
|
|
|
|
State labels track where an issue is in its lifecycle. Every issue must have exactly one State
|
|
label at all times. See [Ticket Lifecycle](#ticket-lifecycle) for the rules governing
|
|
transitions between states.
|
|
|
|
| Label | Meaning |
|
|
|---|---|
|
|
| `State/Unverified` | The issue has been created but has not yet been reviewed by a maintainer. This is the initial state for all new issues. |
|
|
| `State/Verified` | A maintainer has reviewed the issue and confirmed it is valid, actionable, and not a duplicate. The issue is ready to be worked on. |
|
|
| `State/In progress` | Someone is actively working on this issue. |
|
|
| `State/Paused` | Work on this issue has been temporarily suspended, typically because it is blocked by another issue. When an issue is paused due to a blocker, the `Blocked` label must also be present and the blocking issue must be linked (see [Linking and Dependencies](#linking-and-dependencies)). |
|
|
| `State/In review` | The implementation is complete and a Pull Request has been submitted. The work is awaiting peer review. |
|
|
| `State/Completed` | The issue has been resolved and all associated Pull Requests have been merged. |
|
|
| `State/Wont Do` | The issue has been reviewed and a deliberate decision has been made not to address it. This may be because the issue is out of scope, not reproducible, or superseded by other work. A comment explaining the reason is required. |
|
|
|
|
Additionally, the label `Duplicate` is used when an issue is a duplicate of an existing issue.
|
|
When marking an issue as a duplicate, always link to the original issue in a comment before
|
|
closing it.
|
|
|
|
#### Priority Labels (`Priority/`)
|
|
|
|
Priority labels indicate the relative urgency of an issue. Priority is typically assigned by
|
|
maintainers during triaging, but contributors may suggest a priority when creating an issue.
|
|
|
|
| Label | Meaning |
|
|
|---|---|
|
|
| `Priority/Critical` | Must be addressed immediately. Reserved for outages, data loss, security vulnerabilities, or other issues that block a release or affect production. |
|
|
| `Priority/High` | Important work that should be completed soon. High-priority items are typically addressed in the current or next development cycle. |
|
|
| `Priority/Medium` | Normal priority. The issue is valid and should be addressed, but is not urgent. |
|
|
| `Priority/Low` | Nice to have. The issue is valid but is not time-sensitive and may be deferred. |
|
|
| `Priority/Backlog` | The issue has not yet been prioritized. This is the default priority for newly verified issues until a maintainer assigns a different priority. |
|
|
|
|
#### MoSCoW Labels (`MoSCoW/`)
|
|
|
|
MoSCoW labels categorize issues by their importance to the project's goals and milestones:
|
|
|
|
| Label | Meaning |
|
|
|---|---|
|
|
| `MoSCoW/Must Have` | This issue is essential. The project cannot ship or the milestone cannot be considered complete without it. |
|
|
| `MoSCoW/Should Have` | This issue is important but not strictly essential. It should be included if at all possible, but the release is not blocked without it. |
|
|
| `MoSCoW/Could Have` | This issue is desirable but not necessary. It will be included only if time and resources permit. |
|
|
|
|
There is no "Won't Have" label. Issues that will not be addressed are instead moved to the
|
|
`State/Wont Do` state.
|
|
|
|
**Important:** MoSCoW labels are assigned exclusively by the project owner. Contributors
|
|
should not add, remove, or change MoSCoW labels on issues.
|
|
|
|
#### Type Labels (`Type/`)
|
|
|
|
Type labels classify the nature of the work:
|
|
|
|
| Label | Meaning |
|
|
|---|---|
|
|
| `Type/Bug` | A defect in existing functionality. Something that was working or was expected to work is broken or behaving incorrectly. |
|
|
| `Type/Feature` | A new capability or user story. This issue describes functionality that does not yet exist. Also referred to as a "User Story." |
|
|
| `Type/Task` | A unit of technical or administrative work that is not directly a bug fix or a new feature (e.g., refactoring, updating dependencies, improving documentation, infrastructure work). |
|
|
| `Type/Epic` | A large body of work that can be broken down into multiple smaller issues. See [Epics and Legendaries](#epics-and-legendaries). |
|
|
| `Type/Legendary` | An exceptionally large body of work, bigger than an Epic, representing a major initiative. See [Epics and Legendaries](#epics-and-legendaries). |
|
|
|
|
#### Special Labels
|
|
|
|
| Label | Meaning |
|
|
|---|---|
|
|
| `Blocked` | This issue cannot proceed because it depends on another issue that has not yet been resolved. When applying this label, you must link to the blocking issue in a comment. If no issue exists for the blocker, create one first. See [Linking and Dependencies](#linking-and-dependencies). |
|
|
| `Signed-off:` | Used during the review process to indicate formal sign-off by a reviewer or maintainer. |
|
|
|
|
### Ticket Lifecycle
|
|
|
|
Every issue follows a defined lifecycle. The standard progression is:
|
|
|
|
```
|
|
State/Unverified → State/Verified → State/In progress → State/In review → State/Completed
|
|
```
|
|
|
|
At any point, an issue may also transition to `State/Wont Do` or be marked as `Duplicate`.
|
|
An issue may transition to `State/Paused` from `State/In progress` if it becomes blocked,
|
|
and back to `State/In progress` once the blocker is resolved.
|
|
|
|
#### Stage Descriptions and Transition Rules
|
|
|
|
1. **Unverified → Verified (or Wont Do / Duplicate)**
|
|
|
|
All newly created issues start as `State/Unverified`. A maintainer will review the issue
|
|
during triaging (see [Triaging](#triaging)) to determine whether it is valid and actionable.
|
|
After review, the maintainer will move the issue to one of the following:
|
|
- `State/Verified` — the issue is accepted and ready to be worked on.
|
|
- `State/Wont Do` — the issue will not be addressed (with a comment explaining why).
|
|
- `Duplicate` — the issue already exists (with a link to the original).
|
|
|
|
Contributors do not move their own issues out of `State/Unverified`. This transition is
|
|
performed by maintainers only.
|
|
|
|
2. **Verified → In Progress**
|
|
|
|
When a contributor begins working on a verified issue, they must move it to
|
|
`State/In progress`. This signals to other contributors that the issue is being actively
|
|
worked on and prevents duplicate effort. If you intend to work on an issue, assign yourself
|
|
to it and update the state label at the same time.
|
|
|
|
3. **In Progress → Paused** *(optional)*
|
|
|
|
If work on an issue is blocked by a dependency on another unresolved issue, move it to
|
|
`State/Paused` and add the `Blocked` label. You must link to the blocking issue in a
|
|
comment (e.g., "Blocked by #57"). See
|
|
[Linking and Dependencies](#linking-and-dependencies) for full instructions. Once the
|
|
blocker is resolved, remove the `Blocked` label and move the issue back to
|
|
`State/In progress`.
|
|
|
|
4. **In Progress → In Review**
|
|
|
|
When the implementation is complete and a Pull Request has been submitted, move the issue to
|
|
`State/In review`. The Pull Request must reference the issue as described in
|
|
[Pull Request Process](#pull-request-process).
|
|
|
|
5. **In Review → Completed**
|
|
|
|
Once the Pull Request has passed all automated checks, received the required approvals (see
|
|
[Review and Merge Requirements](#review-and-merge-requirements)), and been merged, move the
|
|
issue to `State/Completed`.
|
|
|
|
6. **Any State → Wont Do**
|
|
|
|
At any point, a maintainer may decide that an issue will not be addressed and move it to
|
|
`State/Wont Do`. The reason must be documented in a comment on the issue.
|
|
|
|
### Epics and Legendaries
|
|
|
|
Epics and Legendaries are large-scale work items used to organize and group related issues.
|
|
|
|
- An **Epic** (`Type/Epic`) represents a large body of work that is broken down into multiple
|
|
smaller issues (bugs, features, tasks). Epics provide a high-level view of a feature or
|
|
initiative and track progress across their child issues.
|
|
- A **Legendary** (`Type/Legendary`) is even larger than an Epic. Legendaries represent major
|
|
initiatives or milestones that may encompass multiple Epics.
|
|
|
|
**Rules for Epics and Legendaries:**
|
|
|
|
- Epics and Legendaries are organizational containers. They are not implemented directly — the
|
|
actual work is done in the child issues linked to them.
|
|
- All non-Epic, non-Legendary issues should be linked to a parent Epic or Legendary when one
|
|
exists. This ensures that all work is traceable to a larger objective.
|
|
- When creating a new issue, check whether an existing Epic or Legendary covers the area you
|
|
are working on. If it does, link your issue to it by referencing the Epic or Legendary in
|
|
your issue description (e.g., "Part of #42").
|
|
- If you are proposing a large body of work that does not fit under an existing Epic, consider
|
|
creating a new Epic issue to serve as the parent, then creating individual issues for the
|
|
constituent tasks.
|
|
|
|
### Linking and Dependencies
|
|
|
|
Proper linking between issues ensures that dependencies are visible and that work is
|
|
coordinated effectively.
|
|
|
|
#### Dependency Direction
|
|
|
|
**The direction of a dependency link in Forgejo matters and must be set correctly.** Forgejo
|
|
models dependencies with two sides: a ticket that **blocks** another and a ticket that
|
|
**depends on** the first. These are not interchangeable — reversing the direction changes the
|
|
meaning and can have practical consequences (see
|
|
[Pull Request Dependencies](#pull-request-dependencies) below for a critical example).
|
|
|
|
When creating a dependency between two issues:
|
|
|
|
- The issue that must be completed **first** is the **blocker**. It should list the other issue
|
|
under its **"blocks"** list.
|
|
- The issue that **cannot proceed** until the blocker is resolved is the **dependent**. It
|
|
should list the blocker under its **"depends on"** list.
|
|
|
|
For example, if issue #57 must be finished before issue #63 can begin:
|
|
|
|
- Issue #57 **blocks** #63.
|
|
- Issue #63 **depends on** #57.
|
|
|
|
In Forgejo's UI, you set this by opening issue #63 and adding #57 under "depends on," or by
|
|
opening issue #57 and adding #63 under "blocks." Both actions create the same directed link.
|
|
|
|
#### Pull Request Dependencies
|
|
|
|
**This direction is especially critical for the relationship between Pull Requests and
|
|
issues.** When a PR implements work for an issue, the correct dependency direction is:
|
|
|
|
- The **PR blocks** the issue.
|
|
- The **issue depends on** the PR.
|
|
|
|
This reflects reality: the issue cannot be closed until the PR is merged (the PR's completion
|
|
is a prerequisite for the issue's completion).
|
|
|
|
**WARNING:** If the dependency is set in the **wrong** direction — that is, the issue is
|
|
recorded as blocking the PR — Forgejo will prevent the PR from being merged or closed until
|
|
the issue is resolved first. Since the issue cannot be resolved until the PR is merged, this
|
|
creates an unresolvable deadlock. Always verify the direction before saving the dependency
|
|
link.
|
|
|
|
To set the correct direction in Forgejo's UI:
|
|
|
|
1. Open the **Pull Request**.
|
|
2. In the dependency section, add the linked issue under **"blocks"**.
|
|
3. Verify that on the **issue** side, the PR now appears under **"depends on."**
|
|
|
|
Alternatively, open the issue and add the PR under "depends on" — this creates the same
|
|
directed link.
|
|
|
|
#### Issue-to-Issue Dependencies
|
|
|
|
- **Parent links:** All non-Epic, non-Legendary issues should reference their parent Epic or
|
|
Legendary in the issue description or in a comment (e.g., "Part of #42"). This creates a
|
|
navigable hierarchy from high-level objectives down to individual tasks.
|
|
- **Blocking relationships:** If an issue cannot be worked on until another issue is resolved,
|
|
this dependency must be explicitly recorded:
|
|
1. Add the `Blocked` label to the dependent issue.
|
|
2. Link to the blocking issue in a comment (e.g., "Blocked by #57").
|
|
3. Add the Forgejo dependency with the correct direction: the blocking issue **blocks** the
|
|
dependent issue, and the dependent issue **depends on** the blocking issue.
|
|
4. If no issue exists for the blocker, create one before marking the dependency.
|
|
5. Move the blocked issue to `State/Paused`.
|
|
- **Resolution:** When a blocking issue is resolved, remove the `Blocked` label from all
|
|
issues it was blocking and move them back to the appropriate state (typically
|
|
`State/In progress` or `State/Verified`).
|
|
- **Cross-references:** Use issue references (e.g., `#123`) liberally in comments and
|
|
descriptions to create a navigable web of related work. This helps reviewers and future
|
|
contributors understand the context and history of decisions.
|
|
|
|
### Triaging
|
|
|
|
Triaging is the process by which maintainers review `State/Unverified` issues and determine
|
|
their disposition. During triaging, a maintainer will:
|
|
|
|
1. Read the issue and assess whether it is valid, actionable, and sufficiently well-described.
|
|
2. Check for duplicates. If the issue duplicates an existing one, mark it as `Duplicate`, link
|
|
to the original, and close it.
|
|
3. If the issue is not something the project will address, move it to `State/Wont Do` with a
|
|
comment explaining why.
|
|
4. If the issue is valid, move it to `State/Verified` and apply appropriate `Type/` and
|
|
`Priority/` labels. Newly verified issues receive `Priority/Backlog` by default unless the
|
|
maintainer determines a higher priority is warranted.
|
|
5. Assign the issue to the appropriate milestone. A milestone is mandatory for all issues
|
|
beyond `State/Unverified`.
|
|
6. Link the issue to a parent Epic or Legendary if applicable.
|
|
|
|
Contributors can help the triaging process by writing clear, complete issues that include all
|
|
the required fields described in [Creating Issues](#creating-issues). A well-written issue is
|
|
significantly more likely to be verified quickly and acted on.
|
|
|
|
---
|
|
|
|
## Project-Specific Guidelines
|
|
|
|
The following guidelines are specific to this project and supplement the general rules above
|
|
with concrete tools, thresholds, and conventions.
|
|
|
|
### Backwards Compatibility
|
|
|
|
As described in the general [Versioning](#versioning) section, this project follows
|
|
[Clever Semantic Versioning](https://www.w3.org/submissions/semantic-versioning/). New major
|
|
versions (the leading number, e.g., the `3` in `v3.0.0`) are not backwards compatible, as
|
|
permitted by Clever Semantic Versioning rules. Prior to v3.0.0, no attempt is made to maintain
|
|
any level of backwards compatibility with previous versions — there are no migration guides, no
|
|
compatibility shims, and no support for old configurations or data formats. Any effort toward
|
|
backwards compatibility will begin only from v3.0.0 onward.
|
|
|
|
### File Organization
|
|
|
|
All files must be organized in the following project directories:
|
|
|
|
- `/src/cleveragents` - Source code files. MUST NOT contain test files, documentation, mocking code, or examples.
|
|
- `/features/` - Behave-style unit test files (behavioral tests). Mocking code belongs under `/features/mocks/`.
|
|
- `/robot/` - Robot Framework integration tests (separate from unit tests).
|
|
- `/docs/` - Documentation and markdown files (must be written for mkdocs).
|
|
- `/config/` - Configuration files.
|
|
- `/scripts/` - Utility scripts.
|
|
- `/examples/` - Example code.
|
|
|
|
### Specification
|
|
|
|
`docs/specification.md` is the authoritative source of truth for architectural decisions and
|
|
design details. Before beginning any task, always review the relevant parts of the specification
|
|
to understand the fine architectural details. When there is a discrepancy between the current
|
|
codebase and the specification document, always assume the specification document is correct.
|
|
|
|
### BDD Framework
|
|
|
|
- All unit-level and scenario tests use **Behave** under `features/` following the
|
|
Cucumber/Gherkin standard.
|
|
- Integration and end-to-end tests use **Robot Framework** under `robot/`.
|
|
- **Do not write pytest-style unit tests** under any circumstances. There is intentionally no
|
|
`tests/` directory.
|
|
- Mocking code belongs under `features/mocks/`.
|
|
|
|
**Behave-Specific Guidelines:**
|
|
- Before adding a file under `features/steps/`, check for an existing file that covers the same
|
|
behavior and extend it.
|
|
- Steps used only by `foo.feature` must live in `foo_steps.py`.
|
|
- Steps meant for multiple features belong in clearly named, reusable step files.
|
|
|
|
### Testing Tools
|
|
|
|
Run tests using `nox`. Do not invoke `behave`, `robot`, or similar runners directly. If a `nox`
|
|
session is missing required tooling, add the dependency to the session before rerunning.
|
|
|
|
- **All tests (including static checks):** `nox`
|
|
- **Unit tests only:** `nox -e unit_tests`
|
|
- **Integration tests:** `nox -e integration_tests`
|
|
- **Benchmarks:** `nox -e benchmark`
|
|
- **Coverage report:** `nox -e coverage_report`
|
|
|
|
**Coverage threshold:** Unit test coverage must remain above **97%** at all times. Coverage is
|
|
measured with `nox -e coverage_report` and is enforced in CI. This threshold is stricter than
|
|
the general minimum of 85% described in
|
|
[Review and Merge Requirements](#review-and-merge-requirements) — for this project, **97% is
|
|
the enforced merge gate**. Pull Requests that cause coverage to drop below 97% will not be
|
|
merged.
|
|
|
|
**Performance benchmarks:** Include ASV (airspeed velocity) benchmarks for performance-sensitive
|
|
code. Benchmarks are run with `nox -e benchmark`.
|
|
|
|
### Merge Requirements
|
|
|
|
The general [Review and Merge Requirements](#review-and-merge-requirements) apply to this
|
|
project with the following project-specific details:
|
|
|
|
- **Automated checks** are run via `nox`. The full suite (`nox` with no arguments) must pass,
|
|
which includes unit tests, integration tests, type checking (Pyright), linting, formatting,
|
|
and security scanning.
|
|
- **Coverage gate:** 97% (not the general 85% minimum). Measured by `nox -e coverage_report`.
|
|
- **Pre-commit hooks** (configured in `.pre-commit-config.yaml`) enforce commit quality
|
|
locally. All hooks must pass before a commit is accepted. See
|
|
[Commit Quality Enforcement](#commit-quality-enforcement) for setup instructions.
|
|
|
|
### Static Type Checker
|
|
|
|
All code must pass **Pyright** via `nox -e typecheck`, which is also run as part of the default
|
|
test suite with `nox`. Under no circumstances should type checking be ignored — never modify
|
|
configuration files to disable it, and never use inline comments (such as `# type: ignore`) to
|
|
suppress type checking errors.
|
|
|
|
### Commit Quality Enforcement
|
|
|
|
This project uses pre-commit hooks (configured in `.pre-commit-config.yaml`) to automatically
|
|
enforce commit quality. Hooks run linters, formatters, type checkers, and security scanners
|
|
before each commit, and validate commit message format at the `commit-msg` stage via Commitizen.
|
|
Set up the hooks with:
|
|
|
|
```bash
|
|
scripts/setup-dev.sh
|
|
```
|
|
|
|
Commits that fail any hook check will be rejected. This ensures that every commit in the
|
|
repository meets the project's quality standards before it reaches the shared history.
|
|
|
|
### Build and Project Management
|
|
|
|
- **Hatch** for project management.
|
|
- **nox** for task automation.
|
|
- **pyproject.toml** for all configuration.
|
|
- No Makefiles, no legacy approaches, no wrapper shell scripts.
|
|
- Commands should be native to the toolchain (e.g., `hatch env create`, `nox -s test`), not
|
|
shell scripts or make targets.
|
|
- This is a modern project — all tooling must be from the current ecosystem.
|
|
|
|
### Import Guidelines
|
|
|
|
- Ensure all imports (including `from` statements) are at the top of the Python file. Do not
|
|
scatter imports throughout the file or bury them inside functions or methods.
|
|
- Never encapsulate imports inside an indented code block (like an `if`, `try`, or `for`
|
|
statement).
|
|
- The only exception is for imports used exclusively for type checking purposes and only when
|
|
strictly needed to avoid circular dependencies (e.g., `if TYPE_CHECKING:`).
|
|
- Prefer importing specific symbols (`from module import ClassName`) over importing entire
|
|
modules when only a small number of names are used.
|
|
|
|
### Mock Placement
|
|
|
|
ALL mocks, test doubles, and mock implementations must exist only in the `features/` directory.
|
|
Production code in `src/` and utility scripts in `scripts/` must NEVER contain mock
|
|
implementations, test data, or conditional testing behavior. Use dependency injection to swap
|
|
implementations during tests.
|
|
|
|
### LangChain/LangGraph Best Practices
|
|
|
|
#### Graph Design Patterns
|
|
|
|
When creating LangGraph workflows, follow these patterns:
|
|
|
|
- **State Management:** Use TypedDict classes for state definition with clear field documentation
|
|
- **Node Naming:** Use descriptive verb-based names (e.g., `analyze_requirements`, `generate_plan`)
|
|
- **Error Handling:** Include error fields in state for graceful failure tracking
|
|
- **Checkpointing:** Always integrate MemorySaver for workflow resumption capabilities
|
|
- **Conditional Edges:** Use clear decision functions with descriptive names
|
|
|
|
#### LangChain Integration Guidelines
|
|
|
|
- **Provider Abstraction:** Always use LangChain's unified interfaces (BaseLanguageModel, BaseLLM)
|
|
- **Prompt Templates:** Use ChatPromptTemplate or PromptTemplate for all prompts
|
|
- **Streaming:** Implement both sync (invoke) and async (ainvoke) methods with streaming support
|
|
- **Memory Management:** Use appropriate memory classes (ConversationBufferMemory, EntityMemory)
|
|
- **Output Parsing:** Use structured output parsers (StrOutputParser, JsonOutputParser)
|
|
|
|
#### Testing LangChain/LangGraph Code
|
|
|
|
- **Mock Providers:** Use FakeListLLM or custom mock providers for deterministic testing
|
|
- **State Testing:** Test each node's state transformation independently
|
|
- **Graph Testing:** Verify complete workflow execution with expected state transitions
|
|
- **Streaming Testing:** Test both event emission and final results
|
|
- **Memory Testing:** Verify conversation history and entity tracking
|
|
|
|
#### Configuration Best Practices
|
|
|
|
- **Environment Variables:** Use standard LangChain env vars (LANGCHAIN_TRACING_V2, LANGCHAIN_API_KEY)
|
|
- **Observability:** Keep LangSmith/OpenTelemetry disabled by default (user-configurable)
|
|
- **Provider Configuration:** Support multiple providers through configuration, not hardcoding
|
|
- **Retry Logic:** Use LangChain's built-in retry decorators for LLM calls
|
|
- **Token Limits:** Respect model token limits with appropriate chunking strategies
|
|
|
|
#### Common Patterns
|
|
|
|
```python
|
|
# Example: Proper LangGraph node implementation
|
|
async def analyze_requirements(state: PlanGenerationState) -> dict:
|
|
"""Analyze requirements node with proper error handling."""
|
|
try:
|
|
prompt = ChatPromptTemplate.from_template(
|
|
"Analyze these requirements: {requirements}"
|
|
)
|
|
chain = prompt | llm | StrOutputParser()
|
|
analysis = await chain.ainvoke({"requirements": state["description"]})
|
|
return {"analysis": analysis}
|
|
except Exception as e:
|
|
return {"error": f"Analysis failed: {str(e)}"}
|
|
```
|
|
|
|
### Error Handling Examples
|
|
|
|
The following are language-specific examples illustrating the general error and exception
|
|
handling principles described earlier in this document.
|
|
|
|
**Argument validation example:**
|
|
```python
|
|
def process_data(self, data: list[str], threshold: int) -> None:
|
|
"""Process data with validation."""
|
|
# Argument validation first
|
|
if data is None:
|
|
raise ValueError("data cannot be None")
|
|
if not data: # empty list
|
|
raise ValueError("data cannot be empty")
|
|
if not all(isinstance(item, str) for item in data):
|
|
raise TypeError("data must contain only strings")
|
|
if threshold < 0 or threshold > 100:
|
|
raise ValueError(f"threshold must be between 0 and 100, got {threshold}")
|
|
|
|
# Now perform actual logic
|
|
...
|
|
```
|
|
|
|
**Exception propagation guidelines:**
|
|
- Do not use bare `except:` or `except Exception:` without re-raising unless you have specific
|
|
recovery logic.
|
|
- Avoid returning `None` or default values when an error condition exists — raise exceptions.
|