Agent skill

Rust Architect

by sergigp in sergigp/yarrtube

Architecture, naming, and testing conventions guidelines for architecting Rust codebases.

MITAuto-check passedDevelopment

Install Rust Architect

skills CLI
$ npx skills add sergigp/yarrtube --skill rust-architect -a claude-code

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

GitHub CLI
$ gh skill install sergigp/yarrtube rust-architect --agent claude-code

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

Manual copy
$ git clone --depth 1 https://github.com/sergigp/yarrtube.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/rust-architect .claude/skills/rust-architect && rm -rf skills-src

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

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

Facts

Skill name
rust-architect
GitHub stars
133
Token cost
~6.1k tokens
SKILL.md length
2,998 words
Files
1
Skills in repo
1
Repo updated
First seen
Licence
MIT

At a glance

Architecture, naming, and testing conventions guidelines for architecting Rust codebases.

  • Works in 4 steps: The struct holding its dependencies,… → The service's interface: a pub trait… → impl Api for with the implementation of… → …
  • Refactoring Rust code: creating a new module
  • SKILL.md covers Application Layer, Domain Layer, Infrastructure Layer and HTTP Handlers, plus 6 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Rust Architect is an agent skill from sergigp/yarrtube. Architecture, naming, and testing conventions guidelines for architecting Rust codebases. Use when writing, reviewing, or refactoring Rust code: creating a new module or file, adding a repository/port/service/entity/value object, adding an HTTP handler or CLI command, deciding where a new .rs file goes, or writing/naming Rust tests.

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

It sits in Development, covering Refactoring. It works with Rust, YouTube, SQLite and Docker. The repository describes itself as: Self-hosted YouTube playlist & channel synchronizer for your NAS, built on yt-dlp. No ads, no algorithm. The licence is MIT.

When your agent uses it

  • Refactoring Rust code: creating a new module
  • Adding a repository/port/service/entity/value object
  • Adding an HTTP handler
  • Deciding where a new .rs file goes

Example prompts

  • “/rust-architect”

Workflow steps

4 steps, taken from the first numbered list in SKILL.md.

  1. The struct holding its dependencies, followed by an inherent impl with only new.
  2. The service's interface: a pub trait Api: Send + Sync declaring every public operation (doc comments describing the contract go here, on…
  3. impl Api for with the implementation of those operations.
  4. A second inherent impl holding every private helper, never pub.

What it can do on your machine

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

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are rust).

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

  • Network

    No URLs in SKILL.md.

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

  • Credentials

    Names no API keys, tokens, secrets or passwords.

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

Context cost

Rust Architect loads about 6.1k tokens when it runs. Until then it costs about 87 tokens; SKILL.md has 2,998 words of instructions outside code blocks.

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

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

Safety

Auto-check passed

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

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

SKILL.md

The full file from sergigp/yarrtube at commit 8263380, republished under its MIT licence (© sergigp). 2,998 words, ~6,051 tokens.

