@anyberg/agent-conventions 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +9 -0
- package/.codex-plugin/plugin.json +6 -0
- package/AGENTS.md +24 -0
- package/README.md +244 -0
- package/agents/code-reviewer.md +36 -0
- package/agents/docs-change-steward.md +71 -0
- package/agents/feature-planner.md +24 -0
- package/agents/implementation.md +35 -0
- package/agents/refactoring-planner.md +38 -0
- package/agents/repo-search.md +32 -0
- package/agents/test-runner.md +34 -0
- package/bin/cli.js +311 -0
- package/gemini-extension.json +5 -0
- package/package.json +47 -0
- package/plugin.json +12 -0
- package/skills/api-design/SKILL.md +18 -0
- package/skills/architecture-planning/SKILL.md +113 -0
- package/skills/backlog-management/SKILL.md +73 -0
- package/skills/backlog-management/backends/github-issues.md +37 -0
- package/skills/backlog-management/backends/markdown.md +34 -0
- package/skills/backlog-management/scripts/detect-backend.sh +30 -0
- package/skills/backlog-management/scripts/generate-policy.sh +49 -0
- package/skills/code-review/SKILL.md +95 -0
- package/skills/code-standards/SKILL.md +73 -0
- package/skills/docs-standards/SKILL.md +95 -0
- package/skills/git-conventions/SKILL.md +95 -0
- package/skills/hatch-workflow/SKILL.md +147 -0
- package/skills/python-best-practices/SKILL.md +107 -0
- package/skills/python-coding-guidelines/SKILL.md +58 -0
- package/skills/python-design-patterns/SKILL.md +28 -0
- package/skills/rust-best-practices/SKILL.md +171 -0
- package/skills/rust-coding-guidelines/SKILL.md +77 -0
- package/skills/rust-design-patterns/SKILL.md +83 -0
- package/skills/task-workflow/SKILL.md +122 -0
- package/skills/tech-debt/SKILL.md +41 -0
- package/skills/test-driven-development/SKILL.md +113 -0
- package/skills/testing-strategy/SKILL.md +35 -0
- package/skills/typescript-coding-guidelines/SKILL.md +55 -0
- package/src/plan.js +116 -0
- package/src/targets.js +95 -0
- package/src/write.js +184 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-best-practices
|
|
3
|
+
description: Use when reading or writing Python files (.py, pyproject.toml, requirements.txt).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Best Practices
|
|
7
|
+
|
|
8
|
+
Follows type-first, functional, and error handling patterns from AGENTS.md. This skill covers language-specific idioms only.
|
|
9
|
+
|
|
10
|
+
## Make Illegal States Unrepresentable
|
|
11
|
+
|
|
12
|
+
Use Python's type system to prevent invalid states at type-check time.
|
|
13
|
+
|
|
14
|
+
**Frozen dataclasses for immutable domain models:**
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class User:
|
|
22
|
+
id: str
|
|
23
|
+
email: str
|
|
24
|
+
name: str
|
|
25
|
+
created_at: datetime
|
|
26
|
+
|
|
27
|
+
# Frozen dataclasses are immutable — no accidental mutation
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Discriminated unions with Literal:**
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from dataclasses import dataclass
|
|
34
|
+
from typing import Literal
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Success:
|
|
38
|
+
status: Literal["success"] = "success"
|
|
39
|
+
data: str
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Failure:
|
|
43
|
+
status: Literal["error"] = "error"
|
|
44
|
+
error: Exception
|
|
45
|
+
|
|
46
|
+
RequestState = Success | Failure
|
|
47
|
+
|
|
48
|
+
def handle_state(state: RequestState) -> None:
|
|
49
|
+
match state:
|
|
50
|
+
case Success(data=data):
|
|
51
|
+
render(data)
|
|
52
|
+
case Failure(error=err):
|
|
53
|
+
show_error(err)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**NewType for domain primitives:**
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from typing import NewType
|
|
60
|
+
|
|
61
|
+
UserId = NewType("UserId", str)
|
|
62
|
+
OrderId = NewType("OrderId", str)
|
|
63
|
+
|
|
64
|
+
def get_user(user_id: UserId) -> User:
|
|
65
|
+
# Type checker prevents passing OrderId here
|
|
66
|
+
...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Protocol for structural typing:**
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from typing import Protocol
|
|
73
|
+
|
|
74
|
+
class Readable(Protocol):
|
|
75
|
+
def read(self, n: int = -1) -> bytes: ...
|
|
76
|
+
|
|
77
|
+
def process_input(source: Readable) -> bytes:
|
|
78
|
+
# Accepts any object with a read() method — no inheritance required
|
|
79
|
+
return source.read()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Python-Specific Error Handling
|
|
83
|
+
|
|
84
|
+
Chain exceptions with `from err` to preserve the original traceback:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
try:
|
|
88
|
+
data = json.loads(raw)
|
|
89
|
+
except json.JSONDecodeError as err:
|
|
90
|
+
raise ValueError(f"invalid JSON payload: {err}") from err
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Structured Logging
|
|
94
|
+
|
|
95
|
+
Use a module-level logger with `%s` formatting (deferred string interpolation):
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
import logging
|
|
99
|
+
|
|
100
|
+
logger = logging.getLogger("myapp.widgets")
|
|
101
|
+
|
|
102
|
+
def create_widget(name: str) -> Widget:
|
|
103
|
+
logger.debug("creating widget: %s", name)
|
|
104
|
+
widget = Widget(name=name)
|
|
105
|
+
logger.debug("created widget id=%s", widget.id)
|
|
106
|
+
return widget
|
|
107
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-coding-guidelines
|
|
3
|
+
description: Rules for simplifying code using Python idioms, comprehensions, operators, and eliminating unnecessary complexity
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Coding Guidelines
|
|
7
|
+
|
|
8
|
+
Python-specific idioms and type-system usage. Builds on the **Universal Code Rules** in `code-standards`; only Python-specific rules are listed here.
|
|
9
|
+
|
|
10
|
+
## Code Style
|
|
11
|
+
|
|
12
|
+
- Use `model_dump()` for Pydantic model serialization; reserve `TypeAdapter` with `mode='json'` for collections or external SDKs needing JSON-compatible primitives — `TypeAdapter.dump_python(mode='json')` guarantees primitive types (dicts/lists/strings) instead of `BaseModel` instances when required by external systems
|
|
13
|
+
|
|
14
|
+
## Type System
|
|
15
|
+
|
|
16
|
+
- Use `isinstance()` for type checking, not `hasattr()`, `getattr()`, `type(obj).__name__`, or discriminator field checks like `part_kind` — Enables proper type narrowing for static analysis and prevents fragile string-based comparisons that break during refactoring
|
|
17
|
+
- Use `Literal` types instead of plain `str` for fixed string value sets in parameters, fields, and return types — Makes valid values explicit in type signatures, enabling static type checkers to catch invalid strings at compile time and improving IDE autocomplete
|
|
18
|
+
- Create type aliases for complex types (3+ union branches, `dict[str, Any] | Callable` patterns, multi-value `Literal`s) or types used 2+ times — skip aliases for simple one-off internal types — Reduces duplication and improves readability for complex types while avoiding unnecessary abstraction that obscures simple inline hints
|
|
19
|
+
- Use `if TYPE_CHECKING:` blocks for optional dependency types with quoted hints — keeps package installable without all deps while preserving type safety — Prevents runtime import errors when optional dependencies aren't installed while maintaining proper type annotations instead of falling back to `Any`
|
|
20
|
+
- Type signatures to match runtime reality — if control flow (e.g., `match`/`case`, API contracts) guarantees only specific types reach a code path, narrow the annotation to exclude impossible types from unions — Prevents confusion, enables better type checking, and documents actual behavior rather than overly permissive signatures that suggest unreachable code paths
|
|
21
|
+
- Fix type errors properly instead of using `# type: ignore` or `# pyright: ignore` — use type annotations, narrowing, or `cast()` with explanatory comments — Prevents masking real type errors and makes code safer; when suppressions are genuinely needed (complex generics, tool limits), document with error codes and justification so reviewers understand the safety reasoning
|
|
22
|
+
- Remove redundant runtime checks when types already constrain the value — prevents noise and maintains type system trust — Redundant assertions (`assert x is not None` for non-`Optional` types, duplicate `isinstance()` checks, etc.) add visual clutter and imply the type system can't be trusted, making code harder to maintain
|
|
23
|
+
- Fix type definitions instead of using `cast()` — adjust generics or remove unnecessary unions to match runtime reality — Prevents masking structural type mismatches that indicate design problems; only use `cast()` when runtime logic guarantees safety but static analysis cannot narrow (e.g., after literal checks or known invariants)
|
|
24
|
+
- Don't add `| None` to `TypedDict` fields marked `total=False` or `NotRequired` — optionality is already expressed — Prevents redundant type declarations and makes it clear that omission (not None) is the intended optional behavior
|
|
25
|
+
- Remove `| None` from type annotations when values are guaranteed to be initialized or always provided — Prevents false optionality in types, making the API clearer and avoiding unnecessary None-checks that can never trigger
|
|
26
|
+
|
|
27
|
+
## Error Handling
|
|
28
|
+
|
|
29
|
+
- Use domain specific exceptions for surfacing errors to the user. These are generally defined in the projects `exceptions` module. Use the `raise x from y` pattern to make the cause clear.
|
|
30
|
+
- Use `!r` format specifier for identifiers in error messages (e.g., `f'Tool {name!r}'` not `f'Tool`{name}`'`) — Provides consistent, unambiguous quoting that clearly delimits values and handles edge cases like empty strings or special characters.
|
|
31
|
+
- Fail fast on explicit user config conflicts; gracefully fallback on internal/auto setting conflicts — Catching user mistakes early with clear errors prevents debugging confusion, while internal fallbacks enable cross-provider compatibility and system resilience when constraints are automatically inferred or propagated
|
|
32
|
+
- Inherit new exception types from existing base exceptions when semantically appropriate — Maintains backward compatibility so user code catching parent exceptions continues to work when new exception types are introduced
|
|
33
|
+
- Trust validated invariants and use defaults over assertions — reduces brittle failures and improves resilience — Assertions crash on unexpected states; defaults and graceful handling keep the system operational when assumptions don't hold, while trusting earlier validation stages avoids redundant defensive checks.
|
|
34
|
+
|
|
35
|
+
## Naming
|
|
36
|
+
|
|
37
|
+
- Use `UPPER_CASE` for module constants; prefix with `_` if internal (`_MAX_RETRIES`) — Distinguishes public API from internal implementation details and signals immutability
|
|
38
|
+
|
|
39
|
+
## Imports
|
|
40
|
+
|
|
41
|
+
- Follow google coding guidelines for imports - prefer to import the module instead of items. For example, when you need to use pydantics `BaseModel`, import `pydantic` and use `pydantic.BaseModel`. The `typing`, `typing_extensions`, `collections`, and their submodules are exceptions.
|
|
42
|
+
- Handle optional dependencies: (1) import inside functions to defer requirements, OR (2) use `try`/`except ImportError` at module level with helpful errors directing to install groups like `[web]`, `[bedrock]` — Keeps the package installable without all dependencies while providing clear guidance when optional features are used
|
|
43
|
+
|
|
44
|
+
## General
|
|
45
|
+
|
|
46
|
+
- Projects generally use `hatch` as a project/environment manager. Use the appropriate skill, check the projects documentation, or `pyproject.toml` for entrypoints for testing, static analysis, etc.
|
|
47
|
+
|
|
48
|
+
## Patterns & Idioms
|
|
49
|
+
|
|
50
|
+
- Use list comprehensions instead of for-loop-with-append patterns — more concise, readable, and often faster for transforming/filtering iterables into lists
|
|
51
|
+
- Use dict comprehensions instead of empty dict + loop — reduces boilerplate and signals intent more clearly
|
|
52
|
+
- Use `any()` instead of for-loops with boolean flags when checking if any element matches a condition — eliminates manual flag management and break statements
|
|
53
|
+
- Use `@cached_property` for expensive computed attributes — defers computation until first access and caches the result
|
|
54
|
+
- Omit parameters that match default values in function/constructor calls — makes non-default configuration more visible
|
|
55
|
+
- Eliminate single-use intermediate variables — reassign or return directly instead of creating `_filtered`, `_copy`, etc.
|
|
56
|
+
- Flatten nested `if` statements with no intervening code into `if condition1 and condition2:` — reduces nesting depth without changing logic
|
|
57
|
+
- Use `x or default` for fallback values instead of verbose if-else blocks — avoid when falsy values (0, `''`, `[]`, `None`) are semantically valid
|
|
58
|
+
- Define `TypeAdapter` instances at module level as constants — avoids repeated initialization overhead on every call
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-design-patterns
|
|
3
|
+
description: Python design patterns including KISS, Separation of Concerns, Single Responsibility, and composition over inheritance. Use this skill when designing a new service or component from scratch and choosing how to layer responsibilities, when refactoring a God class or monolithic function that has grown too large, when deciding whether to add a new abstraction or live with duplication, when evaluating a pull request for structural issues like tight coupling or leaking internal types, when choosing between inheritance and composition for a new class hierarchy, or when a codebase is becoming hard to test because of entangled I/O and business logic.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Python Design Patterns
|
|
7
|
+
|
|
8
|
+
## When to Use This Skill
|
|
9
|
+
|
|
10
|
+
- Designing new components or services
|
|
11
|
+
- Refactoring complex or tangled code
|
|
12
|
+
- Deciding whether to create an abstraction
|
|
13
|
+
- Choosing between inheritance and composition
|
|
14
|
+
- Evaluating code complexity and coupling
|
|
15
|
+
- Planning modular architectures
|
|
16
|
+
|
|
17
|
+
## Patterns
|
|
18
|
+
|
|
19
|
+
- **KISS** — Choose the simplest solution that works. A plain dict beats a factory registry. Complexity must earn its place.
|
|
20
|
+
- **Single Responsibility (SRP)** — Each unit has one reason to change. HTTP parsing, business rules, and data access belong in separate classes.
|
|
21
|
+
- **Separation of Concerns** — Layer as: API handler → Service → Repository. Each layer depends only on layers below it; services must never import from handlers.
|
|
22
|
+
- **Composition Over Inheritance** — Build behavior by combining objects, not extending classes. Use constructor injection with Protocols.
|
|
23
|
+
- **Rule of Three** — Wait until you have three instances before abstracting. Duplication is often better than the wrong abstraction.
|
|
24
|
+
- **Function Size** — Functions over 20–50 lines likely serve multiple purposes. Extract when nesting exceeds 3 levels.
|
|
25
|
+
- **Dependency Injection** — Inject via constructor with Protocol-typed parameters. Production wires real implementations; tests wire fakes.
|
|
26
|
+
- **Don't expose internal types** — Use response schemas at API boundaries, not ORM models or internal dataclasses.
|
|
27
|
+
- **Don't mix I/O with business logic** — Business logic should be pure; data access belongs in repositories, not service methods.
|
|
28
|
+
- **Explicit over clever** — Readable code beats elegant code.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-best-practices
|
|
3
|
+
description: Use when reading or writing Rust files (.rs, Cargo.toml).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust Best Practices
|
|
7
|
+
|
|
8
|
+
Follows the same type-first, functional, error-aware philosophy as `python-best-practices`, expressed through Rust's ownership, enums, traits, and `Result`. This skill covers language-specific idioms; pair it with `rust-coding-guidelines` for the rule list.
|
|
9
|
+
|
|
10
|
+
## Make Illegal States Unrepresentable
|
|
11
|
+
|
|
12
|
+
Rust's type system is the primary tool for correctness. Encode invariants so invalid states won't compile.
|
|
13
|
+
|
|
14
|
+
**Immutable domain models — ownership and `let` give you immutability by default:**
|
|
15
|
+
|
|
16
|
+
```rust
|
|
17
|
+
use std::time::SystemTime;
|
|
18
|
+
|
|
19
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
20
|
+
pub struct User {
|
|
21
|
+
pub id: UserId,
|
|
22
|
+
pub email: String,
|
|
23
|
+
pub name: String,
|
|
24
|
+
pub created_at: SystemTime,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// No `mut`, no interior mutability — instances cannot be changed after construction.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Discriminated unions are enums — model mutually-exclusive states directly:**
|
|
31
|
+
|
|
32
|
+
```rust
|
|
33
|
+
pub enum RequestState {
|
|
34
|
+
Success { data: String },
|
|
35
|
+
Failure { error: AppError },
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn handle_state(state: RequestState) {
|
|
39
|
+
match state {
|
|
40
|
+
RequestState::Success { data } => render(&data),
|
|
41
|
+
RequestState::Failure { error } => show_error(&error),
|
|
42
|
+
}
|
|
43
|
+
// Exhaustive: adding a variant turns every match into a compile error until handled.
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Prefer this over a struct with `Option` fields that "shouldn't" both be set — the enum makes the illegal combination impossible to construct.
|
|
48
|
+
|
|
49
|
+
**Newtype pattern for domain primitives — no accidental mixups:**
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
53
|
+
pub struct UserId(String);
|
|
54
|
+
|
|
55
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
56
|
+
pub struct OrderId(String);
|
|
57
|
+
|
|
58
|
+
fn get_user(id: &UserId) -> Option<User> {
|
|
59
|
+
// The compiler rejects passing an `OrderId` here.
|
|
60
|
+
todo!()
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Parse, don't validate — convert at the boundary into a type that cannot be invalid:**
|
|
65
|
+
|
|
66
|
+
```rust
|
|
67
|
+
pub struct Email(String);
|
|
68
|
+
|
|
69
|
+
impl Email {
|
|
70
|
+
/// The only way to build an `Email`. Downstream code never re-checks.
|
|
71
|
+
pub fn parse(raw: &str) -> Result<Self, AppError> {
|
|
72
|
+
if raw.contains('@') {
|
|
73
|
+
Ok(Email(raw.to_owned()))
|
|
74
|
+
} else {
|
|
75
|
+
Err(AppError::InvalidEmail { value: raw.to_owned() })
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Traits for structural/behavioral abstraction (Rust's answer to `Protocol`):**
|
|
82
|
+
|
|
83
|
+
```rust
|
|
84
|
+
use std::io::Read;
|
|
85
|
+
|
|
86
|
+
/// Accepts anything that can be read — no inheritance, static dispatch.
|
|
87
|
+
fn process_input<R: Read>(mut source: R) -> std::io::Result<Vec<u8>> {
|
|
88
|
+
let mut buf = Vec::new();
|
|
89
|
+
source.read_to_end(&mut buf)?;
|
|
90
|
+
Ok(buf)
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Error Handling
|
|
95
|
+
|
|
96
|
+
Use `Result<T, E>` and `?`. Define a domain error enum with `thiserror` for libraries; preserve the cause chain via `#[source]`/`#[from]`.
|
|
97
|
+
|
|
98
|
+
```rust
|
|
99
|
+
use thiserror::Error;
|
|
100
|
+
|
|
101
|
+
#[derive(Debug, Error)]
|
|
102
|
+
pub enum AppError {
|
|
103
|
+
#[error("invalid email: `{value}`")]
|
|
104
|
+
InvalidEmail { value: String },
|
|
105
|
+
|
|
106
|
+
#[error("failed to read config")]
|
|
107
|
+
Config {
|
|
108
|
+
#[source]
|
|
109
|
+
source: std::io::Error,
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// `#[from]` auto-converts at the `?` site and records the cause.
|
|
113
|
+
#[error("serialization failed")]
|
|
114
|
+
Serde(#[from] serde_json::Error),
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Propagate and enrich — `?` plus context is the equivalent of Python's `raise ... from err`:
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
fn load_config(path: &std::path::Path) -> Result<Config, AppError> {
|
|
122
|
+
let raw = std::fs::read_to_string(path).map_err(|source| AppError::Config { source })?;
|
|
123
|
+
let config = serde_json::from_str(&raw)?; // #[from] handles the conversion
|
|
124
|
+
Ok(config)
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For application/binary code where callers won't `match` on the variant, prefer `anyhow` with `.context(...)`:
|
|
129
|
+
|
|
130
|
+
```rust
|
|
131
|
+
use anyhow::Context;
|
|
132
|
+
|
|
133
|
+
fn run() -> anyhow::Result<()> {
|
|
134
|
+
let raw = std::fs::read_to_string("config.json")
|
|
135
|
+
.context("reading config.json")?;
|
|
136
|
+
let _config: Config = serde_json::from_str(&raw)
|
|
137
|
+
.context("parsing config.json")?;
|
|
138
|
+
Ok(())
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Reserve `unwrap`/`expect` for invariants that genuinely cannot fail, and state the reason:
|
|
143
|
+
|
|
144
|
+
```rust
|
|
145
|
+
let port: u16 = "8080".parse().expect("hardcoded port is a valid u16");
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Structured Logging
|
|
149
|
+
|
|
150
|
+
Use the `tracing` crate with structured fields rather than string interpolation — fields are captured as data, not baked into the message:
|
|
151
|
+
|
|
152
|
+
```rust
|
|
153
|
+
use tracing::{debug, info};
|
|
154
|
+
|
|
155
|
+
pub fn create_widget(name: &str) -> Widget {
|
|
156
|
+
debug!(widget.name = name, "creating widget");
|
|
157
|
+
let widget = Widget::new(name);
|
|
158
|
+
info!(widget.id = %widget.id, "created widget");
|
|
159
|
+
widget
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `#[instrument]` to attach a span (with arguments) to a whole function:
|
|
164
|
+
|
|
165
|
+
```rust
|
|
166
|
+
#[tracing::instrument(skip(db))]
|
|
167
|
+
pub async fn fetch_user(db: &Db, id: &UserId) -> Result<User, AppError> {
|
|
168
|
+
// Every event inside inherits the span, including `id`.
|
|
169
|
+
db.get_user(id).await
|
|
170
|
+
}
|
|
171
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-coding-guidelines
|
|
3
|
+
description: Rules for Rust code — idioms, type system usage, error handling, naming, modules, and eliminating unnecessary complexity. Use when reading or writing .rs files or Cargo.toml.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust Coding Guidelines
|
|
7
|
+
|
|
8
|
+
Applies to all `.rs` files and `Cargo.toml`. Builds on the **Universal Code Rules** in `code-standards`; only Rust-specific rules are listed here. Pairs with `rust-best-practices` (worked examples) and `rust-design-patterns`.
|
|
9
|
+
|
|
10
|
+
## Code Style
|
|
11
|
+
|
|
12
|
+
- Compile static `Regex` patterns once with `LazyLock` (or `once_cell::Lazy`) — avoids recompilation overhead on repeated calls
|
|
13
|
+
- Prefer iterator chains (`.iter().map().filter().collect()`) over manual `for`-loop-with-push — more concise, often faster, and signals intent
|
|
14
|
+
- Let `rustfmt` own formatting — never hand-format; run it before committing and never `#[rustfmt::skip]` without a documented reason
|
|
15
|
+
- Prefer expression-oriented code: return the value of an `if`/`match`/block directly instead of mutating a binding then returning it
|
|
16
|
+
|
|
17
|
+
## Type System
|
|
18
|
+
|
|
19
|
+
- Make illegal states unrepresentable — model mutually-exclusive states as `enum` variants carrying their data, not structs full of `Option` fields that "shouldn't" coexist
|
|
20
|
+
- Use the newtype pattern (`struct UserId(String)`) for domain primitives — the compiler prevents passing an `OrderId` where a `UserId` is expected, unlike a bare `String`
|
|
21
|
+
- Parse, don't validate — convert untrusted input into a validated type at the boundary via `TryFrom`/a fallible constructor, so downstream code receives a type that *cannot* be invalid
|
|
22
|
+
- Prefer `enum` + `match` for sum types; exhaustive `match` gives a compile error when a variant is added — avoid a catch-all `_ =>` arm on domain enums you own, so new variants surface every site that must handle them
|
|
23
|
+
- Accept the most general type that works: take `&str` over `&String`, `&[T]` over `&Vec<T>`, `impl IntoIterator` / `impl AsRef<Path>` for flexible APIs
|
|
24
|
+
- Derive `Debug` on every public type; derive `Clone`, `PartialEq`, `Eq`, `Hash` when semantically meaningful — never hand-write what a derive provides
|
|
25
|
+
- Use `#[non_exhaustive]` on public enums/structs that may grow — forces downstream `match`es to keep a wildcard arm, preserving backward compatibility when variants/fields are added
|
|
26
|
+
- Prefer borrowing (`&T`) in function signatures; take ownership only when the function genuinely needs to store or consume the value
|
|
27
|
+
- Reach for generics with trait bounds for static dispatch; use `dyn Trait` (boxed) only when you need heterogeneous collections or to break compile-time coupling
|
|
28
|
+
- Avoid `unsafe`; when genuinely required, isolate it in the smallest possible function with a `// SAFETY:` comment justifying every invariant relied upon
|
|
29
|
+
- Avoid stringly-typed code — model fixed value sets as enums, not `&str` constants compared by equality
|
|
30
|
+
|
|
31
|
+
## Error Handling
|
|
32
|
+
|
|
33
|
+
- Use `Result<T, E>` for recoverable errors and the `?` operator to propagate — never `unwrap()`/`expect()` in library or production paths
|
|
34
|
+
- Reserve `panic!`, `unwrap`, and `expect` for unreachable invariants and tests; when used, `expect("reason")` must state the invariant that makes it infallible
|
|
35
|
+
- Define domain error enums with `thiserror` for libraries — one variant per failure mode, with `#[from]` for automatic conversion and `#[source]` to preserve the cause chain
|
|
36
|
+
- Use `anyhow` (with `.context("...")`) for application/binary code where callers won't match on the error variant — add context at each layer so the chain reads top-down
|
|
37
|
+
- Add context when propagating across an abstraction boundary — a bare `?` that surfaces a low-level IO error to a user is worse than `.context("reading config from {path}")`
|
|
38
|
+
- Return `Result` instead of sentinel values; use `Option<T>` for genuine absence (not failure), and convert with `.ok_or(...)` / `.ok_or_else(...)`
|
|
39
|
+
- Never silently discard a `Result` — handle it, propagate with `?`, or explicitly `let _ =` with a comment explaining why the error is safe to ignore
|
|
40
|
+
|
|
41
|
+
## Naming
|
|
42
|
+
|
|
43
|
+
- `snake_case` for functions, variables, modules, and files; `PascalCase` for types, traits, and enum variants; `SCREAMING_SNAKE_CASE` for `const`/`static`
|
|
44
|
+
- Prefix internal items with nothing but keep them private (no `pub`) — Rust's module visibility, not naming, marks the public surface; use `pub(crate)` for crate-internal sharing
|
|
45
|
+
- Getters drop the `get_` prefix (`fn name(&self)`, not `fn get_name(&self)`); conversions follow convention: `as_` (cheap borrow), `to_` (expensive/owned), `into_` (consuming)
|
|
46
|
+
|
|
47
|
+
## Imports & Modules
|
|
48
|
+
|
|
49
|
+
- Group `use` statements: `std` first, then external crates, then crate-local (`crate::`, `super::`, `self::`), separated by blank lines — `rustfmt`'s `group_imports` enforces this
|
|
50
|
+
- Import the item you use (`use std::collections::HashMap;` then `HashMap`) rather than fully-qualifying at call sites; for trait methods, import the trait
|
|
51
|
+
- Avoid glob imports (`use foo::*`) except for preludes and inside `#[cfg(test)] mod tests` (`use super::*`)
|
|
52
|
+
- Define a module's public API explicitly with `pub use` re-exports at the crate root — let internal module structure stay refactorable without breaking consumers
|
|
53
|
+
- Keep `mod` declarations and visibility tight — expose the minimum; default to private and widen deliberately
|
|
54
|
+
|
|
55
|
+
## Testing
|
|
56
|
+
|
|
57
|
+
- Put unit tests in a `#[cfg(test)] mod tests` block in the same file; put integration tests in `tests/` exercising the public API only
|
|
58
|
+
- Prefer `assert_eq!`/`assert!` with a message; use `#[should_panic(expected = "...")]` to pin the panic reason; reach for `proptest` when input space is large
|
|
59
|
+
|
|
60
|
+
## General
|
|
61
|
+
|
|
62
|
+
- Use `cargo` as the single entrypoint: `cargo build`, `cargo test`, `cargo clippy`, `cargo fmt` — check the project's docs for any wrapper before assuming
|
|
63
|
+
- Run `cargo clippy` before committing and fix lints rather than `#[allow(...)]`-ing them; an `#[allow]` must carry a comment explaining why the lint is wrong here
|
|
64
|
+
- Set `#![deny(warnings)]` or wire `-D warnings` in CI; never disable lints crate-wide without a documented reason
|
|
65
|
+
- Prefer immutability — `let` over `let mut`; introduce `mut` only where a value genuinely changes
|
|
66
|
+
- Pin the edition in `Cargo.toml` and keep dependencies minimal — each crate is a maintenance and audit surface
|
|
67
|
+
|
|
68
|
+
## Patterns & Idioms
|
|
69
|
+
|
|
70
|
+
- Use `if let` / `let ... else` for single-variant extraction instead of a full `match` with a throwaway arm
|
|
71
|
+
- Use combinators (`map`, `and_then`, `unwrap_or_else`, `filter_map`) over manual `match` ladders on `Option`/`Result` when they read more clearly
|
|
72
|
+
- Use `?` to flatten nested error handling instead of pyramids of `match`
|
|
73
|
+
- Prefer `collect()` into the target type (`Result<Vec<_>, _>`, `HashMap<_, _>`) over building and pushing in a loop
|
|
74
|
+
- Use `impl Trait` in argument and return position to avoid naming complex iterator/closure types
|
|
75
|
+
- Use `derive`d `Default` + struct-update syntax (`Foo { x, ..Default::default() }`) over hand-written constructors with many optional fields; reach for the builder pattern when there are many optional fields with interdependencies
|
|
76
|
+
- Use `Cow<str>` when a function sometimes returns borrowed and sometimes owned data, to avoid forcing an allocation
|
|
77
|
+
- Implement `From`/`TryFrom` for conversions rather than ad-hoc `to_x` helpers — they compose with `?` and `.into()`
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust-design-patterns
|
|
3
|
+
description: Rust design patterns including KISS, Separation of Concerns, Single Responsibility, ownership-driven layering, and composition via traits. Use this skill when designing a new service or component from scratch and choosing how to layer responsibilities, when refactoring a God struct or monolithic function that has grown too large, when deciding whether to add a new trait or generic abstraction or live with duplication, when evaluating a pull request for structural issues like tight coupling or leaking internal types, when choosing between generics and trait objects, when deciding what to own versus borrow, or when a codebase is becoming hard to test because of entangled I/O and business logic.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rust Design Patterns
|
|
7
|
+
|
|
8
|
+
## When to Use This Skill
|
|
9
|
+
|
|
10
|
+
- Designing new components or services
|
|
11
|
+
- Refactoring complex or tangled code
|
|
12
|
+
- Deciding whether to create an abstraction (trait, generic, or newtype)
|
|
13
|
+
- Choosing between generics (static dispatch) and trait objects (dynamic dispatch)
|
|
14
|
+
- Deciding what to own versus borrow at an API boundary
|
|
15
|
+
- Evaluating code complexity, coupling, and testability
|
|
16
|
+
- Planning modular crate and module architectures
|
|
17
|
+
|
|
18
|
+
## Patterns
|
|
19
|
+
|
|
20
|
+
- **KISS** — Choose the simplest solution that works. A plain `enum` + `match` beats a trait-object registry. A free function beats a trait with one impl. Complexity must earn its place.
|
|
21
|
+
- **Single Responsibility (SRP)** — Each type/module has one reason to change. Wire parsing, business rules, and persistence belong in separate types — not one God struct.
|
|
22
|
+
- **Separation of Concerns** — Layer as: handler → service → repository. Each layer depends only on layers below via traits; the service must never know about HTTP types or SQL rows.
|
|
23
|
+
- **Composition Over Inheritance** — Rust has no inheritance by design. Build behavior by combining structs and implementing traits; inject collaborators as fields, not by extending a base type.
|
|
24
|
+
- **Parse, Don't Validate** — Push validation to the boundary and return a type that cannot be invalid (newtype, validated struct, enum). Downstream code trusts the type instead of re-checking.
|
|
25
|
+
- **Make Illegal States Unrepresentable** — Model mutually-exclusive states as `enum` variants, not structs of `Option` fields. Let the type system reject invalid combinations at compile time.
|
|
26
|
+
- **Rule of Three** — Wait until you have three instances before abstracting into a trait or generic. Duplication is cheaper than the wrong abstraction — and the wrong trait is expensive to unwind.
|
|
27
|
+
- **Generics over trait objects by default** — Prefer `fn f<T: Trait>(x: T)` (static dispatch, monomorphized, inlinable). Reach for `Box<dyn Trait>` / `&dyn Trait` only for heterogeneous collections, to break compile-time coupling, or to shrink binary/compile size.
|
|
28
|
+
- **Own vs borrow deliberately** — Take `&T` when you only read, `&mut T` when you mutate in place, `T` only when you must store or consume it. Signatures communicate intent; gratuitous ownership forces needless clones on callers.
|
|
29
|
+
- **Newtype for meaning and invariants** — Wrap primitives (`struct Meters(f64)`) to prevent mixups and to hang validation/behavior off a domain type instead of a bare `String`/`u64`.
|
|
30
|
+
- **Function Size** — Functions over 20–50 lines likely serve multiple purposes. Extract when nesting (especially `match`/`if let` pyramids) exceeds 3 levels — `?` and combinators usually flatten them.
|
|
31
|
+
- **Dependency Injection via traits** — Define a trait for each collaborator; inject it as a generic field or `Box<dyn Trait>`. Production wires real impls; tests wire fakes — no mocking framework required.
|
|
32
|
+
- **Don't expose internal types** — Use dedicated request/response (DTO) types at API boundaries, not your domain structs or DB row types. Implement `From` to convert between layers.
|
|
33
|
+
- **Don't mix I/O with business logic** — Keep core logic pure and synchronous over owned/borrowed data; confine `async`, filesystem, network, and DB calls to the edges (repositories, adapters). Pure cores are trivially testable.
|
|
34
|
+
- **Errors as types, not strings** — Model failure modes as a `thiserror` enum per layer; convert across boundaries with `#[from]`. Reserve `anyhow` for the application edge.
|
|
35
|
+
- **Explicit over clever** — Readable code beats elegant code. Avoid deep generic gymnastics and macro magic when a plain function will do.
|
|
36
|
+
|
|
37
|
+
## Composition & Dependency Injection Example
|
|
38
|
+
|
|
39
|
+
```rust
|
|
40
|
+
// A trait per collaborator — the seam for testing and swapping implementations.
|
|
41
|
+
pub trait UserRepository {
|
|
42
|
+
fn find(&self, id: &UserId) -> Result<Option<User>, RepoError>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The service depends on the abstraction, not a concrete DB type.
|
|
46
|
+
pub struct UserService<R: UserRepository> {
|
|
47
|
+
repo: R, // composition: the repo is a field, not a base class
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl<R: UserRepository> UserService<R> {
|
|
51
|
+
pub fn new(repo: R) -> Self {
|
|
52
|
+
Self { repo }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Pure business logic — no I/O details leak in here.
|
|
56
|
+
pub fn display_name(&self, id: &UserId) -> Result<String, RepoError> {
|
|
57
|
+
Ok(self
|
|
58
|
+
.repo
|
|
59
|
+
.find(id)?
|
|
60
|
+
.map(|u| u.name)
|
|
61
|
+
.unwrap_or_else(|| "anonymous".to_owned()))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Production wires the real repo; tests wire a fake — same generic, no mocks.
|
|
66
|
+
#[cfg(test)]
|
|
67
|
+
mod tests {
|
|
68
|
+
use super::*;
|
|
69
|
+
|
|
70
|
+
struct FakeRepo(Option<User>);
|
|
71
|
+
impl UserRepository for FakeRepo {
|
|
72
|
+
fn find(&self, _id: &UserId) -> Result<Option<User>, RepoError> {
|
|
73
|
+
Ok(self.0.clone())
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[test]
|
|
78
|
+
fn falls_back_to_anonymous_when_user_missing() {
|
|
79
|
+
let service = UserService::new(FakeRepo(None));
|
|
80
|
+
assert_eq!(service.display_name(&UserId::new("x")).unwrap(), "anonymous");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-workflow
|
|
3
|
+
description: Use when creating, managing, or promoting tasks, checking merge readiness, or running as an autonomous agent or subagent that implements a backlog item without a human in the loop. Covers task file structure, lifecycle, acceptance criteria, the merge-readiness gate, independent review, and the Autonomous Mode rules that replace human dialogue with blocked-status evidence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Task Workflow
|
|
7
|
+
|
|
8
|
+
Enforces disciplined task management across the codebase. Backlog operations go through **backlog-management**, never by editing backlog storage directly. Project rules come from `<root>/.planning/policy.yml`. If it is missing, run `backlog-management`'s `scripts/generate-policy.sh` before proceeding — it creates the file from best-practice defaults (backend auto-detected) and prints what it generated; report that in your first response, then continue as if the file always existed. It is never regenerated or overwritten once it exists.
|
|
9
|
+
|
|
10
|
+
## Legacy Layout Migration
|
|
11
|
+
|
|
12
|
+
Task, planning, and architecture files live in the **project root**, never in a global tool directory (e.g. `~/.claude`, `~/.copilot`). Older projects may still use the legacy `.agents/` root. Before creating, reading, or modifying any task file, check once per project:
|
|
13
|
+
|
|
14
|
+
1. If `<root>/.agents/tasks/` or `<root>/.agents/planning/` exists, create `<root>/.planning/` (if missing), then:
|
|
15
|
+
- Move `<root>/.agents/tasks/` → `<root>/.planning/tasks/`.
|
|
16
|
+
- Move `<root>/.agents/planning/` → `<root>/.planning/planned/`.
|
|
17
|
+
- Move `<root>/.agents/architecture.md` → `<root>/.planning/architecture.md`, if present.
|
|
18
|
+
- Move `<root>/.agents/backlog.md` → `<root>/BACKLOG.md`, if present (see **backlog-management**).
|
|
19
|
+
- Remove `<root>/.agents/` once empty.
|
|
20
|
+
2. Do this migration silently and automatically. It is additive and safe. Do not ask for confirmation.
|
|
21
|
+
3. After migration, all instructions below refer only to the new `.planning/` and root `BACKLOG.md` locations.
|
|
22
|
+
|
|
23
|
+
## Task Lifecycle
|
|
24
|
+
|
|
25
|
+
Tasks have two states, each with a directory:
|
|
26
|
+
|
|
27
|
+
| State | Location | Created via |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `planned` | `<root>/.planning/planned/<category>/` | Definition only, no code, no branch |
|
|
30
|
+
| `active` | `<root>/.planning/tasks/` | Move from planning OR create directly |
|
|
31
|
+
|
|
32
|
+
**Filename:** `<type>_<short-description>.md` (e.g. `feat_user-authentication.md`)
|
|
33
|
+
|
|
34
|
+
**Promoting planned → active:** move file, set `Status: active`, add `Started` datetime and `Branch` field, before any code. In the same step, `backlog-management.claim(id)` if not already claimed.
|
|
35
|
+
|
|
36
|
+
## Task File Structure
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
# <Title>
|
|
40
|
+
|
|
41
|
+
**Status:** planned | active
|
|
42
|
+
**Backlog:** <id> # backlog-management ID, mandatory
|
|
43
|
+
**Created:** <datetime>
|
|
44
|
+
**Started:** <datetime> # active only
|
|
45
|
+
|
|
46
|
+
## Branch # active only
|
|
47
|
+
`<type>/<id>-<short-description>` # format from policy.git.branch_format
|
|
48
|
+
|
|
49
|
+
## Goal
|
|
50
|
+
One paragraph: what and why.
|
|
51
|
+
|
|
52
|
+
## Acceptance Criteria
|
|
53
|
+
- [ ] Criterion one
|
|
54
|
+
- [ ] Criterion two
|
|
55
|
+
|
|
56
|
+
Interactive mode: refine through questions with the user until clear, specific, and testable.
|
|
57
|
+
Autonomous mode: copied verbatim from the backlog item; see Autonomous Mode below.
|
|
58
|
+
|
|
59
|
+
## Plan
|
|
60
|
+
Ordered steps. Written before any code. Update + log if it changes.
|
|
61
|
+
|
|
62
|
+
## Log # active only
|
|
63
|
+
- `HH:MM` - What was done (past tense, not intent)
|
|
64
|
+
|
|
65
|
+
## Blockers
|
|
66
|
+
Open questions needing human input. In planned tasks these are pre-start blockers and must be resolved before promotion.
|
|
67
|
+
|
|
68
|
+
## Summary # appended before marking done
|
|
69
|
+
What was built; deviations from Plan.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Merge Readiness Verification
|
|
73
|
+
|
|
74
|
+
A branch is **not** merge-ready until every item below has been *verified in the current session*. Verification means running the stated check and observing its result, not ticking a box from memory. Work top to bottom and record evidence.
|
|
75
|
+
|
|
76
|
+
| # | Item | How to verify | Evidence to capture |
|
|
77
|
+
|---|------|---------------|---------------------|
|
|
78
|
+
| 1 | All Acceptance Criteria met | Re-read each criterion; confirm the implementation satisfies it | Each `- [ ]` flipped to `- [x]` |
|
|
79
|
+
| 2 | Tests cover new code | Run the test suite; confirm new/changed code has tests | Test command + pass count |
|
|
80
|
+
| 3 | No lint or type errors | Run linter and type checker | Commands + clean exit |
|
|
81
|
+
| 4 | Pre-commit passes | Run pre-commit hooks across the diff | Command + clean exit |
|
|
82
|
+
| 5 | `CHANGELOG.md` updated | Only if public behaviour changed | Diff, or "N/A, no public behaviour change" |
|
|
83
|
+
| 6 | Version bumped | Only if `policy.versioning.bump` is not `release-commit-only` | Old → new, or "N/A per policy" |
|
|
84
|
+
| 7 | Summary appended to task file | `## Summary` exists and reflects what was built | Section present |
|
|
85
|
+
| 8 | Backlog items for leftovers | `backlog-management.create` per incomplete Plan step or unresolved Blocker, origin = this task | IDs created, or "none outstanding" |
|
|
86
|
+
| 9 | Backlog linked | `backlog-management.link(id, pr)` done when the PR was opened; status is `in-review` | Op output |
|
|
87
|
+
| 10 | Task file removed | After all above pass, delete from `.planning/tasks/` or `.planning/planned/` | File no longer present |
|
|
88
|
+
| 11 | Branch current with remote main | `git fetch`, confirm rebased or merged on `origin/main` with no conflicts | Command + "up to date" |
|
|
89
|
+
| 12 | Independent review | A reviewer with fresh context (separate subagent or a human) approves the PR via **code-review**; the author never self-approves | Review URL |
|
|
90
|
+
| 13 | Scope and path limits | Diff within `policy.autonomous.max_diff_lines` and `max_files`; no `require_human_review_if_touches` path, or PR labelled `needs-human` | Numbers + label state |
|
|
91
|
+
|
|
92
|
+
After merge is confirmed, and only then: `backlog-management.release(id, done, note)`.
|
|
93
|
+
|
|
94
|
+
**Gate, read before claiming "done" or "ready to merge":**
|
|
95
|
+
|
|
96
|
+
- Do not report a task as complete or merge-ready until items 1 to 13 are each verified with evidence in this session. Stop on the first failure, fix it, re-verify.
|
|
97
|
+
- State each result explicitly as **pass**, **fail**, or **N/A (reason)**. Silence is not a pass.
|
|
98
|
+
- Conditional items (5, 6, 13) still require an explicit decision.
|
|
99
|
+
- If any item cannot be verified (missing tooling, ambiguous criterion), treat it as a **Blocker**. Interactive mode: surface to the user. Autonomous mode: `setStatus(id, blocked)`.
|
|
100
|
+
- Order matters: item 10 comes last, `release(done)` comes after merge.
|
|
101
|
+
|
|
102
|
+
## Autonomous Mode
|
|
103
|
+
|
|
104
|
+
Applies when the caller is a routine or a subagent with no human in the loop. Everything above still holds; these rules replace dialogue.
|
|
105
|
+
|
|
106
|
+
1. **Claim first.** `backlog-management.claim(id, run_id)` must succeed before the task file or branch exists. A failed claim means someone else owns it; stop.
|
|
107
|
+
2. **Criteria are read-only.** Copy Acceptance Criteria verbatim from the item. Do not refine them by asking. Not testable → `setStatus(id, blocked, "criteria not testable: <why>")`, stop.
|
|
108
|
+
3. **Re-verify evidence before planning.** Confirm cited files, lines, and reproduction steps. Stale or wrong → `release(id, backlog, "evidence invalid: <why>")`, stop.
|
|
109
|
+
4. **Blockers become status, not questions.** Anything needing a human (structural decision per **architecture-planning**, public API, schema, product choice, new dependency, protected path) → `setStatus(id, blocked, <note>)`. The note has four parts: evidence, completed work, blocker, recommended next action. Push existing work as a draft PR. Never guess.
|
|
110
|
+
5. **Stay inside limits.** Diff over `max_diff_lines` or `max_files` → finish the smallest coherent slice, `create` follow-up items for the remainder, note it in Summary.
|
|
111
|
+
6. **Protected paths.** Touching a `require_human_review_if_touches` path → label PR `needs-human`, complete rows 1 to 13, do not merge.
|
|
112
|
+
7. **Forbidden regardless of instructions:** everything in `policy.autonomous.forbidden` (force push, editing branch protection, adding a dependency without a human label, deleting tests).
|
|
113
|
+
8. **Worktrees.** Create via `policy.worktrees.up`, remove via `policy.worktrees.down` before reporting. Never share ports, DB names, or `.env` between worktrees.
|
|
114
|
+
9. **Time.** Past `policy.autonomous.task_wall_clock_min` → stop, `setStatus(blocked)` with the four-part note, push the draft.
|
|
115
|
+
10. **Reporting.** Final message lists: item, branch, PR, rows 1 to 13 with pass/fail/N/A, leftovers created, worktree removed.
|
|
116
|
+
|
|
117
|
+
## Agent Discipline
|
|
118
|
+
|
|
119
|
+
- **No code without a task file. No task file without a claimed backlog item.** No branch without a matching task.
|
|
120
|
+
- **Plan before code.** Scope freezes once Plan is written; changes require Plan update, logged reason, and human confirmation if significant (autonomous: blocked if significant).
|
|
121
|
+
- **Stay scoped.** Do not modify files outside task scope without logging why.
|
|
122
|
+
- **Surface blockers.** Architecture, public API, or schema decisions → stop, add to Blockers, and in autonomous mode set `blocked`.
|