Skip to content

Commit 48c9905

Browse files
committed
chore: add optional Beads issue queue guidance
1 parent 8be4b73 commit 48c9905

15 files changed

Lines changed: 168 additions & 13 deletions

.gitattributes

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# The pre-commit hook stack enforces LF line endings. Keep checkout behavior
2+
# aligned across Windows, macOS, and Linux so `pre-commit run --all-files` does
3+
# not rewrite the working tree on Windows clones with global autocrlf enabled.
4+
* text=auto eol=lf
5+
6+
*.png binary
7+
*.jpg binary
8+
*.jpeg binary
9+
*.gif binary
10+
*.ico binary
11+
*.pdf binary

.github/pull_request_template.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
<!-- Required for UI change. Delete this section for non-UI PRs. -->
4141

4242

43+
## Local Beads
44+
45+
<!-- Optional. If this repo/project uses a local Beads queue, include the Bead id or "None". GitHub issue linkage below is still required. -->
46+
47+
4348
## Linked issue
4449

4550
Closes #

.github/scripts/check_aspirational_tickets.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
from pathlib import Path
5858

5959
INVARIANTS_DOC = Path("docs/INVARIANTS.md")
60+
GITHUB_API_ERRORS = (urllib.error.URLError, TimeoutError, json.JSONDecodeError)
6061

6162
# A marker line *starts* with one or two asterisks immediately followed by
6263
# `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:
8889
try:
8990
with urllib.request.urlopen(req, timeout=5) as response: # noqa: S310
9091
payload = json.loads(response.read().decode("utf-8"))
91-
except urllib.error.URLError, TimeoutError, json.JSONDecodeError:
92+
except GITHUB_API_ERRORS:
9293
return None
9394
state = payload.get("state")
9495
return state if isinstance(state, str) else None

.github/scripts/check_pin_freshness.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def _load_pin_module() -> ModuleType:
8484

8585
_pins = _load_pin_module()
8686
_API_BASE = "https://api.github.com"
87+
GITHUB_API_ERRORS = (urllib.error.URLError, TimeoutError, json.JSONDecodeError)
8788

8889

8990
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:
104105
try:
105106
with urllib.request.urlopen(req, timeout=10) as response: # noqa: S310
106107
payload = json.loads(response.read().decode("utf-8"))
107-
except urllib.error.URLError, TimeoutError, json.JSONDecodeError:
108+
except GITHUB_API_ERRORS:
108109
return None
109110
return payload if isinstance(payload, dict) else None
110111

.github/scripts/check_tests_present.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import sys
4444
from pathlib import Path
4545

46+
EVENT_READ_ERRORS = (OSError, json.JSONDecodeError)
47+
4648
# Prefixes that declare a behaviour change → tests required.
4749
BLOCKING_PREFIXES: frozenset[str] = frozenset({"feat", "fix"})
4850

@@ -59,7 +61,7 @@ def pr_title_from_event() -> str | None:
5961
return None
6062
try:
6163
data = json.loads(Path(event_path).read_text(encoding="utf-8"))
62-
except OSError, json.JSONDecodeError:
64+
except EVENT_READ_ERRORS:
6365
return None
6466
pr = data.get("pull_request")
6567
if not isinstance(pr, dict):

.github/scripts/check_version_bump.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
PYPROJECT = Path("pyproject.toml")
4040
UV_LOCK = Path("uv.lock")
4141
PACKAGE_NAME = "harness-python-react"
42+
EVENT_READ_ERRORS = (OSError, json.JSONDecodeError)
4243

4344
# Match the project's self-version block in uv.lock:
4445
#
@@ -105,7 +106,7 @@ def pr_title_from_event() -> str | None:
105106
return None
106107
try:
107108
data = json.loads(Path(event_path).read_text(encoding="utf-8"))
108-
except OSError, json.JSONDecodeError:
109+
except EVENT_READ_ERRORS:
109110
return None
110111
pr = data.get("pull_request")
111112
if not isinstance(pr, dict):

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
.claude/bash-log.txt
44
.claude/worktrees/
55

6+
# Optional local Beads queue state
7+
.beads/
8+
beads/
9+
610
# Node / Frontend
711
node_modules/
812
frontend/dist/

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,15 @@ The subject is **lowercase** after the colon. Title Case prose (`Add the thing`)
3434

