From 48c99056ef75f8aa33a1fad692b8c7efc2e468d2 Mon Sep 17 00:00:00 2001 From: Jacob Lindsay Date: Sun, 10 May 2026 08:41:41 +1000 Subject: [PATCH 1/3] chore: add optional Beads issue queue guidance --- .gitattributes | 11 ++ .github/pull_request_template.md | 5 + .github/scripts/check_aspirational_tickets.py | 3 +- .github/scripts/check_pin_freshness.py | 3 +- .github/scripts/check_tests_present.py | 4 +- .github/scripts/check_version_bump.py | 3 +- .gitignore | 4 + CONTRIBUTING.md | 7 +- README.md | 4 +- docs/BEADS.md | 121 ++++++++++++++++++ docs/DEVELOPMENT.md | 1 + docs/HARNESS.md | 8 +- docs/HARNESS_PRIMER.md | 3 + pyproject.toml | 2 +- uv.lock | 2 +- 15 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 .gitattributes create mode 100644 docs/BEADS.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7f90681 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# The pre-commit hook stack enforces LF line endings. Keep checkout behavior +# aligned across Windows, macOS, and Linux so `pre-commit run --all-files` does +# not rewrite the working tree on Windows clones with global autocrlf enabled. +* text=auto eol=lf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7693700..878c18c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -40,6 +40,11 @@ +## Local Beads + + + + ## Linked issue Closes # diff --git a/.github/scripts/check_aspirational_tickets.py b/.github/scripts/check_aspirational_tickets.py index ded7013..42f2a05 100644 --- a/.github/scripts/check_aspirational_tickets.py +++ b/.github/scripts/check_aspirational_tickets.py @@ -57,6 +57,7 @@ from pathlib import Path INVARIANTS_DOC = Path("docs/INVARIANTS.md") +GITHUB_API_ERRORS = (urllib.error.URLError, TimeoutError, json.JSONDecodeError) # A marker line *starts* with one or two asterisks immediately followed by # `Aspirational` and a word boundary. Avoids picking up mid-sentence prose @@ -88,7 +89,7 @@ def _issue_state(repo: str, number: str, token: str) -> str | None: try: with urllib.request.urlopen(req, timeout=5) as response: # noqa: S310 payload = json.loads(response.read().decode("utf-8")) - except urllib.error.URLError, TimeoutError, json.JSONDecodeError: + except GITHUB_API_ERRORS: return None state = payload.get("state") return state if isinstance(state, str) else None diff --git a/.github/scripts/check_pin_freshness.py b/.github/scripts/check_pin_freshness.py index f901bdc..0637c52 100644 --- a/.github/scripts/check_pin_freshness.py +++ b/.github/scripts/check_pin_freshness.py @@ -84,6 +84,7 @@ def _load_pin_module() -> ModuleType: _pins = _load_pin_module() _API_BASE = "https://api.github.com" +GITHUB_API_ERRORS = (urllib.error.URLError, TimeoutError, json.JSONDecodeError) def _fetch_json(url: str, token: str) -> dict[str, object] | None: @@ -104,7 +105,7 @@ def _fetch_json(url: str, token: str) -> dict[str, object] | None: try: with urllib.request.urlopen(req, timeout=10) as response: # noqa: S310 payload = json.loads(response.read().decode("utf-8")) - except urllib.error.URLError, TimeoutError, json.JSONDecodeError: + except GITHUB_API_ERRORS: return None return payload if isinstance(payload, dict) else None diff --git a/.github/scripts/check_tests_present.py b/.github/scripts/check_tests_present.py index 3c689b1..92069ea 100644 --- a/.github/scripts/check_tests_present.py +++ b/.github/scripts/check_tests_present.py @@ -43,6 +43,8 @@ import sys from pathlib import Path +EVENT_READ_ERRORS = (OSError, json.JSONDecodeError) + # Prefixes that declare a behaviour change → tests required. BLOCKING_PREFIXES: frozenset[str] = frozenset({"feat", "fix"}) @@ -59,7 +61,7 @@ def pr_title_from_event() -> str | None: return None try: data = json.loads(Path(event_path).read_text(encoding="utf-8")) - except OSError, json.JSONDecodeError: + except EVENT_READ_ERRORS: return None pr = data.get("pull_request") if not isinstance(pr, dict): diff --git a/.github/scripts/check_version_bump.py b/.github/scripts/check_version_bump.py index 4510c0f..cdf7959 100644 --- a/.github/scripts/check_version_bump.py +++ b/.github/scripts/check_version_bump.py @@ -39,6 +39,7 @@ PYPROJECT = Path("pyproject.toml") UV_LOCK = Path("uv.lock") PACKAGE_NAME = "harness-python-react" +EVENT_READ_ERRORS = (OSError, json.JSONDecodeError) # Match the project's self-version block in uv.lock: # @@ -105,7 +106,7 @@ def pr_title_from_event() -> str | None: return None try: data = json.loads(Path(event_path).read_text(encoding="utf-8")) - except OSError, json.JSONDecodeError: + except EVENT_READ_ERRORS: return None pr = data.get("pull_request") if not isinstance(pr, dict): diff --git a/.gitignore b/.gitignore index 1c8bc4a..a3f61fd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ .claude/bash-log.txt .claude/worktrees/ +# Optional local Beads queue state +.beads/ +beads/ + # Node / Frontend node_modules/ frontend/dist/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba47e27..02c5cfa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,14 +34,15 @@ The subject is **lowercase** after the colon. Title Case prose (`Add the thing`) 1. Open the issue first. Use a feature/bug template; fill every section. 2. Branch off `develop` with the matching name. -3. Land one logical change per PR. Stack PRs if the work is naturally split. -4. The PR template asks five things — answer each (`None` is valid where applicable): +3. If your team uses Beads, mirror or claim the linked issue in the local Beads queue after the issue exists. Beads track local ready/blocked execution only; GitHub Issues remain canonical for scope, discussion, PR linkage, and closure. +4. Land one logical change per PR. Stack PRs if the work is naturally split. +5. The PR template asks five things — answer each (`None` is valid where applicable): - **What & why** (1–3 lines) - **Test plan** (checkbox list; CI covers most of it) - **Invariants affected** — cite numbered rules from `docs/INVARIANTS.md` - **New deps / actions / external surface** (anchor for supply-chain review) - **Screenshots** (UI changes only) -5. Wait for green CI + a code-owner review before merging. +6. Wait for green CI + a code-owner review before merging. ### Solo-owner merge policy diff --git a/README.md b/README.md index 2c4d4da..b972f7a 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ - **Backend:** Python 3.14, FastAPI, Pydantic v2 (`StrictModel` base), `uv` deps, OpenTelemetry SDK + OTLP exporter, structured JSON logs, generic tool-registry pattern. - **Frontend:** Node 24 LTS, React 19.2, Vite 8, TypeScript strict, ESLint 10 flat config, Prettier, Vitest + jsdom + Testing Library. - **Eval harness:** provider-agnostic runner + LLM-judge `Protocol`, three tolerance modes (exact / numeric / semantic), one example golden case, nightly workflow (disabled by default). -- **CI:** 15 required status checks across `ci.yml` (lint/format, mypy strict, unit tests, coverage ≥75%, import-linter architecture, pre-commit, frontend build, frontend quality, branch-protection sync, commit-type sync) + `security.yml` (gitleaks, pip-audit, npm audit, trivy) + PR-title lint. +- **CI:** 21 required status checks across `ci.yml` (lint/format, mypy strict, unit tests, coverage, import-linter architecture, pre-commit, frontend build, frontend quality, branch-protection sync, commit-type sync, version/action/tests/docs audits) + `security.yml` (gitleaks, pip-audit, npm audit, trivy) + PR-title lint. - **Release:** tag-triggered workflow that builds the image, pushes to `ghcr.io`, generates a CycloneDX SBOM, and publishes the GitHub Release. - **Agent integration:** `.claude/hooks/` (forbidden-flag blocker, secret scan, formatter dispatch, SessionStart context) + six auto-activating skills (architect / code-reviewer / devops / frontend / qa-engineer / technical-writer). +- **Issue execution:** GitHub Issues remain the external source of truth; optional Beads guidance adds a local dependency-aware execution queue without changing issue closure authority. - **Docker:** multi-stage Dockerfile (non-root, healthcheck), `docker compose up` boots app + frontend + Jaeger. ## Quickstart @@ -114,6 +115,7 @@ See [`docs/HARNESS.md`](docs/HARNESS.md) for the full umbrella. Highlights: | [`docs/BOUNDARIES.md`](docs/BOUNDARIES.md) | Module layering + the import-linter contracts | | [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) | Local setup, branching, justfile, CI | | [`docs/EVAL_HARNESS.md`](docs/EVAL_HARNESS.md) | Eval flywheel + opt-in for the nightly workflow | +| [`docs/BEADS.md`](docs/BEADS.md) | Optional local Beads queue layered under GitHub Issues | | [`docs/SECURITY.md`](docs/SECURITY.md) | Threat model + defence-in-depth map | | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Scaffold-level component view | | [`CONTRIBUTING.md`](CONTRIBUTING.md) | Branching, commit format, PR flow | diff --git a/docs/BEADS.md b/docs/BEADS.md new file mode 100644 index 0000000..0ec8db8 --- /dev/null +++ b/docs/BEADS.md @@ -0,0 +1,121 @@ +# Optional Beads execution queue + +Beads can be layered onto this harness as a local execution queue for teams that +want dependency-aware task planning, agent handoffs, or ready/blocked views while +still keeping GitHub Issues as the external source of truth. + +This document is intentionally conservative: it adds Beads guidance without +replacing the repository's existing GitHub issue and PR process. + +## Review of existing GitHub issue guidance + +The current harness already treats GitHub as the public planning and merge +record: + +- `.github/ISSUE_TEMPLATE/bug.md`, `feature.md`, and `eval-regression.md` + define the supported intake paths, and blank issues are disabled in + `.github/ISSUE_TEMPLATE/config.yml`. +- `CONTRIBUTING.md` requires one issue per branch, short-lived branches named + `/-`, and green CI plus review before merge. +- `.github/pull_request_template.md` requires What & why, Test plan, + Invariants affected, supply-chain surface, Screenshots when relevant, and a + linked issue. +- `CLAUDE.md` and `docs/DEVELOPMENT.md` describe the same one-issue, + one-branch, `develop` to `main` release flow for agent and human operators. +- `docs/TASKS.md` is a project-local planning map cross-referenced with GitHub + issues and the project board. + +There is no Beads-specific policy in the base harness today. Any Beads addition +must therefore be additive and must not make GitHub issue state ambiguous. + +## GitHub Issues vs Beads + +| System | Owns | Does not own | +|---|---|---| +| GitHub Issues | Public backlog, user-facing requirements, labels, project board state, discussion, acceptance criteria, links from PRs, and final issue closure. | Local agent claims, transient execution notes, or dependency scheduling that would be noisy in the public issue. | +| Beads | Local execution queue, ready/blocked views, dependency graph, implementation notes, reviewer handoff notes, and restart-safe task claims. | The canonical requirement, public status, release notes, or authority to close a GitHub issue. | + +The rule is simple: **GitHub answers what work exists and whether it is +externally done; Beads answers what the local execution system should pick up +next.** + +## Sync contract + +When using Beads with this harness: + +1. Create or confirm the GitHub issue first. +2. Mirror the issue into Beads with an immutable external reference: + - GitHub repository owner/name. + - GitHub issue number. + - GitHub issue URL. + - Original issue title. +3. Use Beads for local status only: `ready`, `in_progress`, `blocked`, + `review`, or `done` are execution states, not replacements for the GitHub + issue state. +4. Put the Bead id in local notes, branch notes, or PR body when useful, but + keep `Closes #` pointing at the GitHub issue. +5. Do not close a GitHub issue because a Bead is marked done. Close only after + the PR is merged, required checks are green, any required manual or browser + validation is recorded, and a human-readable note has been added to the + issue or PR. + +If the GitHub issue changes after import, update the Bead from GitHub before +continuing. GitHub wins on scope, acceptance criteria, and user-visible status. + +## Recommended Bead fields + +A Bead should carry enough information for a new agent or contributor to resume +without reopening every browser tab: + +| Field | Purpose | +|---|---| +| `external_ref` | GitHub issue URL, for example `https://github.com/owner/repo/issues/123`. | +| `github_issue` | Numeric issue id used by branches and PRs. | +| `acceptance` | The current acceptance criteria copied or summarized from GitHub. | +| `dependencies` | Other Beads or GitHub issues that must land first. | +| `status` | Local execution state. | +| `owner` | Optional local agent or human claim. | +| `evidence` | Paths or URLs for test output, review notes, screenshots, or deploy checks. | +| `closeout` | Merge SHA, PR URL, and verification notes once complete. | + +Avoid storing secrets, tokens, credentials, private customer data, or raw +production payloads in Beads. Treat Beads data as local operational metadata. + +## PR discipline when Beads are used + +The existing PR template still applies. Add Beads information without deleting +any required section: + +- `Linked issue` remains `Closes #`. +- Mention the Bead id or local queue reference under `What & why` or the + optional Beads section. +- Include Beads-derived evidence paths in `Test plan` only when they are useful + to a reviewer. +- If the Bead changed scope, update the GitHub issue before asking for review. +- If the Bead was blocked by an external dependency, note that in the PR or + issue rather than hiding it in the local queue. + +## Local artifact hygiene + +Beads state is usually local execution metadata. Do not commit raw Beads +databases, scratch exports, or agent logs by default. Commit only intentional +summaries or docs that reviewers need. + +If a downstream project decides to version Beads state, document that policy in +that project and make sure secret scanning, review, and retention expectations +are explicit. + +## Closure checklist + +Before marking a Bead done: + +- The GitHub issue is still the correct external requirement. +- The PR links the GitHub issue and, where useful, the Bead id. +- Required CI checks passed or have documented non-applicability. +- Required review happened according to repository policy. +- Any requested browser, deploy, eval, or security evidence is attached or + linked. +- The GitHub issue receives a closeout note before or during closure. + +Beads improve local throughput only if they reduce ambiguity. If a Bead and a +GitHub issue disagree, stop and reconcile them before implementation continues. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index db7bb90..a5827cd 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -73,6 +73,7 @@ Every recipe uses `uv run --frozen` — bare `uv run` silently re-resolves when - `main` is protected: every required CI context must pass + 1 review + commit-type sync + branch-protection sync. - `develop` is the integration branch; same gates as `main` minus a strictness flag (`strict: false` so PRs don't need rebases). - Feature branches are short-lived and named `/-`. +- Optional Beads queues can mirror GitHub issues for local execution, but GitHub remains the source of truth for requirements, PR linkage, and closure. See `docs/BEADS.md`. ## Commit messages diff --git a/docs/HARNESS.md b/docs/HARNESS.md index e24007c..6a7a7e8 100644 --- a/docs/HARNESS.md +++ b/docs/HARNESS.md @@ -13,7 +13,7 @@ The "harness" is the set of mechanical controls that make LLM-driven coding prod | **Tests** | Behaviour | `pytest tests/`, `pytest eval/`, `vitest` | | **Coverage** | ≥ 75% on `src/` | `pyproject.toml` `[tool.coverage.report]` | | **Pre-commit** | Local-first defence | `.pre-commit-config.yaml` (ruff, gitleaks, commitizen, mypy, hygiene) | -| **CI** | Non-bypassable | `.github/workflows/ci.yml` (15 contexts) + `security.yml` + `pr-title.yml` + `release.yml` + `release-drafter.yml` | +| **CI** | Non-bypassable | `.github/workflows/ci.yml` + `security.yml` + `pr-title.yml` (21 required contexts) plus release and maintenance workflows | | **Branch protection** | Declarative, drift-checked | `.github/branch-protection/{develop,main}.json` + `branch-protection.yml` apply workflow + `check_required_contexts.py` meta-gate | | **Commit format** | Seven prefixes only | `[tool.commitizen]` schema + `pr-title.yml` allowlist + `check_commit_types.py` meta-gate | | **Secret scan** | Three checkpoints | local hook → pre-commit → `security.yml` gitleaks | @@ -21,6 +21,7 @@ The "harness" is the set of mechanical controls that make LLM-driven coding prod | **Dep scan** | Pinned + audited | pip-audit, npm audit | | **Release** | Reproducible artefacts | `release.yml` (image push to GHCR + CycloneDX SBOM) | | **Eval** | LLM-output regressions | `src/eval/`, `eval/`, `eval-nightly.yml` (workflow_dispatch by default) | +| **Issue execution** | GitHub stays canonical; Beads can drive local ready/blocked work | GitHub issue templates + PR template + optional `docs/BEADS.md` queue guidance | | **Agent hooks** | LLM coder side enforcement | `.claude/hooks/{pretooluse_bash, posttooluse_writeedit, sessionstart}.py` + `settings.local.json.example` | | **Skills** | Auto-activated agent guidance | `.claude/skills/{architect, code-reviewer, devops, frontend, qa-engineer, technical-writer}` | @@ -40,5 +41,6 @@ For an engineer setting up the template: 2. **`docs/BOUNDARIES.md`** — module layering and the import-linter contracts. 3. **`docs/DEVELOPMENT.md`** — local setup, the `justfile`, the CI pipeline. 4. **`docs/EVAL_HARNESS.md`** — the eval flywheel; how to add a case, how to opt the nightly into running. -5. **`docs/SECURITY.md`** — threat model + the defence-in-depth map. -6. **`docs/ARCHITECTURE.md`** — scaffold-level diagram; expand as your domain lands. +5. **`docs/BEADS.md`** — optional local execution queue layered under GitHub Issues. +6. **`docs/SECURITY.md`** — threat model + the defence-in-depth map. +7. **`docs/ARCHITECTURE.md`** — scaffold-level diagram; expand as your domain lands. diff --git a/docs/HARNESS_PRIMER.md b/docs/HARNESS_PRIMER.md index 7d9606d..737ac4c 100644 --- a/docs/HARNESS_PRIMER.md +++ b/docs/HARNESS_PRIMER.md @@ -269,6 +269,7 @@ Distinct from the **build harness** (everything above), the **evaluation harness |---|---| | PR template | [.github/pull_request_template.md](../.github/pull_request_template.md). | | Issue templates | [.github/ISSUE_TEMPLATE/](../.github/ISSUE_TEMPLATE/): `bug.md`, `feature.md`, `eval-regression.md`. Blank issues disabled. | +| Optional Beads queue | [docs/BEADS.md](BEADS.md): GitHub Issues remain canonical while Beads can track local ready/blocked execution. | | Code ownership | [.github/CODEOWNERS](../.github/CODEOWNERS). | | Branch protection | [.github/branch-protection/{main,develop}.json](../.github/branch-protection/) declarative configs, re-applied weekly by [branch-protection.yml](../.github/workflows/branch-protection.yml). | | Commit message shape | Commitizen, configured in `pyproject.toml`. | @@ -359,6 +360,7 @@ The error names the offending module, line, and contract — no guessing. | **OpenTelemetry (OTel)** | Vendor-neutral standard for traces, metrics, logs. The repo follows `gen_ai.*` and `db.*` semantic conventions for attribute names. | | **CycloneDX** | An SBOM format. Generated per release and attached to the GitHub Release. | | **gitleaks** | Pattern-based secret scanner. | +| **Beads** | Optional local issue queue used for dependency-aware execution and handoffs; GitHub Issues remain canonical. | --- @@ -372,4 +374,5 @@ The error names the offending module, line, and contract — no guessing. | [ARCHITECTURE.md](ARCHITECTURE.md) | The system design — components, request flow. | | [SECURITY.md](SECURITY.md) | Threat model + defence-in-depth mapping. | | [EVAL_HARNESS.md](EVAL_HARNESS.md) | The eval flywheel. | +| [BEADS.md](BEADS.md) | Optional local Beads queue layered under GitHub Issues. | | [DEVELOPMENT.md](DEVELOPMENT.md) | Local setup, branching, releases. | diff --git a/pyproject.toml b/pyproject.toml index c1f5158..a01deaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harness-python-react" -version = "0.2.9" +version = "0.2.10" description = "Production-quality LLM-driven coding harness — Python (FastAPI) backend, Vite + React + TypeScript frontend." readme = "README.md" requires-python = ">=3.14" diff --git a/uv.lock b/uv.lock index 8fe0b4f..cdc1eae 100644 --- a/uv.lock +++ b/uv.lock @@ -328,7 +328,7 @@ wheels = [ [[package]] name = "harness-python-react" -version = "0.2.9" +version = "0.2.10" source = { virtual = "." } dependencies = [ { name = "fastapi" }, From 4df67bdbd13eb494bc33c23ca05704212ca3f76a Mon Sep 17 00:00:00 2001 From: "const.koutsakis@aurecongroup.com" Date: Mon, 25 May 2026 23:41:09 +1000 Subject: [PATCH 2/3] chore: address PR-86 review feedback (BEADS doc + template + CI-script compile gate) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies the actionable items from the PR-86 review: - docs/BEADS.md: lead with a one-sentence "what Beads is" + upstream link; state the stance explicitly (optional/additive, recommended for agent-driven flows, GitHub remains authoritative); add a YAML example block under Recommended Bead fields; replace the duplicated Closure checklist with a Bead-specific narrowing that cites the PR template + CONTRIBUTING; call out that .beads/ is wiped by git clean -fdx. - .github/pull_request_template.md: collapse the "Local Beads" section into an HTML-commented opt-in block so it is invisible in the rendered preview until a Beads-using team uncomments it. - CONTRIBUTING.md: document the one-shot git renormalisation step for Windows clones after the .gitattributes change lands. - tests/test_scripts_compile.py: regression gate that py_compiles every .github/scripts/*.py. The "scripts unparseable" review finding was based on an older local Python — PEP 758 (3.14) makes the unparenthesised except clauses valid, so the scripts ARE fine on the project pin. The test guards against an actual syntax error landing in future. --- .github/pull_request_template.md | 6 +++- CONTRIBUTING.md | 15 +++++++++ docs/BEADS.md | 58 +++++++++++++++++++++++--------- tests/test_scripts_compile.py | 28 +++++++++++++++ 4 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 tests/test_scripts_compile.py diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 878c18c..a180c91 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -40,9 +40,13 @@ + +Optional opt-in. Only add this section if your team uses a local Beads queue +(see docs/BEADS.md). Uncomment the heading and replace this comment with the +Bead id. GitHub issue linkage below is still required either way. +--> ## Linked issue diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 02c5cfa..3565676 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,21 @@ gh pr merge --admin --squash --delete-branch When a second collaborator joins, drop the `--admin` flag and adopt standard PR review. Update this section + `CODEOWNERS` in the same PR. +## Line endings (Windows clones) + +This repo enforces LF line endings via `.gitattributes` (`* text=auto eol=lf`) +and the pre-commit hygiene hook. If you cloned on Windows with +`core.autocrlf=true`, the first checkout after pulling the `.gitattributes` +change can leave the working tree out of sync with the index. Renormalise +once: + +```sh +git add --renormalize . +git commit -m "chore: renormalise line endings" +``` + +After that, day-to-day work is unaffected. + ## Local pre-push gate ```sh diff --git a/docs/BEADS.md b/docs/BEADS.md index 0ec8db8..d3271cc 100644 --- a/docs/BEADS.md +++ b/docs/BEADS.md @@ -1,11 +1,20 @@ # Optional Beads execution queue -Beads can be layered onto this harness as a local execution queue for teams that -want dependency-aware task planning, agent handoffs, or ready/blocked views while -still keeping GitHub Issues as the external source of truth. - -This document is intentionally conservative: it adds Beads guidance without -replacing the repository's existing GitHub issue and PR process. +[Beads](https://github.com/steveyegge/beads) is an open-source +dependency-aware issue tracker designed for AI coding agents — it gives an +agent a local ready/blocked view of work, a dependency graph, and restart-safe +task claims that GitHub Issues alone do not. + +This document is **optional and additive**. The base harness does not assume +Beads; if your team has no agent or multi-actor execution concern, GitHub +Issues plus the PR template is sufficient and you can skip this doc entirely. +Beads is recommended specifically when you are coordinating an LLM agent (or +several) against this repo and want dependency planning the public issue +tracker does not provide. The README and `docs/HARNESS.md` references describe +Beads as optional infrastructure, not part of the standard contributor flow. + +Wherever Beads is used, GitHub Issues remain the external source of truth and +the authority for issue closure. ## Review of existing GitHub issue guidance @@ -78,8 +87,26 @@ without reopening every browser tab: | `evidence` | Paths or URLs for test output, review notes, screenshots, or deploy checks. | | `closeout` | Merge SHA, PR URL, and verification notes once complete. | +A short YAML example: + +```yaml +external_ref: https://github.com/owner/repo/issues/123 +github_issue: 123 +acceptance: | + /api/v1/echo rejects payloads >1KiB with HTTP 413. +dependencies: [122] # other Bead ids or GitHub issues +status: ready +owner: agent-a +evidence: + - tests/test_api.py::test_echo_size_cap +closeout: null +``` + Avoid storing secrets, tokens, credentials, private customer data, or raw production payloads in Beads. Treat Beads data as local operational metadata. +Note that `.beads/` is gitignored, so anything Beads stores locally — including +agent-action audit logs — is wiped by `git clean -fdx`; commit deliberate +summaries to the repo if you need them to survive workspace resets. ## PR discipline when Beads are used @@ -107,15 +134,16 @@ are explicit. ## Closure checklist -Before marking a Bead done: +The PR-merge and issue-closure gates already live in +`.github/pull_request_template.md` and `CONTRIBUTING.md` — don't duplicate them +here. The Bead-specific closure rule is narrower: -- The GitHub issue is still the correct external requirement. -- The PR links the GitHub issue and, where useful, the Bead id. -- Required CI checks passed or have documented non-applicability. -- Required review happened according to repository policy. -- Any requested browser, deploy, eval, or security evidence is attached or - linked. -- The GitHub issue receives a closeout note before or during closure. +- Do not mark a Bead done until the GitHub issue's closure conditions (per the + PR template and `CONTRIBUTING.md`) are met. Beads track the local execution + state of work GitHub already authorised; they don't grant new closure + authority. +- If the Bead and the GitHub issue disagree on scope, acceptance, or status, + stop and reconcile against GitHub before continuing. Beads improve local throughput only if they reduce ambiguity. If a Bead and a -GitHub issue disagree, stop and reconcile them before implementation continues. +GitHub issue disagree, the GitHub issue wins. diff --git a/tests/test_scripts_compile.py b/tests/test_scripts_compile.py new file mode 100644 index 0000000..214abbb --- /dev/null +++ b/tests/test_scripts_compile.py @@ -0,0 +1,28 @@ +"""Every script in `.github/scripts/` must parse on the project's pinned Python. + +Catches a class of regression where a script lands with a syntax error that +the corresponding CI gate happens to not exercise. Cheap and broad: one test, +one `py_compile.compile` per script. +""" + +from __future__ import annotations + +import py_compile +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parent.parent +SCRIPTS_DIR = REPO_ROOT / ".github" / "scripts" + + +def _ci_scripts() -> list[Path]: + return sorted(p for p in SCRIPTS_DIR.glob("*.py") if p.is_file()) + + +@pytest.mark.parametrize("script", _ci_scripts(), ids=lambda p: p.name) +def test_ci_script_compiles(script: Path) -> None: + try: + py_compile.compile(str(script), doraise=True) + except py_compile.PyCompileError as exc: # pragma: no cover — failure path + pytest.fail(f"{script.name} failed to compile: {exc.msg}") From bfd19c74adf7498cb2675e73a4d12264b82463c8 Mon Sep 17 00:00:00 2001 From: "const.koutsakis@aurecongroup.com" Date: Tue, 26 May 2026 00:08:55 +1000 Subject: [PATCH 3/3] chore: bump version to 0.2.11 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a01deaa..0651387 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harness-python-react" -version = "0.2.10" +version = "0.2.11" description = "Production-quality LLM-driven coding harness — Python (FastAPI) backend, Vite + React + TypeScript frontend." readme = "README.md" requires-python = ">=3.14" diff --git a/uv.lock b/uv.lock index cdc1eae..26d00b8 100644 --- a/uv.lock +++ b/uv.lock @@ -328,7 +328,7 @@ wheels = [ [[package]] name = "harness-python-react" -version = "0.2.10" +version = "0.2.11" source = { virtual = "." } dependencies = [ { name = "fastapi" },