Requirements: Repo tooling setup
Introduction
tiny-harness has no code, no toolchain and no CI today: the repository holds only agent instructions and the-loop's docs trees. Issue #2 asks for the full Python project scaffold — packaging, linting, type checking, tests, commit linting, git hooks, CI, automated PyPI releases and a documentation site — with a single hello_world function as the first piece of code.
The work is rated risk tier 4: it adds .github/workflows/** (a sensitive path), and the release workflow writes to main and publishes to PyPI.
Requirements
Requirement 1 — Python project and package layout
User story: As a contributor, I want a standard pyproject.toml project managed by uv, so that one command gives me a working environment.
Acceptance criteria (EARS)
- The repository SHALL declare the project in
pyproject.tomlwith distribution nametiny_harnessand import packagetiny_harness. - The project SHALL require the latest stable CPython 3 release available at implementation time, pinned in
.python-versionandrequires-python. - WHEN a contributor runs
uv syncin a fresh clone THEN uv SHALL create.venv/in the repository root and install the project and its development dependencies there. - The repository SHALL commit
uv.lock, and CI SHALL fail WHENuv.lockis out of date withpyproject.toml. - All library code SHALL live under the top-level
tiny_harness/folder, and the package SHALL be importable asfrom tiny_harness import ....
Requirement 2 — Hello world
User story: As a contributor, I want one example function and its test, so that the tooling has something real to check and new code has a pattern to copy.
Acceptance criteria (EARS)
- The package SHALL expose exactly one public function,
hello_world, importable asfrom tiny_harness import hello_world. - WHEN
hello_world()is called THEN it SHALL return the string"Hello, world!". - The repository SHALL contain a unit test for
hello_worldthat runs under pytest. - The repository SHALL contain at least one integration test, kept apart from unit tests, that exercises the installed package; each integration test SHALL carry a Gherkin docstring linked to this requirement (
testing.gherkinDocstrings: required).
Requirement 3 — Lint, type check, test
User story: As a contributor, I want ruff, pyright and pytest configured once, so that local runs and CI agree.
Acceptance criteria (EARS)
- The repository SHALL configure ruff as linter and formatter, pyright as type checker and pytest as test runner in
pyproject.toml, pinned throughuv.lock. - WHEN
uv run ruff check,uv run ruff format --check,uv run pyrightanduv run pytestrun on the delivered tree THEN each SHALL exit 0. - Pyright SHALL run in
strictmode overtiny_harness/and the tests.
Requirement 4 — Conventional Commits
User story: As a maintainer, I want commit messages linted, so that releases can be versioned from them.
Acceptance criteria (EARS)
- The repository SHALL use commitizen with the
cz_conventional_commitsrules. - WHEN a contributor commits with a message that is not a Conventional Commit THEN the
commit-msghook SHALL reject the commit. - WHEN the message is a Conventional Commit (
feat:,fix:, …), a merge or a revert THEN the hook SHALL accept it.
Requirement 5 — Pre-commit hooks
User story: As a contributor, I want the checks to run before each commit, so that broken code does not reach a PR.
Acceptance criteria (EARS)
- The repository SHALL configure the pre-commit framework with hooks for ruff lint, ruff format, pyright, the unit tests and markdownlint (the-loop lints all files, markdown included), plus commitizen on
commit-msg. - WHEN a contributor runs the documented install command THEN the
pre-commitandcommit-msggit hooks SHALL be installed. - WHEN a commit introduces a lint error, a type error or a failing unit test THEN the
pre-commithook SHALL fail and block the commit. - Hooks SHALL invoke tools through
uv runso they use the versions pinned inuv.lock.
Requirement 6 — CI on pull requests
User story: As a reviewer, I want every PR checked by the same commands as the local hooks, plus integration tests, so that green CI means the same thing everywhere.
Acceptance criteria (EARS)
- WHEN a pull request is opened or updated THEN CI SHALL run the same pre-commit hooks over all files (
pre-commit run --all-files). - WHEN a pull request is opened or updated THEN CI SHALL run the integration tests.
- IF any of these steps fails THEN the CI run SHALL fail.
- WHEN a pull request is opened or updated THEN CI SHALL build the documentation site and fail if the build fails.
Requirement 7 — Release on merge to main
User story: As a maintainer, I want each merge to main released to PyPI with a version computed from the commits, so that publishing needs no manual steps or stored tokens.
Acceptance criteria (EARS)
- The release workflow SHALL be
.github/workflows/release.ymland its publish job SHALL use the GitHub environmentpypi. - WHEN a commit lands on
mainTHEN the release workflow SHALL first run every check the PR workflow runs, and SHALL NOT publish if any fails. - WHEN the checks pass and the commits since the last release warrant one THEN the workflow SHALL compute the next version with commitizen (
fix→ patch,feat→ minor, breaking → major), commit the new version tomain, tag itv<version>, build the distribution and publish it to PyPI astiny_harness. - WHEN no commit since the last release warrants one THEN the workflow SHALL publish nothing and SHALL succeed.
- The publish step SHALL authenticate to PyPI with Trusted Publishing (OIDC) only.
- WHEN the workflow's own version-bump commit lands on
mainTHEN it SHALL NOT trigger another release.
Requirement 8 — Documentation site
User story: As a reader, I want the project's docs as a searchable site, so that onboarding, development and architecture notes are in one place.
Acceptance criteria (EARS)
- All project documentation SHALL live under
docs/as Markdown, including the existing the-loop trees (specs, capabilities, architecture, decisions, learnings). - The site SHALL be generated by VitePress with the default theme — top nav, sidebar, local search — in the style of the-loop's docs.
- WHEN a commit that changes
docs/lands onmainTHEN a workflow SHALL build the site and deploy it to GitHub Pages. - The docs SHALL describe the tech stack and local development: environment setup, running each check, and installing the pre-commit hooks.
- WHEN
README.mdis read THEN it SHALL link to the docs site and the local-dev guide.
Non-functional requirements
- Parity: pre-commit, the PR workflow and the release workflow run the same commands through
uv run; a check that passes locally passes in CI. - Pinned tooling: Python tools resolve from
uv.lock; the docs toolchain resolves from a committed lockfile; GitHub Actions use major-version tags of first-party or widely-used actions. - Speed: the PR checks finish in under 5 minutes on a GitHub-hosted runner.
Security considerations
- Actors & trust: maintainers (trusted, merge to
main); outside contributors opening PRs from forks (untrusted — their code runs in CI); third-party GitHub Actions and PyPI/npm packages (untrusted supply chain). - Trust boundaries & data: the release job holds two privileges —
contents: writeon the repository and an OIDC token PyPI trusts fortiny_harness. PR code must never run with either. No long-lived secret (PyPI token, PAT) is stored anywhere. - Abuse cases (EARS):
- WHEN a pull request from a fork runs CI THEN the workflow SHALL run with a read-only
GITHUB_TOKENand noid-token: write, and SHALL NOT usepull_request_target. - WHEN any workflow other than
release.ymlonmain(environmentpypi) requests a PyPI upload THEN PyPI SHALL refuse it, because the trusted publisher names only that workflow and environment. - WHEN a checks step fails on
mainTHEN the release job SHALL NOT bump, tag or publish. - IF a secret or token would appear in committed files or logs THEN it SHALL NOT be committed; the repository SHALL contain no credentials.
- WHEN a pull request from a fork runs CI THEN the workflow SHALL run with a read-only
- Fail closed: permissions default to
contents: readat workflow level and are widened only on the job that needs them (contents: writefor the bump job,id-token: writefor the publish job,pages: write+id-token: writefor the docs deploy). A missingpypienvironment or publisher makes the publish fail, never fall back to a token.
Out of scope
- Any harness functionality beyond
hello_world. - Branch protection rules, the PyPI publisher itself (already registered) and enabling GitHub Pages in the repository settings — repository settings, not files. The docs record what must be set.
- Docs versioning, i18n and a custom theme.
Open questions
None blocking. Two defaults taken, to be confirmed in review of the PR:
- The first published version is
0.1.0. - If branch protection on
mainlater rejects the release workflow's bump push, the maintainer allowsgithub-actions[bot]to bypass it.