deskctl/openspec/changes/archive/2026-03-25-test-and-tooling-foundation/design.md
Hari 3819a85c47
tests and tooling (#4)
* init openspec

* clean out src, move mod into lib, remove trash

* create tests

* pre-commit hook

* add tests to CI

* update website

* README, CONTRIBUTING and Makefile

* openspec

* archive task

* fix ci order

* fix integration test

* fix validation tests
2026-03-25 19:29:59 -04:00

6.5 KiB

Context

Phase 1 stabilized the runtime contract, but the repo still lacks the structure and tooling needed to keep that contract stable as contributors add features. The current state is mixed:

  • Rust checks exist in CI, but there is no explicit cargo test lane and no Xvfb integration lane.
  • The repo has site-local Prettier config under site/, but no root-level contributor workflow for formatting or hooks.
  • Integration-style tests are starting to appear, but the crate is still binary-first and does not yet have a clean top-level tests/ structure.
  • An empty src/tests/ directory exists, which suggests the intended direction is not yet settled.

This change should establish the repo-quality foundation before Phase 3 agent features and Phase 4 distribution work expand the maintenance surface further.

Goals / Non-Goals

Goals:

  • Define a clean test architecture with a centralized top-level tests/ directory for integration coverage.
  • Introduce the crate structure needed for integration tests to import the project cleanly.
  • Add a real Xvfb-backed CI lane and make CI validate the same commands contributors run locally.
  • Define one formatting and hook workflow for the repo instead of ad hoc tool usage.
  • Keep the site formatting story integrated without turning the entire repo into a Node-first project.

Non-Goals:

  • New runtime capabilities such as wait-for-window, version, or broader read commands.
  • npm distribution, crates.io publishing, or release automation changes.
  • Introducing both Husky and pre-commit, or multiple competing hook systems.
  • Adding rustfmt.toml unless we have a concrete non-default formatting requirement.

Decisions

1. Convert the crate to library + binary

This change will introduce src/lib.rs and make src/main.rs a thin binary wrapper.

Rationale:

  • Top-level Rust integration tests in /tests should import the crate cleanly.
  • Shared test support and internal modules become easier to organize without abusing src/main.rs.
  • This is the standard structure for a Rust project that needs both unit and integration coverage.

Alternatives considered:

  • Keep the binary-only layout and continue placing all tests inside src/. Rejected because it makes integration coverage awkward and keeps test structure implicit.
  • Put integration helpers in src/tests without a library target. Rejected because it preserves the binary-first coupling and does not create a clean external-test boundary.

2. Standardize on top-level /tests for integration coverage

Integration tests will live under a centralized /tests directory, with shared helpers under tests/support/.

Rationale:

  • The runtime-facing flows are integration problems, not unit problems.
  • A centralized /tests layout makes it clear which tests require Xvfb or daemon orchestration.
  • It keeps src/ focused on application code.

Alternatives considered:

  • Keep helpers in src/test_support.rs. Rejected because it mixes production and integration concerns.

3. Standardize on pre-commit, not Husky

This change will define one hook system: pre-commit.

Rationale:

  • The repo is Rust-first, not root-Node-managed.
  • Husky would imply a root package.json and a Node-first workflow the repo does not currently have.
  • pre-commit can run Rust and site checks without forcing the whole repo through npm.

Alternatives considered:

  • Husky. Rejected because it introduces a root Node workflow for a repo that is not otherwise Node-based.
  • Both Husky and pre-commit. Rejected because dual hook systems inevitably drift.
  • No hooks. Rejected because contributor ergonomics and CI parity are explicit goals of this phase.

4. Keep formatting opinionated but minimal

This phase will use default rustfmt behavior and site-local Prettier behavior. A root rustfmt.toml will only be added if the implementation reveals a real non-default formatting need.

Rationale:

  • A config file with no meaningful opinion is noise.
  • What matters more is that CI and hooks actually run formatting checks.
  • The site already has a working Prettier configuration; we should integrate it rather than duplicate it prematurely.

Alternatives considered:

  • Add rustfmt.toml immediately. Rejected because there is no concrete formatting policy to encode yet.
  • Add a root Prettier config for the whole repo. Rejected because it would broaden Node tooling scope before there is a clear need.

5. CI should call stable local entrypoints

This phase should define one local command surface for validation, and CI should call those same commands instead of hand-coded bespoke steps where practical.

Candidate checks:

  • format check
  • clippy
  • unit tests
  • Xvfb integration tests
  • site formatting check

Rationale:

  • Local/CI drift is one of the fastest ways to make an open source repo unpleasant to contribute to.
  • Contributors should be able to run the same validation shape locally before pushing.

Alternatives considered:

  • Keep all validation logic encoded only in GitHub Actions. Rejected because local parity matters.

Risks / Trade-offs

  • [Introducing src/lib.rs creates some churn] → Keep main.rs thin and preserve module names to minimize callsite disruption.
  • [Xvfb CI can be flaky if test fixtures are underspecified] → Keep fixture windows simple and deterministic; avoid broad screenshot assertions early.
  • [Pre-commit adds a Python-based contributor dependency] → Document installation clearly and keep hooks fast so the value exceeds the setup cost.
  • [Formatting/tooling scope could sprawl into site build work] → Limit this phase to formatting and validation, not full site build architecture.

Migration Plan

  1. Introduce src/lib.rs and move reusable modules behind the library target.
  2. Move integration support into top-level tests/support/ and create real /tests coverage for Xvfb-backed flows.
  3. Add local validation entrypoints for formatting, lint, and tests.
  4. Add a root hook configuration using pre-commit.
  5. Update CI to run unit tests, Xvfb integration tests, and relevant formatting checks on pull requests and main.
  6. Update contributor docs so local validation, hooks, and test structure are discoverable.

Rollback strategy:

  • This phase is repo-internal and pre-1.0, so rollback is a normal revert rather than a compatibility shim.

Open Questions

  • Whether the local validation entrypoint should be a Justfile, Makefile, or another lightweight wrapper.
  • Whether site validation in this phase should be limited to Prettier checks or also include astro check.