add reusable project skills (6 agnostic, 2 project-specific)

This commit is contained in:
2026-06-02 23:49:04 +02:00
parent 351ba78340
commit 8ffd5c2ce4
7 changed files with 409 additions and 0 deletions
+139
View File
@@ -0,0 +1,139 @@
# CI/CD Pipeline Setup
Blueprint for setting up a GitHub Actions CI/CD pipeline with checks,
builds, draft releases, and supply-chain provenance.
## Overview
The pipeline is organized as a single workflow file (`.github/workflows/ci.yml`)
with jobs that run in dependency order. Tag pushes trigger the build+release
path; branch pushes and PRs trigger only the check path.
## Job Structure
```
check (every push)
├── unit tests
├── linter
├── dependency audit
└── documentation
pages (master only) — deploy API docs
build (tags only)
├── compile for all targets
├── package into archives
└── upload as workflow artifacts
provenance (tags only) — SLSA attestation
release (tags only) — create draft release with all artifacts
```
## Check Job
Runs on every push and pull request. Should include:
- **Check** — verify the project compiles
- **Tests** — run the full test suite
- **Linter** — enforce zero warnings
- **Dependency audit** — check for known vulnerabilities
- **Documentation** — generate and upload API docs (master only)
```yaml
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --workspace
- run: cargo test --workspace
- run: cargo clippy --workspace -- -D warnings
- run: cargo doc --no-deps
```
## Build & Release Job
Only runs on tag pushes matching a version pattern (e.g. `v*`).
- Builds for all target platforms
- Creates release archives (tar.gz, zip)
- Generates SHA256 hashes of all artifacts
- Uploads archives as workflow artifacts
- Sets `draft: true` so releases require manual publishing
```yaml
build:
needs: check
if: startsWith(github.ref, 'refs/tags/v')
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- uses: actions/checkout@v4
- run: <build commands for each target>
- name: Package
run: <create archives>
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
path: builds/
```
## Provenance (SLSA)
Uses the SLSA v3 generator to attest that release artifacts were built
by the CI pipeline from a specific commit.
```yaml
provenance:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
with:
base64-subjects: "${{ needs.build.outputs.hashes }}"
upload-assets: false
```
## Draft Release Job
Creates a draft release after build + provenance both complete. Downloads
the binary artifacts and provenance attestation, then uploads them together.
```yaml
release:
needs: [build, provenance]
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
- uses: softprops/action-gh-release@v2
with:
files: <artifact paths>
body_path: _release.md
draft: true
```
## Guardrails
- Releases are always **drafts** — the maintainer tests before publishing
- Provenance runs as a separate job to avoid interfering with the release
- `upload-assets: false` on the SLSA generator — let the release job handle all uploads
- SHA256 hashes are generated in the build job and passed to provenance
## Key Actions
| Action | Purpose |
|--------|---------|
| `actions/checkout@v4` | Check out source code |
| `actions/upload-artifact@v4` | Store build artifacts between jobs |
| `actions/download-artifact@v4` | Retrieve artifacts in downstream jobs |
| `softprops/action-gh-release@v2` | Create releases (supports `draft: true`) |
| `slsa-framework/slsa-github-generator` | Generate SLSA v3 provenance |
| `dtolnay/rust-toolchain@stable` | Install the language toolchain |
## First-Time Setup
1. Create `.github/workflows/ci.yml` with the job structure above
2. Create `_release.md` with the initial release notes template
3. Enable GitHub Pages if using API docs (Settings → Pages → GitHub Actions)
4. Create a signed tag and push to verify the pipeline
5. Test the draft → publish cycle end-to-end
+50
View File
@@ -0,0 +1,50 @@
# Documentation
When to update each documentation file in the project.
## Hierarchy
| File | Purpose | When to update |
|------|---------|----------------|
| `src/*` | Doc comments on every function | Every change to the function |
| `CHANGELOG.md` | What changed, version, date | Every commit |
| `GOVERNANCE.md` | Roadmap, release process, checklist | When process or plans change |
| `KNOWN_ISSUES.md` | Bugs and limitations users might encounter | When a new issue surfaces or is resolved |
| `DECISIONS.md` | Rationale behind architecture choices | When a significant decision is made |
| `_release.md` | What's new in the current release | Every release |
| `README.md` | User-facing features, build instructions | When features or setup change |
| CI workflow file | Pipeline configuration | When pipeline logic changes |
## Doc Comment Standards
- Every public function must have a doc comment
- Each comment explains *what* the function does, *why* it exists, and
*what edge cases* it handles
- No "Internal helper" or "see module-level docs" placeholders
- Language-standard doc format (e.g. `///` in Rust, `///` in C#, `/** */` in JS)
## Changelog Format
```
## [v0.x.y] — YYYY-MM-DD
### Added
- New features
### Fixed
- Bug fixes
### Changed
- Behavior changes, refactors
### Removed
- Features removed
### Notes
- Operational notes (git resets, CI issues, etc.)
```
## Release Notes
- Contains only the current version's changes — no history from older versions
- Replaced entirely for each new release, not appended
+29
View File
@@ -0,0 +1,29 @@
# Privacy & Security
Privacy-first design principles and security practices.
## Privacy Principles
- **No sensitive paths written to disk** — user data is session-only when possible
- **No scanning of user profiles or system directories** beyond the current user
- **All logged paths sanitized** to strip user-identifiable prefixes
- **User consent tracked via sentinel file**, not a config file with user data
## Input Handling
- Strip control characters from user-provided input before it reaches
config files, logs, or the filesystem — prevents injection and log forgery
- Validate and sanitize all external input before processing
## Security Practices
- `deny(unsafe_code)` enabled where the language supports it — zero unsafe
- Dependency auditing and license checks in CI for every commit
- GPG-signed release tags with build provenance attestation
- Vulnerability disclosure policy documented in the repository
## What We Avoid
- No telemetry, no network connections, no auto-updater
- No admin or root privileges required — runs in user context
- No third-party services in the shipped binary
+53
View File
@@ -0,0 +1,53 @@
# Release Workflow
Versioning, release checklist, signing, and push conventions.
## Versioning
| Bump | When | Example |
|------|------|---------|
| PATCH | Bug fix or small polish after a release | v0.3.1 → v0.3.2 |
| MINOR | New feature or significant restructure | v0.3.x → v0.4.0 |
| MAJOR | Breaking change (1.0+ only) | 1.0.0 → 2.0.0 |
- Version in the project manifest matches the latest release tag
- Tags are created at release time, not per-commit
- Multiple commits can happen between tags
## Draft Release Cycle
```
commit → push → tag v0.x.y → CI builds draft → user tests → publish
```
No release goes live without the user testing the draft first.
## Release Checklist
Before signing a release tag:
- [ ] Impact analysis completed — all call sites for new/changed functions
- [ ] All tests pass
- [ ] Linter passes with zero warnings
- [ ] CHANGELOG.md has an entry for the new version
- [ ] Working tree is clean — no uncommitted changes
- [ ] Release notes file is updated for the new version
After CI completes:
- [ ] Download draft binaries from the releases page
- [ ] Test on target platform — basic workflow, key features
- [ ] Click **Publish release** on the hosting platform when satisfied
## Signing & Push
- Commit with `--no-gpg-sign` (avoids GPG passphrase hang in non-interactive terminals)
- Maintainer amends with `--gpg-sign` before push
- Normal push for fast-forward commits
- `--force-with-lease` for amended commits or tag refreshes
- If force-pushing, delete old tag and re-tag after the new commit lands
## Release Notes
- Release notes file contains only the current version's changes
- Historical release notes on the platform are cleaned per-release
+78
View File
@@ -0,0 +1,78 @@
# Rust Standards
Coding conventions and patterns for the NotAlterra project.
## Error Handling
- Use `anyhow::Result` for fallible functions — no custom error types
- `anyhow::bail!` for early returns with context
- `.with_context(|| format!(...))` on `Result` from external crates
- Avoid `unwrap()` and `expect()` outside of tests and examples
- Use `let _ = fallible_fn()` when deliberately ignoring errors
## File I/O
- Prefer `std::fs` convenience functions for simple operations
(`fs::read()`, `fs::write()`, `fs::read_dir()`)
- Use `PathBuf` for owned paths, `&Path` for borrowed references
- Validate paths early with `path.exists()` before operations
- Use `saturating_sub` for all arithmetic that could underflow
## String Handling
- Use `format!()` for construction, avoid `+` concatenation
- Use `to_string_lossy()` for OsStr → String conversion
- Strip control characters from user input before storage or logging
## Data Structures
- Prefer `Vec` over linked lists or custom containers
- Use `HashSet` for deduplication
- Use `Option<T>` for nullable values — never `null` or sentinel values
- Use `struct` with named fields for complex return types
- Derive `Debug, Clone` on all data structures, `Default` where meaningful
## Pattern: Property Extraction (GVAS parser)
All four extractors follow the same pattern:
1. Scan for property name using `windows(target.len()).position()`
2. Validate the FName header (length dword + null terminator)
3. Skip past the property type name
4. Validate bounds before every index access
5. Return `Option` or `Result` — never panic
When adding a new property type, follow this exact pattern and add a bounds
check on every array access.
## Pattern: TUI Dialogs
All dialogs follow the same structure:
1. Define popup area with `centered_rect_size()` or `centered_rect()`
2. Render `Clear` widget, then a bordered `Block`
3. Compute `inner` area with margin
4. Render title, body, buttons in sequence
5. Call `draw_whale_separator(f, bar, app)` at the bottom
## Pattern: Menu Actions
All menu actions:
1. Validate preconditions (save folder set, backup exists)
2. Show status/spinner if the operation takes time
3. Call through to `ops::*` for actual file operations
4. Log via `guard::log_action()` with sanitized paths
5. Show result dialog (success or error)
6. Refresh dashboard stats
## Clippy
- `-D warnings` enforced in CI — no exceptions
- Fix lints immediately when CI catches new ones
- Common lints that have fired: `collapsible_match`, `manual_is_multiple_of`,
`empty_line_after_doc_comments`, `needless_borrows_for_generic_args`
## Dependencies
- Prefer crates with 50M+ downloads for core functionality
- Avoid platform-specific FFI — prefer pure Rust where possible
- Keep dependency count minimal — each new crate is a review point
- Run `cargo audit` and `cargo deny` before each release
+23
View File
@@ -0,0 +1,23 @@
# Session Protocol
How to structure and execute each development session efficiently.
## Initialization
1. **Full project scan first** — read the build file, all source files,
tests, docs. Ensures the full codebase is understood before making changes.
2. **Check version control status** — identify any uncommitted work from
prior sessions.
3. **Load companion skills**`documentation`, `release-workflow`,
`testing-fuzzing`, `privacy-security`, `ci-cd-pipeline`.
## Task Protocol
1. **Impact analysis before code** — list every file and call site that needs
to change. Present for validation before writing code.
2. **One feature per cycle** — one logical change per commit. Batch docs,
changelog, and version metadata with the feature.
3. **Call site audit on completion** — verify every reference is updated.
Build + test + lint before declaring done.
4. **Draft release cycle** — commit → push → tag → CI builds draft →
user tests → publish. Never ship without draft testing.
+37
View File
@@ -0,0 +1,37 @@
# Testing & Fuzzing
Testing conventions and fuzzing strategy.
## Integration Tests
**Test patterns to follow:**
- Use temporary directories for all file-based tests
- Test both success and failure paths
- Test round-trips: create → process → verify content matches
- Test edge cases: empty inputs, truncated data, missing files
**Where tests live:**
- `tests/` — cross-module integration tests
- `src/` — unit tests in language-standard test modules
- `fuzz/fuzz_targets/` — fuzz targets
## Fuzz Targets
Fuzzing is for code that parses **untrusted binary input**.
**When to add a fuzz target:**
- Custom binary format parsers
- Archive round-trips (create archive from fuzzed inputs → restore → compare)
- Not needed for deterministic operations or wrapping established libraries
**Requirements:**
- Fuzzing requires a nightly toolchain for sanitizer instrumentation
- The project must have a fuzz harness (e.g. cargo-fuzz, libfuzzer, oss-fuzz)
## Coverage Requirements
- Every new feature ships with integration or unit tests
- Fuzz targets for binary parsers and archive round-trips only
- All tests must pass before every commit
- Linter must pass with zero warnings
- Doc coverage check must pass (if implemented)