3535
1. Open the issue first. Use a feature/bug template; fill every section.
3636
2. Branch off `develop` with the matching name.
37-
3. Land one logical change per PR. Stack PRs if the work is naturally split.
38-
4. The PR template asks five things — answer each (`None` is valid where applicable):
37+
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.
38+
4. Land one logical change per PR. Stack PRs if the work is naturally split.
39+
5. The PR template asks five things — answer each (`None` is valid where applicable):
3940
- **What & why** (1–3 lines)
4041
- **Test plan** (checkbox list; CI covers most of it)
4142
- **Invariants affected** — cite numbered rules from `docs/INVARIANTS.md`
4243
- **New deps / actions / external surface** (anchor for supply-chain review)
4344
- **Screenshots** (UI changes only)
44-
5. Wait for green CI + a code-owner review before merging.
45+
6. Wait for green CI + a code-owner review before merging.
4546

4647
### Solo-owner merge policy
4748

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
- **Backend:** Python 3.14, FastAPI, Pydantic v2 (`StrictModel` base), `uv` deps, OpenTelemetry SDK + OTLP exporter, structured JSON logs, generic tool-registry pattern.
1616
- **Frontend:** Node 24 LTS, React 19.2, Vite 8, TypeScript strict, ESLint 10 flat config, Prettier, Vitest + jsdom + Testing Library.
1717
- **Eval harness:** provider-agnostic runner + LLM-judge `Protocol`, three tolerance modes (exact / numeric / semantic), one example golden case, nightly workflow (disabled by default).
18-
- **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.
18+
- **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.
1919
- **Release:** tag-triggered workflow that builds the image, pushes to `ghcr.io`, generates a CycloneDX SBOM, and publishes the GitHub Release.
2020
- **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).
21+
- **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.
2122
- **Docker:** multi-stage Dockerfile (non-root, healthcheck), `docker compose up` boots app + frontend + Jaeger.
2223

2324
## Quickstart
@@ -114,6 +115,7 @@ See [`docs/HARNESS.md`](docs/HARNESS.md) for the full umbrella. Highlights:
114115
| [`docs/BOUNDARIES.md`](docs/BOUNDARIES.md) | Module layering + the import-linter contracts |
115116
| [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) | Local setup, branching, justfile, CI |
116117
| [`docs/EVAL_HARNESS.md`](docs/EVAL_HARNESS.md) | Eval flywheel + opt-in for the nightly workflow |
118+
| [`docs/BEADS.md`](docs/BEADS.md) | Optional local Beads queue layered under GitHub Issues |
117119
| [`docs/SECURITY.md`](docs/SECURITY.md) | Threat model + defence-in-depth map |
118120
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Scaffold-level component view |
119121
| [`CONTRIBUTING.md`](CONTRIBUTING.md) | Branching, commit format, PR flow |

