Imported from D4id4los/taskboard (
AGENTS.md). Install upstream withnpx skills add D4id4los/taskboard. Copyright stays with the author.
AGENTS.md — Taskboard Project Guidelines
Instructions for LLM coding agents (and humans) working in this repository.
Read this file before writing any code or opening a pull request. The
full engineering context lives in docs/architecture.org,
docs/coding_guidelines.org, and docs/testing_strategy.org.
1. Project Identity & Architecture
- Project Context:
taskboardis a cross-platform task management and reminder system designed for both passive Kiosk displays (low-power, offline-resilient) and interactive Desktop environments. - Core Architecture: Headless Core with an Event-Driven Multi-Actor
Architecture running on the
tokioasync runtime. - Framework Agnostic: The UI layer acts strictly as a "dumb presentation client" that reads from a central state and dispatches events. Do not embed business logic or state mutation inside the UI layer.
- Target Platforms: x86_64 Linux is the dev platform; first-class binary releases for x86_64 Linux & Windows; second priority for 64-bit MacOS, 64-bit ARM Linux & Windows.
- Target Hardware: The app targets not just desktop PCs but low-powered kiosk machines, so good performance and modest memory usage are project goals.
2. Workspace Crate Boundaries
The project is a Cargo workspace using the taskboard-* prefix. Adhere
strictly to these dependency boundaries:
taskboard-domain: Pure Rust data structures, inter-actor message enums, and trait interfaces (Ports). Rule: zero network, UI, or asynchronous runtime dependencies. Pure functions only.taskboard-state: The State Engine actor. Rule: processes incoming commands and updates local state. Sole source of truth for application state; owns the write side ofArcSwap<AppState>.taskboard-sync-nextcloud: Nextcloud REST/WebDAV API actor.taskboard-storage-sqlite: Local disk caching actor usingsqlx.taskboard-ui-slint: Swappable UI layer. Rule: cannot depend on IO crates; presentation logic only.taskboard-app: The main executable runner that bootstraps channels, reads config viafigment, and spawns Tokio actors.
3. Inter-Actor Communication Protocols
Never use orchestrator bottlenecks or shared mutable state locks (RwLock
or Mutex) for cross-actor communication. Use these typed primitives:
- State → UI (reads):
arc_swap::ArcSwap<AppState>— lock-free, zero-contention state reads on the UI rendering thread. - Commands to actors:
tokio::sync::mpscfor point-to-point commands (e.g., UI sending user intents to the State Engine, State Engine commanding the database). - System notifications:
tokio::sync::broadcastfor global status changes (e.g.,SystemEvent::NetworkLost). - Asynchronous queries: embed a
tokio::sync::oneshot::Senderinside anmpscmessage payload for request-response cycles.
4. Tech Stack & Implementation Rules
When generating code, rely exclusively on the approved project stack:
| Category | Approved Crate/Tool | Implementation Rule |
|---|---|---|
| Error handling (domain/libs) | thiserror |
Derive static, strongly-typed error enums (#[derive(Error)]). |
| Error handling (binary) | color-eyre |
Use in taskboard-app to capture and format top-level dynamic errors. |
| Time & dates | chrono |
Use for time, schedules, timezone management, and API formatting. |
| Identifiers | uuid |
Generate v4 (random) or v7 (time-ordered) UUIDs. |
| Configuration | figment |
Hierarchical: defaults → taskboard.toml → TASKBOARD_* env. |
| Logging | tracing |
Contextual spans across .await boundaries; include the log compatibility feature flag. |
All shared dependency versions are pinned once in the root Cargo.toml
[workspace.dependencies] — never add an unpinned duplicate to a member
crate.
5. TDD & Testing Directives
Testability is a primary design goal. Use TDD when developing new features
and fixing bugs. Full strategy: docs/testing_strategy.org. Hard rules:
- Time manipulation: never use
tokio::time::sleepin tests. Usetokio::time::pause()andtokio::time::advance()to fast-forward the async runtime clock deterministically. - Snapshot testing: do not test the UI framework's state internally. Use
instato snapshot test theStateEngine: feed a sequence of commands and verify the resultingAppStatetree. - Fakes over mocks: favor handwritten fakes (e.g., a
HashMapimplementing a repository trait) over pre-programmed mocking libraries for stateful components. - Property testing: use
proptestto validate pure domain logic (sorting, scheduling) with randomized inputs. - A new test failing can be a bug: when a newly written test fails, treat a bug in the production code as equally likely as a bug in the test. Evaluate both before deciding what to change.
- Do not weaken a correct test to make it pass: when a test found an actual bug, you may not weaken it. If fixing production code is out-of-scope, leave the test failing and note the bug in your final report plus a proposed fix prompt for a future agent.
- No quality ceilings: never assert upper bounds on success (decode rate ≤ X%, "at most N" for things that should ideally succeed). Floors and ceilings on garbage input are fine. Litmus test: if the code got strictly better, would this test still pass?
- No text assertions: never assert error message, status, log, or UI
label text (including substrings and disjunctions). Assert typed outcomes
(
matches!(err, E::Variant)) or structural facts instead. - One behavior, one test, lowest layer: don't duplicate a unit test through the engine thread or the integration suite; higher layers test routing only. Don't test test-helpers, derived trait impls, or inline reimplementations of production code.
- Deterministic by construction: no fixed sleeps waiting for state; poll a predicate with a deadline, join handles, or inject a clock seam. Assert terminal state, never transient intermediates or wall-clock time.
- No pins of "today's behavior": if a comment says "current behavior",
the assertion is probably a limitation, not a contract. Test the desired
semantics; if undesired behavior must be tolerated temporarily, name the
test
..._currently_pins_...so it reads as debt.
6. Licensing Headers
The workspace uses a Dual-Tier Licensing model. Include the appropriate
SPDX header at the top of every new .rs file:
- Library/Domain crates (
taskboard-domain,taskboard-state,taskboard-storage-sqlite,taskboard-sync-nextcloud):// SPDX-License-Identifier: MIT OR Apache-2.0 - Application/UI crates (
taskboard-app,taskboard-ui-slint):// SPDX-License-Identifier: AGPL-3.0-only
Every crate except taskboard-ui-slint must carry
#![forbid(unsafe_code)].
7. Artefact Management: Plans & Reports
All AI-generated non-code artefacts MUST be stored under .artifacts/ at
the repository root. Never dump temporary notes, analysis files, or
implementation specs into the root directory or source folders.
.artifacts/
├── plans/ # Forward-looking implementation specs (YYYY-MM-DD-<topic>-plan.md)
├── reports/ # Backward-looking analyses, benchmarks, audits (YYYY-MM-DD-<topic>-report.md)
└── templates/ # Reusable artefact templates (copy out of here; never commit filled-in copies)
- Generate a plan before multi-file refactors, complex feature additions, or architectural changes; follow its template.
- Large plans split into phases use:
YYYY-MM-DD-<overall-name>-phase<N>-<phase-name>-plan.md. - Dispatch plans — multi-chunk work executed by the three-stage
agent protocol (planning → implementation → review, one branch and
PR per chunk) — follow the template at
.artifacts/templates/dispatch-plan.mdand are namedYYYY-MM-DD-<topic>-dispatch-plan.mdinplans/. A dispatch plan chunks an already-accepted design round or roadmap wave; it is written after acceptance, never instead of it. - Improvement ideas that are out-of-scope are recorded as GitHub
Issues in this repository (the markdown backlog file was migrated
to issues #109–#290 on 2026-10-17; the entry ↔ issue mapping lives in
.artifacts/reports/2026-10-17-backlog-to-github-issues-migration-report.md). Conventions:- One issue per idea, title prefixed
backlog:; the body is a summary plus a permalink to the originating plan/report under.artifacts/— deep context stays in the artefact file, never copied wholesale into the issue. - Labels carry the structure the old template kept in prose:
a category label (
architecture,refactoring,testing,performance,ui,dx,ci,security,documentation, ...) and, when a priority is known,p1/p2/p3. - Milestones (
vX.Y.Z) are the "when": assign one only when a decision has been made to schedule the work; issues without a milestone are the unscheduled queue. - Closing an issue as completed is the archive — do not delete issues, and do not reopen closed ones without new information.
- Flaky tests: record each observation as a comment on an issue
labelled
flaky-test+watchlist(one issue per distinct test). Never fix flaky tests opportunistically inside unrelated work; triage them in a dedicated pass over that label query.
- One issue per idea, title prefixed
8. Git & Workflow Rules
-
mainis branch-protected on GitHub: every change entersmainthrough a GitHub PR that must pass its required CI checks — local merges intomainare not available. Agents therefore hand off at "PR open +gh pr checksgreen"; the user performs the merge on GitHub. A failing required check is never bypassed or force-past — it is diagnosed and fixed on the branch. -
Never push to a remote without an explicit instruction from the user.
-
Never merge a feature branch into
main(double-rebase style) without an explicit order from the user; the user reviews changes before they entermain. -
Do create a feature branch for every code change:
<category>/<workspace-scope>-<short-description>(categories:feat/,fix/,refactor/,test/,docs/,ci/,chore/). -
Do commit freely to feature branches as work progresses; never work directly on
main. (Exception: repository-bootstrap commits recorded at the very start of the project's history.) -
Commits use
<type>(<module>): <short-desc>plus a body. Types:feat,fix,refactor,test,docs,ci,chore. -
Parallel worktrees are created next to the project root as
<project-root>-WT-<branch-name>. -
ghis available for CI triage (gh run list/watch/view --log-failed,gh pr checks). -
PR granularity: default to one branch/PR per plan when the plan is one coherent deliverable. Use stacked PRs (each branch based on the previous one, all PRs targeted at
main) only when a single PR does not make sense — e.g. independently reviewable slices of a large effort, or parallelizable work that must land in order. -
Stacked PRs + rebase merges require the rebase dance.
mainis rebase-merged, so when an upstream PR in a stack lands, its commits are rewritten ontomainwith new SHAs — every descendant branch still contains the original (now duplicate) commits and GitHub reports phantom conflicts. The agent doing stacked-PR work should expect this after each merge and fix it proactively — rebasing the branch ontoorigin/mainwhile dropping the already-merged commits:git fetch origin --prune git rebase --onto origin/main <tip-of-the-branch-that-just-landed> <my-branch> # resolve nothing unless real conflicts appear; then: git push --force-with-lease origin <my-branch>When handing the merge to the user, print the exact commands so they can be copy-pasted.
--force-with-leaseis mandatory; never plain--force. PRs are not affected by any of this if nothing is stacked behind them. -
Commit hygiene for rebase merges: every commit lands on
mainverbatim, so each one must stand alone — a correct build, a meaningful<type>(<module>)subject, and no "WIP"/leftover noise. Never put fixes in follow-up commits with duplicate subjects; amend or reword before the PR opens.
9. Versioning
Single global semver across all crates, defined in
[workspace.package] version in the root Cargo.toml and inherited by
every member. Bump releases exclusively via ./scripts/bump_version.sh <major|minor|patch> [--no-push], which verifies a clean tree, creates
chore/release-v<X.Y.Z> off the fetched origin/main, bumps the
version, refreshes Cargo.lock, promotes the CHANGELOG.md release
entry, commits on the branch, and — unless --no-push — pushes the
branch and opens the release PR (chore(release): v<X.Y.Z>). The tag
is never created by the script: main is branch-protected, so after
the human merges the PR they tag the merged main commit and push
vX.Y.Z, which triggers the Release workflow (the script prints those
steps). Agents run the script only in --no-push mode.
10. Code Quality Gates (non-negotiable)
Before every handoff:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace
cargo test --doc --workspace
cargo deny check
cargo llvm-cov nextest --workspace --all-features --no-tests=warn --no-report
scripts/coverage-quarantine-profraw.sh target/llvm-cov-target
cargo llvm-cov report --lcov --output-path lcov.info
The coverage gate is the CI-identical three-command sequence (not the
monolithic cargo llvm-cov --workspace): the quarantine step drops torn
profraws from legitimately SIGKILLed instrumented children before the
merge, so a killed test binary cannot abort the whole report — the
2026-10-13 coverage-flake fix, applied on both sides.
Do not offer a result containing clippy warnings or formatting drift.
