hands-on-test
Performs manual hands-on testing of a web application using playwright-cli. Spawns the dev server if needed, navigates to pages, performs browser actions,…
Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries.
$ npx -y skills add ktnyt/cclsp --skill architecture --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/architectureContext preview
The summary Claude sees to decide when to auto-load this skill.
Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries.
name: architecture description: Guides the design of safely disposable code through contracts (traits/interfaces) and dependency inversion. Use when designing new modules, refactoring existing code, or making architectural decisions about component boundaries.
Every implementation should be disposable. The system's correctness is defined by its contracts, not by any particular implementation behind them.
> Define **what** a component does (contract), not **how** it does it (implementation). > Any implementation that satisfies the contract is interchangeable.
A component is "safely disposable" when: 1. Its behavior is fully described by a contract (trait, interface, protocol) 2. No consumer depends on implementation details 3. It can be deleted and rewritten from the contract alone without breaking the system
Start with the contract. Write the trait/interface **before** any implementation.
**Rust:**
pub trait UserRepository {
fn find_by_id(&self, id: UserId) -> Result<Option<User>, RepoError>;
fn save(&self, user: &User) -> Result<(), RepoError>;
}**Go:**
type UserRepository interface {
FindByID(ctx context.Context, id UserID) (*User, error)
Save(ctx context.Context, user *User) error
}**TypeScript:**
interface UserRepository {
findById(id: UserId): Promise<User | null>;
save(user: User): Promise<void>;
}Rules:
Errors are part of the contract. Define domain-level error types that hide infrastructure details.
#[derive(Debug, thiserror::Error)]
pub enum RepoError {
#[error("entity not found: {0}")]
NotFound(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("internal error")]
Internal(#[source] Box<dyn std::error::Error + Send + Sync>),
}The `Internal` variant wraps infrastructure errors without leaking them into the contract.
Each implementation is a disposable artifact. Write it knowing it can be thrown away.
pub struct PgUserRepository {
pool: PgPool,
}
impl UserRepository for PgUserRepository {
fn find_by_id(&self, id: UserId) -> Result<Option<User>, RepoError> {
// Postgres-specific code here.
// This entire struct is disposable.
}
fn save(&self, user: &User) -> Result<(), RepoError> {
// ...
}
}Consumers accept the contract, never the concrete type.
pub struct UserService<R: UserRepository> {
repo: R,
}
impl<R: UserRepository> UserService<R> {
pub fn new(repo: R) -> Self {
Self { repo }
}
pub fn get_user(&self, id: UserId) -> Result<Option<User>, RepoError> {
self.repo.find_by_id(id)
}
}In Go, this is implicit — accept the interface:
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}Write tests that verify the contract, not the implementation. These tests can be reused across implementations.
// A test suite that works for ANY UserRepository implementation.
fn test_repository_contract(repo: &impl UserRepository) {
let user = User::new("test@example.com");
repo.save(&user).unwrap();
let found = repo.find_by_id(user.id()).unwrap();
assert_eq!(found, Some(user));
}
#[test]
fn pg_repo_satisfies_contract() {
let repo = PgUserRepository::new(test_pool());
test_repository_contract(&repo);
}
#[test]
fn in_memory_repo_satisfies_contract() {
let repo = InMemoryUserRepository::new();
test_repository_contract(&repo);
}Organize code so that dependencies always point inward toward the domain:
src/ ├── domain/ # Contracts + domain types (zero external deps) │ ├── model.rs # Domain entities and value objects │ ├── repo.rs # Repository contracts (traits) │ └── service.rs # Domain services using contracts ├── infra/ # Disposable implementations │ ├── pg_repo.rs # Postgres implementation │ └── http.rs # HTTP handlers └── main.rs # Wiring (connects contracts to implementations)
Before considering a component done, verify:
| Anti-Pattern | Problem | Fix | |---|---|---| | Fat contract | 10+ methods, hard to implement | Split into focused contracts | | Leaky contract | Infrastructure types in signatures | Use domain types only | | Concrete dependency | Consumer imports the struct directly | Accept the trait/interface | | God module | One module does everything | Extract contracts and split | | Premature abstraction | Contract with only one possible implementation forever | Wait until there's a reason to abstract |
Not everything needs a contract. Skip abstraction when:
cclsp is a Model Context Protocol (MCP) server that seamlessly integrates LLM-based coding agents with Language Server Protocol (LSP) servers.
Repo: ktnyt/cclsp
Performs manual hands-on testing of a web application using playwright-cli. Spawns the dev server if needed, navigates to pages, performs browser actions,…
Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web…
Request a security expert assessment for code changes that touch child process spawning, file system access, configuration loading, or environment variable…