From 87c7a58f2734bdddbfdc4ba8a724341ee353ebae Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 05:33:39 +0000 Subject: [PATCH 1/6] docs: add design workflow guide --- docs/README.md | 1 + docs/design-workflow.md | 220 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 docs/design-workflow.md diff --git a/docs/README.md b/docs/README.md index 3e4cd0eb..b26f8ecf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ picking the right document for what you're capturing. | Artifact | For | |---|---| +| **Design workflow** (`docs/design-workflow.md`) | How discussion becomes canonical design, how dependencies are recorded, and when implementation may begin. | | **Glossary** (`docs/glossary.md`) | Canonical term definitions — what words mean in this project. | | **PRD** (`docs/prds/`) | A feature: what to build, scope, success criteria. | | **Research note** (`docs/research/`) | A landscape/tradeoff investigation. | diff --git a/docs/design-workflow.md b/docs/design-workflow.md new file mode 100644 index 00000000..7de49d8b --- /dev/null +++ b/docs/design-workflow.md @@ -0,0 +1,220 @@ +# Design workflow + +How bot-bottle turns discussion into canonical design and then into +implementation without leaving the repository's architecture scattered across +issue and review threads. + +The goal is not more documentation. The goal is one discoverable current answer +for every load-bearing design question. + +## Sources of truth + +Design artifacts have different jobs: + +| Artifact | Authority | +|---|---| +| Architecture and decision records | Stable system-wide boundaries, policies, and invariants | +| PRDs | The current design for a feature | +| Research notes | Evidence and tradeoff analysis; informative, not normative | +| Issues | Work tracking, open questions, and discussion | +| Pull-request comments | Review history; never the final home of a design decision | + +When a discussion changes the design, update the relevant PRD, decision record, +or architecture document before treating the discussion as resolved. A comment +may explain why a decision changed, but future implementers must not need to +reconstruct the decision from a thread. + +Avoid duplicating the same rule in several canonical documents. Prefer one +canonical statement and links from dependent documents. + +## Choosing the canonical artifact + +Use a PRD when the decision describes a feature: its behavior, scope, success +criteria, trust model, implementation slices, and tests. + +Use a decision record when the choice is broader than one feature or will +constrain several future features. Examples include state ownership, credential +boundaries, compatibility policy, and what the project does or does not claim +as a security guarantee. + +Use an architecture document or concise architecture section when readers need +a current cross-feature map: component ownership, control-plane and data-plane +boundaries, durable state, credential flow, and network choke points. + +Use a research note when the conclusion depends on comparing external systems, +protocols, or approaches. Promote any resulting project decision into a PRD or +decision record. + +## From discussion to implementation + +### 1. Open the design discussion + +An issue may start with incomplete requirements. Record: + +- the problem and desired outcome; +- known security or compatibility constraints; +- the current owner of affected state and credentials; +- related PRDs, decisions, issues, and pull requests; +- open questions that would materially change the implementation. + +Do not disguise an unresolved trust-boundary or state-ownership decision as an +implementation detail. + +### 2. Draft or update the canonical design + +Before substantial implementation, write the feature PRD and update any +system-wide decision it changes. + +An active design should make these relationships visible near its top: + +```markdown +Status: Draft | Active | Superseded | Retargeted +Depends on: #... +Blocks: #... +Supersedes: ... +Last validated against main: +``` + +`Last validated against main` is a staleness signal, not a promise that no code +has changed. Update it after a meaningful design review against the current +architecture. + +For security-sensitive work, state: + +- the exact guarantee and explicit non-guarantees; +- trusted and untrusted components; +- who creates each identity or attribution field; +- who owns durable state; +- failure and recovery behavior; +- how the design is tested at its boundaries. + +### 3. Resolve review into the repository + +When review settles a design-changing question: + +1. Update the canonical document in the same pull request. +2. Mark conflicting documents Superseded or Retargeted, or update them. +3. Add or adjust dependency links. +4. Leave a concise resolution comment linking to the canonical change. + +A useful resolution comment is: + +```text +Resolution: +Canonicalized in: +Supersedes: +Follow-up: +``` + +The resolution is incomplete until the repository reflects it. + +### 4. Check design readiness + +Implementation may begin when: + +- the PRD's material trust, ownership, and compatibility questions are settled; +- dependencies and blockers are explicit; +- the design agrees with current architecture and decision records; +- superseded documents are marked or updated; +- success criteria and boundary tests are concrete; +- remaining open questions can be answered during implementation without + changing the feature's guarantee or component ownership. + +Small exploratory spikes may happen earlier. A spike proves feasibility; it does +not establish a production contract or silently settle the design. + +### 5. Implement in ordered slices + +Prefer small, independently reviewable slices after the parent design is +accepted. Record the dependency chain explicitly. + +Parallel work is safe when slices do not compete for the same unsettled +interface or ownership boundary. If a foundational change will alter the +transport, schema, state owner, or trust domain used by another slice, land the +foundation first. + +An implementation pull request should identify: + +- the PRD or decision it implements; +- the implementation chunk; +- its base and blockers; +- any design deviation discovered during implementation. + +If implementation reveals a load-bearing design change, pause that slice and +update the canonical design. Do not let the code and review thread become an +undocumented replacement for the PRD. + +## Dependency and staleness management + +### Dependency direction + +Write dependencies in terms of contracts, not chronology: + +```text +credential provisioning contract + -> host-controller authentication + -> privileged host operations +``` + +If only part of a feature is blocked, say so. For example, a manifest parser may +proceed while that feature's durable audit-storage chunk waits for the canonical +audit schema. + +### Superseding documents + +Do not silently edit history to make an old design appear to have always said +the new thing. Preserve the rationale, but make current status unmistakable: + +```markdown +Status: Superseded +Superseded by: +Reason: +``` + +If part of a PRD remains valid, mark it Retargeted and identify which scope moved +elsewhere. + +### Architecture sweeps + +After a foundational change, do a targeted architecture sweep before building +more features on it: + +1. Identify the concepts the change affects, such as `bot-bottle.db`, host + controller, orchestrator, audit ownership, or signing key. +2. Search active PRDs, decisions, and open issues for those concepts. +3. Update or supersede contradictory statements. +4. Refresh dependency links and the current architecture summary. +5. Confirm stacked implementation branches still have the correct base. + +This is a milestone activity, not a recurring documentation ceremony. + +## Pull-request checklist + +Use the relevant items in design and implementation pull requests: + +- [ ] The canonical PRD or decision is linked. +- [ ] Design-changing review decisions are reflected in-repo. +- [ ] Dependencies and blockers are explicit. +- [ ] State, credential, and trust ownership agree with current architecture. +- [ ] Superseded or retargeted documents are marked. +- [ ] Security guarantees and non-guarantees are precise. +- [ ] Open questions do not change the promised guarantee or ownership model. +- [ ] Implementation deviations updated the canonical design. +- [ ] The design was validated against a current `main`. + +## Lightweight maintenance + +Automation should enforce document shape, not pretend to understand +architecture. Useful checks include: + +- active PRDs contain status and dependency metadata; +- superseded PRDs link to their replacement; +- referenced documents and issues exist; +- implementation pull requests identify their PRD and chunk; +- document filenames and lifecycle states follow repository conventions. + +Human review remains responsible for detecting conflicting guarantees or +ownership claims. + +The durable rule is simple: **discussion discovers the decision; the repository +records it; implementation follows it.** -- 2.52.0 From 6d29a34514daefa0cb58f78555a163d14845af3a Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 06:37:43 +0000 Subject: [PATCH 2/6] docs: streamline design workflow guidance --- docs/design-workflow.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/design-workflow.md b/docs/design-workflow.md index 7de49d8b..469fd8e4 100644 --- a/docs/design-workflow.md +++ b/docs/design-workflow.md @@ -13,16 +13,16 @@ Design artifacts have different jobs: | Artifact | Authority | |---|---| -| Architecture and decision records | Stable system-wide boundaries, policies, and invariants | +| Decision records | Stable system-wide boundaries, policies, and invariants | | PRDs | The current design for a feature | | Research notes | Evidence and tradeoff analysis; informative, not normative | | Issues | Work tracking, open questions, and discussion | | Pull-request comments | Review history; never the final home of a design decision | -When a discussion changes the design, update the relevant PRD, decision record, -or architecture document before treating the discussion as resolved. A comment -may explain why a decision changed, but future implementers must not need to -reconstruct the decision from a thread. +When a discussion changes the design, update the relevant PRD or decision +record before treating the discussion as resolved. A comment may explain why a +decision changed, but future implementers must not need to reconstruct the +decision from a thread. Avoid duplicating the same rule in several canonical documents. Prefer one canonical statement and links from dependent documents. @@ -37,10 +37,6 @@ constrain several future features. Examples include state ownership, credential boundaries, compatibility policy, and what the project does or does not claim as a security guarantee. -Use an architecture document or concise architecture section when readers need -a current cross-feature map: component ownership, control-plane and data-plane -boundaries, durable state, credential flow, and network choke points. - Use a research note when the conclusion depends on comparing external systems, protocols, or approaches. Promote any resulting project decision into a PRD or decision record. @@ -70,14 +66,11 @@ An active design should make these relationships visible near its top: ```markdown Status: Draft | Active | Superseded | Retargeted Depends on: #... -Blocks: #... Supersedes: ... -Last validated against main: ``` -`Last validated against main` is a staleness signal, not a promise that no code -has changed. Update it after a meaningful design review against the current -architecture. +Record dependencies only on the dependent document. Do not maintain reverse +`Blocks` lists that can drift as dependent work changes. For security-sensitive work, state: @@ -174,6 +167,12 @@ Reason: If part of a PRD remains valid, mark it Retargeted and identify which scope moved elsewhere. +Add a short supersession note near the top explaining what changed, why the old +design is no longer current, and where the current design lives. For a research +note whose original analysis remains useful, preserve that analysis and append +a dated addendum with the newer finding instead of rewriting the note as though +it had always reached the new conclusion. + ### Architecture sweeps After a foundational change, do a targeted architecture sweep before building @@ -200,7 +199,6 @@ Use the relevant items in design and implementation pull requests: - [ ] Security guarantees and non-guarantees are precise. - [ ] Open questions do not change the promised guarantee or ownership model. - [ ] Implementation deviations updated the canonical design. -- [ ] The design was validated against a current `main`. ## Lightweight maintenance -- 2.52.0 From b0cbec4375dd0596391e090e5fd64c93e8bf0d87 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 06:41:07 +0000 Subject: [PATCH 3/6] docs(prd): explain superseded dashboard designs --- docs/prds/0019-active-agents-in-dashboard.md | 5 +++++ docs/prds/0020-start-and-attach-from-dashboard.md | 5 +++++ docs/prds/0021-dashboard-tmux-split-pane.md | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/docs/prds/0019-active-agents-in-dashboard.md b/docs/prds/0019-active-agents-in-dashboard.md index 1052c032..44f558ad 100644 --- a/docs/prds/0019-active-agents-in-dashboard.md +++ b/docs/prds/0019-active-agents-in-dashboard.md @@ -4,6 +4,11 @@ - **Author:** didericis - **Created:** 2026-05-26 +> **Superseded.** PRD 0049 removed the active-agents pane and agent-scoped +> operator edit verbs when the dashboard was narrowed back to a proposal-only +> supervise TUI. A future agent-management surface was deferred rather than +> carried forward from this design. The design below is retained as history. + ## Summary The dashboard today is proposal-centric: it lists every pending diff --git a/docs/prds/0020-start-and-attach-from-dashboard.md b/docs/prds/0020-start-and-attach-from-dashboard.md index 1088088a..a88d349e 100644 --- a/docs/prds/0020-start-and-attach-from-dashboard.md +++ b/docs/prds/0020-start-and-attach-from-dashboard.md @@ -4,6 +4,11 @@ - **Author:** didericis - **Created:** 2026-05-26 +> **Superseded.** PRD 0049 removed start, re-attach, and stop actions from the +> dashboard when it became the proposal-only supervise TUI. Bottle lifecycle +> remains in the dedicated CLI commands; no dashboard replacement from this +> design remains active. The design below is retained as history. + ## Summary Today the dashboard is read-only: it surfaces pending proposals diff --git a/docs/prds/0021-dashboard-tmux-split-pane.md b/docs/prds/0021-dashboard-tmux-split-pane.md index ca5fd0bd..abe8e7b1 100644 --- a/docs/prds/0021-dashboard-tmux-split-pane.md +++ b/docs/prds/0021-dashboard-tmux-split-pane.md @@ -4,6 +4,11 @@ - **Author:** didericis - **Created:** 2026-05-26 +> **Superseded.** PRD 0049 removed agent handoff and tmux pane management when +> the dashboard was reduced to the proposal-only supervise TUI. The split-pane +> interaction described below has no active replacement and is retained only +> as design history. + ## Summary When the dashboard runs inside tmux, lay it out as the **left -- 2.52.0 From 1824f2a3880881f848006c300725379ba974c9bd Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 06:46:00 +0000 Subject: [PATCH 4/6] ci(tracker): allow labelled standalone pull requests --- .../0005-issues-own-tracker-metadata.md | 35 ++++++++++++++----- scripts/tracker_policy.py | 16 ++++----- tests/unit/test_tracker_policy.py | 27 +++++++++++--- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/docs/decisions/0005-issues-own-tracker-metadata.md b/docs/decisions/0005-issues-own-tracker-metadata.md index 14220cab..98acbc0d 100644 --- a/docs/decisions/0005-issues-own-tracker-metadata.md +++ b/docs/decisions/0005-issues-own-tracker-metadata.md @@ -4,6 +4,10 @@ - **Date:** 2026-07-18 - **Deciders:** didericis +> **Amended 2026-07-26.** A pull request may carry labels directly instead of +> linking a tracking issue. When a PR does link an issue, the issue remains the +> canonical owner of planning metadata and the reference is validated. + ## Context Gitea exposes labels on both issues and pull requests. Applying the same labels @@ -20,19 +24,28 @@ would make the issue history less truthful. ## Decision -Issues are the canonical tracker records and own labels. Every issue has at -least one label. An issue opened or left without labels receives -`Status/Needs Triage` automatically until it is classified. +Issues are the canonical tracker records and own labels when a separate work +item exists. Every issue has at least one label. An issue opened or left +without labels receives `Status/Needs Triage` automatically until it is +classified. -Pull requests carry no labels. Every new PR deliberately references at least -one existing issue in its title or description with one of these forms: +Every new pull request is tracked in one of two ways: + +1. It deliberately references at least one existing issue in its title or + description. Tracker metadata stays on that issue and the PR remains + unlabelled. +2. It carries at least one label directly when a separate issue would add no + useful planning context. + +Issue references use one of these forms: - `Closes #123`, `Fixes #123`, or `Resolves #123` when merging completes it. - `Part of #123`, `Related to #123`, `Refs #123`, or `References #123` when it contributes without completing it. -Gitea Actions enforces both PR rules as a status check and repairs the empty -issue-label state. Branch protection makes the PR policy check required. +Gitea Actions enforces the either/or PR rule, validates any issue references, +and repairs the empty issue-label state. Branch protection makes the PR policy +check required. The policy applies from 2026-07-18 onward. Existing issues may be labelled as they are encountered, but closed PRs are grandfathered: no retrospective @@ -40,9 +53,13 @@ issues or PR labels are created solely to make history conform. ## Consequences -- Classification, priority, and workflow metadata have one source of truth. -- A PR's issue link is the navigation path to its planning metadata. +- Classification, priority, and workflow metadata have one source of truth for + each change: the linked issue when one exists, otherwise the PR. +- For issue-backed changes, the PR's issue link is the navigation path to its + planning metadata. - Multi-PR issues do not require copied or synchronized labels. +- Small standalone changes do not require a tracking issue created solely to + satisfy automation. - `Status/Needs Triage` is an intentional fallback, not a final classification. - Direct issue creation remains convenient; automation repairs a missing label diff --git a/scripts/tracker_policy.py b/scripts/tracker_policy.py index 4494875f..aec64a24 100644 --- a/scripts/tracker_policy.py +++ b/scripts/tracker_policy.py @@ -54,18 +54,14 @@ def check_pull_request(event: dict[str, Any], api: GiteaApi) -> list[str]: pull = event["pull_request"] errors: list[str] = [] labels = pull.get("labels") or [] - if labels: - errors.append( - "PRs must be unlabeled; put tracker metadata on the linked issue " - f"(found: {', '.join(label['name'] for label in labels)})." - ) - numbers = deliberate_issue_numbers(pull.get("title", ""), pull.get("body", "")) if not numbers: - errors.append( - "PR must reference an issue with Closes/Fixes/Resolves #N, " - "Part of #N, Related to #N, Refs #N, or References #N." - ) + if not labels: + errors.append( + "PR must either have a label or reference an issue with " + "Closes/Fixes/Resolves #N, Part of #N, Related to #N, " + "Refs #N, or References #N." + ) return errors real_issues = 0 diff --git a/tests/unit/test_tracker_policy.py b/tests/unit/test_tracker_policy.py index be79a174..517d33ce 100644 --- a/tests/unit/test_tracker_policy.py +++ b/tests/unit/test_tracker_policy.py @@ -27,7 +27,27 @@ class TestCheckPullRequest(unittest.TestCase): event = {"pull_request": {"title": "Change", "body": "Part of #12", "labels": []}} self.assertEqual(check_pull_request(event, api), []) - def test_rejects_labels_and_pr_reference(self): + def test_accepts_labelled_pr_without_issue(self): + api = Mock() + event = { + "pull_request": { + "title": "Change", + "body": "", + "labels": [{"name": "Kind/Documentation"}], + } + } + self.assertEqual(check_pull_request(event, api), []) + api.request.assert_not_called() + + def test_rejects_unlabelled_pr_without_issue(self): + api = Mock() + event = {"pull_request": {"title": "Change", "body": "", "labels": []}} + errors = check_pull_request(event, api) + self.assertEqual(len(errors), 1) + self.assertIn("either have a label or reference an issue", errors[0]) + api.request.assert_not_called() + + def test_validates_issue_reference_even_when_pr_is_labelled(self): api = Mock() api.request.return_value = {"number": 12, "pull_request": {}} event = { @@ -38,9 +58,8 @@ class TestCheckPullRequest(unittest.TestCase): } } errors = check_pull_request(event, api) - self.assertEqual(len(errors), 2) - self.assertIn("unlabeled", errors[0]) - self.assertIn("not an issue", errors[1]) + self.assertEqual(len(errors), 1) + self.assertIn("not an issue", errors[0]) class TestEnsureIssueLabel(unittest.TestCase): -- 2.52.0 From 652d4ba58159c990e676e00347adf7e72ea54f88 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 06:49:06 +0000 Subject: [PATCH 5/6] ci(tracker): require one metadata owner per pull request --- .../0005-issues-own-tracker-metadata.md | 12 ++++++----- scripts/tracker_policy.py | 6 ++++++ tests/unit/test_tracker_policy.py | 21 ++++++++++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/docs/decisions/0005-issues-own-tracker-metadata.md b/docs/decisions/0005-issues-own-tracker-metadata.md index 98acbc0d..43f3e956 100644 --- a/docs/decisions/0005-issues-own-tracker-metadata.md +++ b/docs/decisions/0005-issues-own-tracker-metadata.md @@ -29,7 +29,8 @@ item exists. Every issue has at least one label. An issue opened or left without labels receives `Status/Needs Triage` automatically until it is classified. -Every new pull request is tracked in one of two ways: +Every new pull request is tracked in exactly one of two mutually exclusive +ways: 1. It deliberately references at least one existing issue in its title or description. Tracker metadata stays on that issue and the PR remains @@ -43,9 +44,9 @@ Issue references use one of these forms: - `Part of #123`, `Related to #123`, `Refs #123`, or `References #123` when it contributes without completing it. -Gitea Actions enforces the either/or PR rule, validates any issue references, -and repairs the empty issue-label state. Branch protection makes the PR policy -check required. +Gitea Actions enforces the exclusive either/or PR rule, validates any issue +references, and repairs the empty issue-label state. Branch protection makes +the PR policy check required. The policy applies from 2026-07-18 onward. Existing issues may be labelled as they are encountered, but closed PRs are grandfathered: no retrospective @@ -70,5 +71,6 @@ issues or PR labels are created solely to make history conform. ## Links - Issue #405. -- `.gitea/workflows/tracker-policy.yml`. +- `.gitea/workflows/tracker-policy-pr.yml`. +- `.gitea/workflows/tracker-policy-issues.yml`. - `scripts/tracker_policy.py`. diff --git a/scripts/tracker_policy.py b/scripts/tracker_policy.py index aec64a24..144aa27a 100644 --- a/scripts/tracker_policy.py +++ b/scripts/tracker_policy.py @@ -55,6 +55,12 @@ def check_pull_request(event: dict[str, Any], api: GiteaApi) -> list[str]: errors: list[str] = [] labels = pull.get("labels") or [] numbers = deliberate_issue_numbers(pull.get("title", ""), pull.get("body", "")) + if labels and numbers: + errors.append( + "PR must use exactly one tracking mode: remove PR labels when " + "linking an issue, or remove the issue reference when labels " + "belong on the PR." + ) if not numbers: if not labels: errors.append( diff --git a/tests/unit/test_tracker_policy.py b/tests/unit/test_tracker_policy.py index 517d33ce..3dba3c3a 100644 --- a/tests/unit/test_tracker_policy.py +++ b/tests/unit/test_tracker_policy.py @@ -47,7 +47,21 @@ class TestCheckPullRequest(unittest.TestCase): self.assertIn("either have a label or reference an issue", errors[0]) api.request.assert_not_called() - def test_validates_issue_reference_even_when_pr_is_labelled(self): + def test_rejects_labelled_pr_linked_to_real_issue(self): + api = Mock() + api.request.return_value = {"number": 12, "pull_request": None} + event = { + "pull_request": { + "title": "Change", + "body": "Closes #12", + "labels": [{"name": "Kind/Documentation"}], + } + } + errors = check_pull_request(event, api) + self.assertEqual(len(errors), 1) + self.assertIn("exactly one tracking mode", errors[0]) + + def test_still_validates_issue_reference_when_both_modes_are_used(self): api = Mock() api.request.return_value = {"number": 12, "pull_request": {}} event = { @@ -58,8 +72,9 @@ class TestCheckPullRequest(unittest.TestCase): } } errors = check_pull_request(event, api) - self.assertEqual(len(errors), 1) - self.assertIn("not an issue", errors[0]) + self.assertEqual(len(errors), 2) + self.assertIn("exactly one tracking mode", errors[0]) + self.assertIn("not an issue", errors[1]) class TestEnsureIssueLabel(unittest.TestCase): -- 2.52.0 From 9b8fd7c2ec67d883bc58007d13a8e3640ba7a4c5 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 06:53:01 +0000 Subject: [PATCH 6/6] docs(adr): align tracker policy title --- docs/decisions/0005-issues-own-tracker-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decisions/0005-issues-own-tracker-metadata.md b/docs/decisions/0005-issues-own-tracker-metadata.md index 43f3e956..5eb3b2d5 100644 --- a/docs/decisions/0005-issues-own-tracker-metadata.md +++ b/docs/decisions/0005-issues-own-tracker-metadata.md @@ -1,4 +1,4 @@ -# ADR 0005: Keep tracker metadata on issues +# ADR 0005: Keep tracker metadata on one tracker object - **Status:** Accepted - **Date:** 2026-07-18 -- 2.52.0