Download SKILL.mdSave it as .claude/skills/rust-architect/SKILL.md (or your agent's skills folder).
name
rust-architect
description
Architecture, naming, and testing conventions guidelines for architecting Rust codebases. Use when writing, reviewing, or refactoring Rust code: creating a new module or file, adding a repository/port/service/entity/value object, adding an HTTP handler or CLI command, deciding where a new .rs file goes, or writing/naming Rust tests.

Philosophy

We try to follow a Domain Driven Design (DDD) approach with hexagonal architecture (aka ports and adapters) with some opinionated decisions. We try to follow clean architecture and we give a lot of importance to tests.

Layers

We structure our code in three layers: application, domain, and infrastructure.

Application Layer

This is the entry point to the application, the most common case will be http controllers but it can also be a CLI application, subscribers (message consuming from a queue system), etc. The main responsibility of this layer is to VALIDATE entry data and TRANSFORM it into Value Objects (VO). In the case of http this layer is the one responsible of parsing HTTP requests, validating the data and returning HTTP errors if not valid, transforming into VO and calling the domain layer, in the case that this the method needs to return a response it will transform the domain response into a response DTO and return it to the caller with the correct HTTP status code.

Domain Layer

This is where the core BUSINESS LOGIC of the application lives. It is composed of entities, value objects, domain services, and domain events. The main responsibility of this layer is to implement the business rules and logic of the application and ORCHESTRATE calls to the ports in the infrastructure layer. We need to try to not depend on any external libraries or frameworks but some concessions could be done.

Infrastructure Layer

This layer is responsible for implementing ports and COMMUNICATING WITH EXTERNAL SYSTEMS. Most of the time we will be coding repositories to access the database, but external clients to external APIs should be implemented here too. As a convention, even if this is not entirely correct bc this should go in the domain layer, we will place the trait of the repository here, near the implementation bc it's convenient when adding new methods to the repository. repositories/ holds one aggregate's dedicated port implementation (a port injected into a single aggregate's domain service); shared/ holds infra usable across aggregates, regardless of whether it's a port (e.g. the clock, domain events) — see the file structure below. We must not hide behaviour in this layer, repositories should be as simple as possible and should behave like collections with methods like find, find_by_id, save, delete, etc. The domain layer should be the one that implements the business logic, rules and entity transformations, not the infrastructure layer. The infrastructure layer should be as simple as possible and should not contain any business logic like domain event publishing, it should only contain the operations on entities and optional monitoring. Write operations such as save, insert, update should have the entity as a parameter, not the properties. Delete could operate on the entity identifier instead of the entity itself and read methods such as find(id), find_all, etc should always return entities or collections of entities.

File Structure and Naming and other opinions

We try to organize our domain code into modules with the aggregate name as module name. This is an example of how we would structure the code:

src/
  domain/
    <aggregate>/
      <value_object>.rs   # one file per value object, named after the type
      <entity>.rs         # named after the type, even if it repeats the folder name
      errors.rs           # every error type for this aggregate, together
    services/              # every aggregate's use-case services, together, flat
      <use_case>.rs         # one domain service per use case, e.g. widget_creator.rs
  application/              # every external entry point (adapters), one subfolder per interface
    http/  (or grpc/, etc.)
      mod.rs                 # ApiServices + router wiring only
      error.rs               # ApiError + From<ValidationError>
      blocking.rs            # run_blocking
      validation.rs          # required() + shared missing-field messages
      <resource>/
        mod.rs                # handlers only
        dto.rs                # request/response wire types + From<Domain> conversions
    cli/                      # only if the project has a CLI
      mod.rs                  # Cli/Commands definitions (this is the CLI's "routing")
      <command>.rs             # one file per command's orchestration
    subscribers/              # event subscribers, one file per subscriber
      <subscriber>.rs
    tasks/                    # scheduled/background task handlers, one file per task
      <task>.rs
  infrastructure/
    repositories/            # one aggregate's dedicated port implementation
      <implementation>_<port>.rs   # trait + its implementation, together
    client/                  # port-shaped adapters nothing in domain/ injects
      <port>.rs
    shared/                  # infra usable across aggregates (ports or not)
      <thing>.rs
  • We prefer not generic names: not entity.rs, not value_objects.rs, not ports.rs. A file is named after the single type/concept it holds (user.rs holds User, user_id.rs holds UserId).

  • errors.rs is the one deliberately generic name: every error type for an aggregate (use-case error enums) lives together in one file, not scattered across the files that raise them.

  • Every value object constructor fails with the single shared ValidationError(String) (domain/shared/errors.rs), never a per-VO error type. The application layer maps it once (e.g. impl From<ValidationError> for ApiError → 400), so handlers build VOs with a plain ?.

  • Every value object gets its own file. Don't bundle multiple value objects into one "value objects" file.

  • A port is named after the concept it fronts, not the one method it happens to expose (WidgetRepository, not WidgetLookup, even if today it only has an exists method). "Repository" is used loosely for "adapter implementing a domain port," not strictly persistence.

  • infrastructure/repositories/ files are named <implementation>_<port>.rs (sqlite_playlist_repository.rs implements PlaylistRepository with SQLite, youtube_video_downloader_repository.rs implements VideoDownloaderRepository against YouTube/yt-dlp). The prefix signals which technology backs the port. infrastructure/shared/ and infrastructure/client/ files are named after the port/thing itself, not this convention, since they aren't per-aggregate repositories.

  • A repository/port method either reads (returns anyhow::Result<Entity> / anyhow::Result<Vec<Entity>> / anyhow::Result<Option<Entity>>, an entity or collection, never a wrapper/outcome enum) or writes (returns anyhow::Result<()>, void on success). No custom infra error types (RepositoryError/LookupError and friends), infra failures are untyped anyhow::Error. A Repository always returns the entities that its name implies (UserRepository returns User).

  • Business-meaningful outcomes that look like they belong in the repository (e.g. "was this newly created, or did it already exist?") are decided in the domain service, not returned by infra: call find, branch on Some/None, then call insert/save. This trades DB-level atomicity for keeping business logic out of infra, a known, accepted race window, not an oversight.

  • State transitions live on the entity, never as behavior-named repository methods or as anemic domain models operated from domain service. We prefer immutable state transitions: a transition method takes self by value and returns a new Self (via struct-update syntax, Self { field: new_value, ..self }) rather than mutating &mut self. Name such a method with_<field> when it simply sets one field (e.g. with_thumbnail); reserve a verb-based name (mark_downloaded, start_download, reset_for_redownload) for a transition that carries additional domain meaning beyond "set this field".

  • Fn ordering: within any impl block, and among free functions in a file, order is: new (if it exists), then every pub method or trait-impl method (trait-impl methods are the type's public surface even without the pub keyword), then private/helper methods. This applies uniformly, no exceptions — including repositories' row_to_* mapping helpers, which go after the trait-impl methods they support, not before.

  • Domain service layout: every domain service file is laid out in four blocks, top to bottom, so a reader meets the contract before the implementation:

    1. The struct holding its dependencies, followed by an inherent impl with only new.
    2. The service's interface: a pub trait <Service>Api: Send + Sync declaring every public operation (doc comments describing the contract go here, on the trait method, not on the impl).
    3. impl <Service>Api for <Service> with the implementation of those operations.
    4. A second inherent impl <Service> holding every private helper, never pub.

    Guideline (not a hard rule): every public operation (block 3) should read at a glance — aim for 10–15 lines of body after rustfmt. It reads as a sequence of named steps (let channel = self.find_channel(&id)?; self.delete_channel_videos(&id)?; ...), each step a private helper in block 4 named for what it does. Lookup-or-fail matches, multi-arm outcome handling, event building and repeated map_err chains are the usual candidates to extract. When two arms or two operations share logic (e.g. marking a failed download as errored), extract it once rather than shrinking each copy. Exceptions are fine for genuinely complex flows (e.g. reconciliation) where splitting further would scatter one coherent algorithm across helpers that only make sense together — prefer readability over the line count.

    Callers keep depending on the concrete service type (State<PlaylistCreator>, video_downloader: VideoDownloader) and just use the …Api trait to call it; the trait exists to make the contract readable, not to add dynamic dispatch. Supporting types the service returns (e.g. an outcome enum) and constants go above the struct.

    rust
    pub struct WidgetCreator {
        repository: Arc<dyn WidgetRepository>,
    }
    
    impl WidgetCreator {
        pub fn new(repository: Arc<dyn WidgetRepository>) -> Self {
            Self { repository }
        }
    }
    
    pub trait WidgetCreatorApi: Send + Sync {
        fn create(&self, id: WidgetId) -> Result<Widget, CreateWidgetError>;
    }
    
    impl WidgetCreatorApi for WidgetCreator {
        fn create(&self, id: WidgetId) -> Result<Widget, CreateWidgetError> {
            self.ensure_absent(&id)?;
            // ...
        }
    }
    
    impl WidgetCreator {
        fn ensure_absent(&self, id: &WidgetId) -> Result<(), CreateWidgetError> {
            // ...
        }
    }
  • Read models (*View, e.g. TaskView, ChannelView) hold the flat fields they expose, not the entity they're derived from.

  • Domain services are call-agnostic: they know nothing about HTTP, CLI, subscribers, or tasks. Adapting any external trigger into a domain call — parsing/validating input, invoking the domain service, mapping its result back — is the application layer's sole responsibility.

HTTP Handlers

  • A handler extracts only the service(s) it uses (State<PlaylistCreator>), never the whole ApiServices. ApiServices derives FromRef so axum resolves the sub-state.
  • Handlers return typed results, never an opaque Response: Result<(StatusCode, Json<T>), ApiError> when the status varies, Result<Json<T>, ApiError> for a plain 200, Result<StatusCode, ApiError> for bodiless responses.
  • Every failure is an ApiError (status + message, rendered as {"error": ...}). Input is validated with ? only: VOs via From<ValidationError>, missing fields via required(request.field, MISSING_X)? (http/validation.rs).
  • Domain errors are mapped explicitly per variant (Err(e @ CreateChannelError::Lookup(_)) => Err(ApiError::new(StatusCode::BAD_GATEWAY, e))), no catch-all arm. A mapping repeated across handlers gets one small function.
  • Every service call goes through run_blocking (http/blocking.rs), since services are synchronous (SQLite, blocking HTTP). Don't judge per call whether it's needed.
  • Response DTOs are flat and carry only the fields their consumers use. Never compose one resource's response by wrapping or #[serde(flatten)]-ing another response DTO. A genuinely nested JSON object (e.g. a video's source) gets its own DTO.

Testing

Tests are classified by where they enter the code, not by what they fake. Our goal is to couple our tests as much as possible to behaviour instead of implementation, so we can refactor the code without breaking the tests.

  • Acceptance tests: enter through an application adapter (HTTP handler, event subscriber, task). The default and by far the most numerous kind.
  • Behaviour tests: enter through a domain service directly. The exception, reserved for domain logic too complex to cover through an adapter.
  • Value object tests: exhaustive validation rules, one file per value object.
  • Infrastructure tests: a single adapter (repository, client) against its real dependency.

Acceptance and behaviour tests follow the same rules for persistence and test doubles (see below): persistence is real, only external dependencies are faked. The Playwright suite in smoke-tests/ is the true end-to-end layer (real binary, real browser) and lives outside these conventions.

Show full SKILL.md (1,432 more words)Show less

Acceptance Tests

This tests the domain logic and the validations at application level. We will place this tests in application (for example in http controllers or event subscribers) and the test will be the type of "I receive this request and I expect this response and these collateral effects". In the case of event subscribers we will send events and assert the final state of the repositories. Very similar for Tasks, we will create tasks and assert the final state of the repositories.

HTTP acceptance tests call the handler function directly with only the service it uses, not through a Router. A small helper per handler unwraps the Json (create(service, request) -> Result<(StatusCode, ChannelResponse), ApiError>). Route wiring (paths, methods, which handler each route reaches) is covered by the Playwright suite in smoke-tests/, not by Rust tests: every route the UI calls must be exercised by some spec there.

Every test has the same 7 steps, top to bottom and inline: TestDatabase + repositories/fakes → seed them → build the service with Service::new(..) → build the request → call the handler → assert the response → assert side effects (repositories, outbox events, scheduled tasks, fake state).

  • No fixture structs that bundle fakes/repositories and no service() → service_with() builder chains. They hide which dependencies a test uses and make it easy to skip asserting them. The one allowed exception is a constructor helper for a service with many ports no test observes: it takes the asserted repositories as parameters and fills in the rest (channel_video_reconciler(&db, channel_repository, ..)). A test that observes one of the ports the helper fills in builds the service inline with Service::new(..) instead of growing the helper or adding a second one.
  • Assert whole typed values in one assert_eq!: Ok((StatusCode::CREATED, some_channel_response())), Err(ApiError::bad_request("<exact message>")), repository.list().unwrap() == vec![..]. Never field by field, never len(), never raw JSON (that only re-tests serde).
  • Requests and expected values are built from a valid default overridden with struct update syntax (CreateChannelRequest { quality: None, ..create_request("@x") }).
  • Seed state directly into repositories, never by calling another handler: a test only exercises the handler it names.
  • DTO mapping is covered through handler responses, not with standalone DTO tests.
  • Module layout: imports, then every it_should_* test, then helpers.

A domain service reached from several adapters (e.g. the reconcilers, called from a task, a subscriber and an HTTP handler) has its logic tested exhaustively through one primary adapter, the one that does the recurring work (the task). Every other adapter tests only what differs for it (e.g. force_reconcile not rescheduling, error mapping) plus one happy path proving the service is wired. When an adapter's call path starts to diverge inside the service, the tests for that difference go in that adapter.

Tests whose request is rejected before reaching the service (validation 4XX) assert the response only and don't need a database: their any_<service>() helper builds repositories on an unmigrated in-memory connection (Connection::open_in_memory()), so a request that wrongly got through fails loudly instead of passing.

Behaviour Tests

Ideally we should not tests domain services at all because the logic there is tested from acceptance tests. There could be exceptions for very complex domain logic that is hard to test from application layer, but this should be the exception and not the rule. When one is justified, it calls the domain service directly and asserts its result plus the final state of the repositories, under the same persistence and fakes rules as acceptance tests: a behaviour test is never a reason to keep a fake of our own persistence alive. Before adding one, check the acceptance tests of the adapters calling that service don't already cover it.

Persistence and Test Doubles

Persistence is real, not faked. Because our database is an embedded SQLite file, which is fast, cheap to create and is the production engine, acceptance and behaviour tests wire the real Sqlite* repositories against a fresh database per test instead of fakes. This catches SQL, row-mapping, ordering, upsert and constraint bugs that a hand-written fake silently re-implements (and drifts from). Concretely:

  • Every test starts with let db = TestDatabase::new(); (infrastructure/shared/sqlite_connection.rs): a freshly migrated database file in its own temp directory, removed when the test ends, so tests run in parallel without sharing state. No pool of pre-migrated databases: migrating a fresh one costs a few milliseconds.
  • Each repository gets its own connection, SqliteXRepository::new(db.connection()) (or db.shared_connection() for the ones taking Arc<Mutex<Connection>>), opened with the same sqlite_connection::open as production (WAL, busy timeout), so tests exercise the real multi-connection setup.
  • Seed and assert through the port traits (insert, list, find, list_for_...), never through raw SQL. When a whole-table assertion needs a read the port doesn't have (e.g. listing every video), add a #[cfg(test)] inherent method on the SQLite implementation rather than widening the port for tests.
  • Storage-assigned values (autoincrement ids) are deterministic in a fresh database, so assert them as real values (ChannelVideo { id: 1, ..ChannelVideo::create(..) }) rather than masking them.
  • Domain events are asserted through the real outbox: the real SqliteEventPublisher writes them, and the test reads them back with SqliteEventRepository::list_eligible(), comparing against the expected ScheduledEvent rows (a pending_event(id, DomainEvent) helper builds them). Scheduled tasks likewise through the real SqliteTaskRepository (list_non_completed()).

Fakes are for everything else: out-of-process or side-effecting dependencies (external APIs such as YouTube, yt-dlp, outbound HTTP, and, for now, filesystem-backed ports). The fakes will be hand-written and will be as simple as possible, they will not use any mocking library. The fakes will be state-based, for example a fake backed by a Mutex<Vec<T>>, and may offer constructors for failure scenarios (e.g. failing(), unavailable()).

The Clock is treated like a port and faked (FixedClock) so time is deterministic; it is also what makes stored timestamps (e.g. event created_at) assertable.

Value Object Tests

Each value object's validation rules are tested exhaustively in its own file (every accepted and rejected input, asserting the exact Ok(vo) / Err(ValidationError(message))). Acceptance tests must not repeat those rules: per endpoint, one invalid-value test proves the ValidationError → 4XX mapping, plus tests for the adapter's own checks (e.g. a required field missing from the request). Services take value objects as parameters, so the type system already guarantees a handler validates before calling the domain.

Infrastructure Tests

Infrastructure tests use the real dependency, colocated with the code. Some examples:

  • Embedded engine (e.g. SQLite) -> a real instance, not a container. Since acceptance tests already exercise the SQLite repositories end to end, their own tests focus on what acceptance tests don't reach: row-mapping edge cases, ordering, conflict handling.
  • Networked service (Postgres, Kafka, Redis...) -> testcontainers.
  • Outbound HTTP dependency -> the real HTTP client against a fake/mock server, not a container.

Keep tests in #[cfg(test)] mod tests in the same file as the code they test by default. Only introduce a top-level tests/ directory (which needs a lib.rs, turning the project into a library + thin binary) when a project independently justifies it — not just to match a template.

Testing Opinions

  • Test naming: it_should_<behaviour>[_if_<condition>], short enough to read at a glance — drop the _if_... part when there's no meaningful precondition beyond "given valid input". This naming focuses on behaviour instead of implementation.
    • Name the behaviour, not the mechanics: no HTTP status codes, event/task type names, or "and store nothing / change nothing" side-effect lists — the asserts carry those details.
    • Happy path is the verb: it_should_create_a_channel, it_should_list_all_playlists, it_should_download_the_video.
    • Errors use fail: it_should_fail_to_create_if_path_missing, it_should_fail_if_invalid_handle_provided.
    • Tolerated no-ops use skip / ignore: it_should_skip_if_playlist_is_gone, it_should_ignore_reconcile_of_a_missing_channel.
    • When one test module covers several handlers, keep the operation in the name so it stays unambiguous (fail_to_delete_… vs fail_to_reconcile_…).
  • Tests should be as deterministic as possible. In the case of time we will use a fake clock that we can control. This will behave as a regular port.

Other Opinions

  • Code should be as functional as possible, we prefer mapping/folding/filtering over imperative loops, and immutable data over mutable data. We will use Option and Result types instead of nulls and exceptions. We will use iterators instead of for loops when possible. We will use map, filter, fold, etc. instead of imperative loops when possible.

Deliberate deviations. Don't "fix" these

Some of these are deliberate deviations from DDD/hexagonal orthodoxy, for convenience or to avoid overengineering:

  • Port traits live in infrastructure/, not domain/ (dependency direction inverted, traded for editing convenience).
  • Repositories have no typed error enums, everything is anyhow::Error until the domain service gives it meaning.
  • An idempotent "create" use case is two non-atomic port calls (find then insert), not one atomic upsert.
  • Orchestration lives in domain/services/, not a separate application/ layer. The application/ folder holds only adapters (HTTP, CLI, subscribers, tasks) that translate an external trigger into a domain call — business orchestration itself never lives there.

If code looks like it violates textbook architecture in one of these ways, it's probably intentional. Ask before changing it.

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

Files

Just SKILL.md in .claude/skills/rust-architect of sergigp/yarrtube.

Open the folder on GitHubat commit 8263380

Compare with similar skills

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

Rust Architect compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Rust Architect this skillsergigp/yarrtube133—~6.1kAutomated safety check: PassMIT
RTK Rust Design Patternsrtk-ai/rtk83k—~1.9kAutomated safety check: PassApache-2.0
Pnpm Engineteambit/bit18k—~1.9kAutomated safety check: PassCustom licence
Code Refactor Reviewkcsujeet/ilamy-calendar3512 repos~1.6kAutomated safety check: PassMIT
Create Release Checklistsoftware-mansion/smelter734—~1.9kAutomated safety check: NotesCustom licence
Coding Agentmastra-ai/mastra29k—~2.3kAutomated safety check: PassCustom licence

Similar skills

  • Describes seven Rust design patterns for the RTK CLI filter modules, with when to use each, RTK examples, and notes on when a pattern is overkill.

    83k GitHub stars~1.9k tokensUpdated today
    DevelopmentAuto-check passed
  • Pnpm Engine

    teambit/bit

    Work on the pnpm Rust engine (@pnpm/napi, the pacquet crates) that bit install runs through.

    18k GitHub stars~1.9k tokensUpdated today
    DevelopmentAuto-check passed
  • Code Refactor Review

    kcsujeet/ilamy-calendar

    Reviews code changes for reuse, composition, codebase consistency, and slop.

    351 GitHub starsUsed in 2 repos~1.6k tokens
    DevelopmentAuto-check passed
  • Create Release Checklist

    software-mansion/smelter

    Generate a GitHub release-checklist issue for a full (non-RC) release of the Smelter server and/or the TypeScript SDK.

    734 GitHub stars~1.9k tokensUpdated today
    DevelopmentAuto-check: notes
  • Coding Agent

    mastra-ai/mastra

    Authoring playbook for building agents that write, edit, review, or refactor code.

    29k GitHub stars~2.3k tokensUpdated today
    DevelopmentAuto-check passed
  • Qovery Console coding standards, architecture guidelines, naming conventions, testing practices, and development workflows.

    227 GitHub stars~924 tokensUpdated today
    DevelopmentAuto-check passed

Categories

Questions about Rust Architect

What does Rust Architect do?

Architecture, naming, and testing conventions guidelines for architecting Rust codebases. Rust Architect is an agent skill from sergigp/yarrtube. Architecture, naming, and testing conventions guidelines for architecting Rust codebases.

When should I use Rust Architect?

Rust Architect fits situations like: refactoring Rust code: creating a new module; adding a repository/port/service/entity/value object; adding an HTTP handler; deciding where a new .rs file goes.

How do I install Rust Architect in Claude Code?

Run `npx skills add sergigp/yarrtube --skill rust-architect -a claude-code`. Or copy the skill folder (.claude/skills/rust-architect in sergigp/yarrtube) into .claude/skills/rust-architect in your project. Claude Code loads it when a task matches its description.

How do I install Rust Architect in Codex?

Run `npx skills add sergigp/yarrtube --skill rust-architect -a codex`. Or copy the skill folder (.claude/skills/rust-architect in sergigp/yarrtube) into .agents/skills/rust-architect in your project. Codex loads it when a task matches its description.

Can I use Rust Architect in Cursor, Gemini CLI or GitHub Copilot?

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

What does Rust Architect need to run?

SKILL.md names no scripts, command-line tools or credentials: Rust Architect is instructions for the agent only.

Does Rust Architect access the network?

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

Is Rust Architect safe to install?

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

What licence does Rust Architect use?

Rust Architect is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Rust Architect use?

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

What are the alternatives to Rust Architect?

Skills that share tags, products or a category with Rust Architect: RTK Rust Design Patterns (rtk-ai/rtk, 83k stars), Pnpm Engine (teambit/bit, 18k stars), Code Refactor Review (kcsujeet/ilamy-calendar, 351 stars) and Create Release Checklist (software-mansion/smelter, 734 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Rust Architect?

sergigp (a GitHub user) maintains it in sergigp/yarrtube, which has 133 GitHub stars. The repository was last updated on October 7, 2026.

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