docs/BEADS.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Optional Beads execution queue
2+
3+
Beads can be layered onto this harness as a local execution queue for teams that
4+
want dependency-aware task planning, agent handoffs, or ready/blocked views while
5+
still keeping GitHub Issues as the external source of truth.
6+
7+
This document is intentionally conservative: it adds Beads guidance without
8+
replacing the repository's existing GitHub issue and PR process.
9+
10+
## Review of existing GitHub issue guidance
11+
12+
The current harness already treats GitHub as the public planning and merge
13+
record:
14+
15+
- `.github/ISSUE_TEMPLATE/bug.md`, `feature.md`, and `eval-regression.md`
16+
define the supported intake paths, and blank issues are disabled in
17+
`.github/ISSUE_TEMPLATE/config.yml`.
18+
- `CONTRIBUTING.md` requires one issue per branch, short-lived branches named
19+
`<type>/<issue-number>-<kebab-title>`, and green CI plus review before merge.
20+
- `.github/pull_request_template.md` requires What & why, Test plan,
21+
Invariants affected, supply-chain surface, Screenshots when relevant, and a
22+
linked issue.
23+
- `CLAUDE.md` and `docs/DEVELOPMENT.md` describe the same one-issue,
24+
one-branch, `develop` to `main` release flow for agent and human operators.
25+
- `docs/TASKS.md` is a project-local planning map cross-referenced with GitHub
26+
issues and the project board.
27+
28+
There is no Beads-specific policy in the base harness today. Any Beads addition
29+
must therefore be additive and must not make GitHub issue state ambiguous.
30+
31+
## GitHub Issues vs Beads
32+
33+
| System | Owns | Does not own |
34+
|---|---|---|
35+
| 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. |
36+
| 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. |
37+
38+
The rule is simple: **GitHub answers what work exists and whether it is
39+
externally done; Beads answers what the local execution system should pick up
40+
next.**
41+
42+
## Sync contract
43+
44+
When using Beads with this harness:
45+
46+
1. Create or confirm the GitHub issue first.
47+
2. Mirror the issue into Beads with an immutable external reference:
48+
- GitHub repository owner/name.
49+
- GitHub issue number.
50+
- GitHub issue URL.
51+
- Original issue title.
52+
3. Use Beads for local status only: `ready`, `in_progress`, `blocked`,
53+
`review`, or `done` are execution states, not replacements for the GitHub
54+
issue state.
55+
4. Put the Bead id in local notes, branch notes, or PR body when useful, but
56+
keep `Closes #<issue>` pointing at the GitHub issue.
57+
5. Do not close a GitHub issue because a Bead is marked done. Close only after
58+
the PR is merged, required checks are green, any required manual or browser
59+
validation is recorded, and a human-readable note has been added to the
60+
issue or PR.
61+
62+
If the GitHub issue changes after import, update the Bead from GitHub before
63+
continuing. GitHub wins on scope, acceptance criteria, and user-visible status.
64+
65+
## Recommended Bead fields
66+
67+
A Bead should carry enough information for a new agent or contributor to resume
68+
without reopening every browser tab:
69+
70+
| Field | Purpose |
71+
|---|---|
72+
| `external_ref` | GitHub issue URL, for example `https://github.com/owner/repo/issues/123`. |
73+
| `github_issue` | Numeric issue id used by branches and PRs. |
74+
| `acceptance` | The current acceptance criteria copied or summarized from GitHub. |
75+
| `dependencies` | Other Beads or GitHub issues that must land first. |
76+
| `status` | Local execution state. |
77+
| `owner` | Optional local agent or human claim. |
78+
| `evidence` | Paths or URLs for test output, review notes, screenshots, or deploy checks. |
79+
| `closeout` | Merge SHA, PR URL, and verification notes once complete. |
80+
81+
Avoid storing secrets, tokens, credentials, private customer data, or raw
82+
production payloads in Beads. Treat Beads data as local operational metadata.
83+
84+
## PR discipline when Beads are used
85+
86+
The existing PR template still applies. Add Beads information without deleting
87+
any required section:
88+
89+
- `Linked issue` remains `Closes #<issue>`.
90+
- Mention the Bead id or local queue reference under `What & why` or the
91+
optional Beads section.
92+
- Include Beads-derived evidence paths in `Test plan` only when they are useful
93+
to a reviewer.
94+
- If the Bead changed scope, update the GitHub issue before asking for review.
95+
- If the Bead was blocked by an external dependency, note that in the PR or
96+
issue rather than hiding it in the local queue.
97+
98+
## Local artifact hygiene
99+
100+
Beads state is usually local execution metadata. Do not commit raw Beads
101+
databases, scratch exports, or agent logs by default. Commit only intentional
102+
summaries or docs that reviewers need.
103+
104+
If a downstream project decides to version Beads state, document that policy in
105+
that project and make sure secret scanning, review, and retention expectations
106+
are explicit.
107+
108+
## Closure checklist
109+
110+
Before marking a Bead done:
111+
112+
- The GitHub issue is still the correct external requirement.
113+
- The PR links the GitHub issue and, where useful, the Bead id.
114+
- Required CI checks passed or have documented non-applicability.
115+
- Required review happened according to repository policy.
116+
- Any requested browser, deploy, eval, or security evidence is attached or
117+
linked.
118+
- The GitHub issue receives a closeout note before or during closure.
119+
120+
Beads improve local throughput only if they reduce ambiguity. If a Bead and a
121+
GitHub issue disagree, stop and reconcile them before implementation continues.

0 commit comments

Comments
 (0)