Compare commits
6 Commits
pr-211
...
044a8a3122
| Author | SHA1 | Date | |
|---|---|---|---|
| 044a8a3122 | |||
| bbaecdb559 | |||
| 35f6584fb2 | |||
| 598c96679a | |||
| 8596877cd9 | |||
| 1d2313b98c |
@@ -28,9 +28,10 @@ the container lifecycle and the copying of skills and env vars into it.
|
|||||||
- `bot-bottle.json` — legacy manifest of named agents (env / skills / prompt
|
- `bot-bottle.json` — legacy manifest of named agents (env / skills / prompt
|
||||||
per agent), consumed by `cli.py`. See "Manifest" under
|
per agent), consumed by `cli.py`. See "Manifest" under
|
||||||
"Intended design".
|
"Intended design".
|
||||||
- `docs/INDEX.md` — pointer to the research notes.
|
- `docs/README.md` — docs overview; when to write which document.
|
||||||
- `docs/prds/` — product requirement docs.
|
- `docs/prds/` — product requirement docs (see `docs/prds/README.md` for format).
|
||||||
- `docs/research/` — research notes (empty for now, kept tracked via `.gitkeep`).
|
- `docs/research/` — research notes.
|
||||||
|
- `docs/decisions/` — decision records (ADR-lite).
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Research notes live in `research/`. Product requirement docs live in `prds/`.
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Docs
|
||||||
|
|
||||||
|
How this project records what it builds and why — and a guide to
|
||||||
|
picking the right document for what you're capturing.
|
||||||
|
|
||||||
|
## When to write which document
|
||||||
|
|
||||||
|
| Artifact | For |
|
||||||
|
|---|---|
|
||||||
|
| **PRD** (`docs/prds/`) | A feature: what to build, scope, success criteria. |
|
||||||
|
| **Research note** (`docs/research/`) | A landscape/tradeoff investigation. |
|
||||||
|
| **Decision record** (`docs/decisions/`) | A decision that isn't itself a feature — a policy, a convention, a "we will / won't do this," or a load-bearing choice made inside a larger PRD that deserves to be discoverable on its own. |
|
||||||
|
|
||||||
|
A decision that's fully specified by a PRD doesn't need duplicating in
|
||||||
|
a decision record. Write one when the *decision* would otherwise be
|
||||||
|
buried in prose, lost in an issue thread, or have no in-repo home at
|
||||||
|
all (small requests that don't merit a PRD; non-feature choices like
|
||||||
|
merge strategy or a trust posture).
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# ADR 0001: Merge PRs with rebase, not merge commits
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-05-28
|
||||||
|
- **Deciders:** didericis
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
PRs need a merge strategy. Gitea offers merge-commit, squash, rebase,
|
||||||
|
and rebase-merge. The project uses [Conventional
|
||||||
|
Commits](https://www.conventionalcommits.org/) enforced by a
|
||||||
|
`commit-msg` hook, and PRDs typically land as a multi-commit PR where
|
||||||
|
each commit is meaningful on its own (e.g. PR #95: a `docs(prd)` commit,
|
||||||
|
a `feat(manifest)` implementation commit, and a `docs(manifest)`
|
||||||
|
commit). The history should stay readable and the individual
|
||||||
|
conventional commits should survive onto `main`.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Merge PRs with **rebase** (Gitea's `rebase` style; `Do: "rebase"` via
|
||||||
|
the API). The branch's commits are replayed onto `main` with no merge
|
||||||
|
commit, producing a linear history that preserves each commit verbatim.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- **Linear history**, no merge bubbles; `git log --oneline` reads as a
|
||||||
|
straight sequence of conventional commits.
|
||||||
|
- **Each commit is preserved** (unlike squash, which would collapse the
|
||||||
|
PRD/impl/docs commits into one and lose the staged structure).
|
||||||
|
- **Commit SHAs are rewritten at merge.** The replayed commits on `main`
|
||||||
|
get new SHAs, and the source branch is deleted, so a link to a file
|
||||||
|
by *branch name* (`/src/branch/<feature>/…`) dies at merge. This is
|
||||||
|
why links to not-yet-merged files are pinned to a **commit SHA**
|
||||||
|
(`/src/commit/<sha>/…`), which stays reachable via the retained
|
||||||
|
`refs/pull/<n>/head` ref. See
|
||||||
|
`docs/research/issue-tracking-vs-in-repo-decision-history.md`.
|
||||||
|
- **Trade-off accepted:** without a merge commit, the "these commits
|
||||||
|
landed together as PR #N" grouping is not recorded in git itself — it
|
||||||
|
lives in forge state (the PR). That is a mild concession against the
|
||||||
|
keep-history-in-the-repo posture; the conventional-commit scopes and
|
||||||
|
PRD references in the messages keep changes traceable without it.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- `docs/research/issue-tracking-vs-in-repo-decision-history.md` — the
|
||||||
|
commit-pinning consequence above.
|
||||||
|
- Observed practice: PRs #92, #93 merged with rebase; #95 to follow.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# ADR 0002: Agent-set git identity is claimed, not vouched
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-05-28
|
||||||
|
- **Deciders:** didericis
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
PRD 0027 lifts `git.user` (name/email) to the agent layer, so an agent
|
||||||
|
file may declare its own commit identity. Agent files can live in
|
||||||
|
`$CWD/.bot-bottle/agents/` — i.e. they can be supplied by a cloned,
|
||||||
|
less-trusted repository. That raises the question of whether a
|
||||||
|
repo-supplied agent setting its own git identity is a security concern,
|
||||||
|
and whether agent identity should be gated differently for `$CWD`
|
||||||
|
agents than for `$HOME` agents.
|
||||||
|
|
||||||
|
This record exists because the decision is a **trust posture** worth
|
||||||
|
finding on its own, separate from the feature PRD that introduced it.
|
||||||
|
The full analysis lives in PRD 0027; the decision is summarized here.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Allow agents to set `git.user`, and treat an agent-declared identity as
|
||||||
|
**claimed, not vouched**. No `$CWD`-vs-`$HOME` gating on the identity
|
||||||
|
field. `git.remotes` stays bottle-only (home-only).
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- A cloned repo's agent file can present any commit author name/email,
|
||||||
|
including one that reads like a real person's. This is accepted: git
|
||||||
|
authorship is **not a credential** (push auth is the bottle's remote
|
||||||
|
key/token), is **already forgeable** from inside the bottle at runtime
|
||||||
|
(`git config user.email …`), and was never a trust anchor.
|
||||||
|
- If attribution integrity ever matters, the answer is commit
|
||||||
|
**signing** (SSH/GPG), not the author field — so this decision closes
|
||||||
|
no door that was open.
|
||||||
|
- `git.remotes` is deliberately *not* lifted to the agent layer: it
|
||||||
|
carries credentials and host trust (IdentityFile, KnownHostKey) and
|
||||||
|
remains a bottle-only, home-only concern.
|
||||||
|
- Revisit if a future change ever makes commit identity load-bearing
|
||||||
|
(e.g. enforced signing keyed on author), at which point gating
|
||||||
|
`$CWD`-supplied identities would matter.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
- PRD 0027 (`docs/prds/0027-agent-git-user-identity.md`) — full trust
|
||||||
|
analysis and schema.
|
||||||
|
- Issue #94, PR #95 — the feature this decision was made for.
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Decision records
|
||||||
|
|
||||||
|
Short, durable records of decisions — one file per decision. This is a
|
||||||
|
lightweight [Architecture Decision Record](https://adr.github.io/)
|
||||||
|
practice: capture *what was decided and why* in a versioned file so the
|
||||||
|
reasoning lives in the clone, not in a forge issue thread or a chat log
|
||||||
|
that disappears when the provider does.
|
||||||
|
|
||||||
|
See `docs/research/issue-tracking-vs-in-repo-decision-history.md` for
|
||||||
|
the rationale behind keeping decision history in-repo, and
|
||||||
|
[`docs/README.md`](../README.md) for when to write a decision record
|
||||||
|
vs. a PRD or research note.
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
One Markdown file per decision, numbered sequentially and zero-padded
|
||||||
|
(`0001-…`, `0002-…`), matching the PRD numbering style. Keep it short —
|
||||||
|
the discipline is writing it down, not the ceremony.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# ADR 0000: <short imperative title>
|
||||||
|
|
||||||
|
- **Status:** Proposed | Accepted | Superseded by ADR NNNN
|
||||||
|
- **Date:** YYYY-MM-DD
|
||||||
|
- **Deciders:** <who>
|
||||||
|
|
||||||
|
## Context
|
||||||
|
What forced the decision; the constraints in play.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
What we decided, stated plainly.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
What follows — the good, and the costs/trade-offs accepted.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
PRDs, research notes, issues/PRs. Forge links are convenience
|
||||||
|
pointers; the reasoning above must stand without them.
|
||||||
|
```
|
||||||
|
|
||||||
|
The records are the index: `ls docs/decisions/` or skim the titles.
|
||||||
|
No hand-maintained list to keep in sync.
|
||||||
@@ -39,6 +39,41 @@ trust boundary*: only `$HOME` bottles can declare it, only `$HOME`
|
|||||||
bottles can be its target. Cloned repos still cannot author
|
bottles can be its target. Cloned repos still cannot author
|
||||||
bottle-equivalent config.
|
bottle-equivalent config.
|
||||||
|
|
||||||
|
## Alternatives considered
|
||||||
|
|
||||||
|
The question raised in issue #88 was *where composition should live*.
|
||||||
|
Three points in that design space, recorded here so the decision
|
||||||
|
stands on its own without the issue thread:
|
||||||
|
|
||||||
|
1. **Duplicate bottles (status quo).** Copy `dev.md` to `staging.md`
|
||||||
|
and edit. Zero new mechanism, but every shared field drifts: a
|
||||||
|
route added to `dev` is silently missing from `staging`. This is
|
||||||
|
the pain that prompted #88.
|
||||||
|
|
||||||
|
2. **Agent-side `bottle_config:` override (the original #88
|
||||||
|
proposal).** Let an agent file carry an inline block that merges
|
||||||
|
over its referenced bottle. Ergonomically attractive — one file,
|
||||||
|
no second bottle — but it **breaks the trust boundary**: agent
|
||||||
|
files can come from `$CWD/.bot-bottle/agents/` in a cloned repo, so
|
||||||
|
a clone could redeclare egress routes, env mappings, and git
|
||||||
|
remotes — i.e. grant itself bottle-equivalent authority over
|
||||||
|
credentials and network egress. The home-only-bottle invariant
|
||||||
|
exists precisely to stop this.
|
||||||
|
|
||||||
|
3. **Bottle-side `extends:` (chosen).** Move composition to the
|
||||||
|
bottle layer, where it inherits the home-only property for free:
|
||||||
|
only `$HOME` bottles can declare `extends:`, and only `$HOME`
|
||||||
|
bottles can be its target. Identical duplication relief to option
|
||||||
|
2, none of its trust erosion. The cost is that an override requires
|
||||||
|
a (home-owned) child bottle rather than an inline agent block —
|
||||||
|
which is the *point*: the override authority stays in `$HOME`.
|
||||||
|
|
||||||
|
`extends:` wins because it solves the duplication pain entirely on the
|
||||||
|
trusted side of the agent-vs-bottle boundary. (PRD 0027 later lifts a
|
||||||
|
deliberately narrow, non-credential field — `git.user` — to the agent
|
||||||
|
layer, on the separate reasoning that commit identity is not a
|
||||||
|
capability; egress, credentials, and remotes stay bottle-only.)
|
||||||
|
|
||||||
## Goals / Success Criteria
|
## Goals / Success Criteria
|
||||||
|
|
||||||
- Add `extends: <bottle-name>` to the bottle frontmatter schema.
|
- Add `extends: <bottle-name>` to the bottle frontmatter schema.
|
||||||
@@ -58,9 +93,9 @@ bottle-equivalent config.
|
|||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
- **No agent-side `bottle_config:`.** That's the design issue #88
|
- **No agent-side `bottle_config:`.** Option 2 under "Alternatives
|
||||||
considered and weighed against; this PRD is the alternative
|
considered" — weighed and rejected on trust grounds. Don't
|
||||||
picked in the issue's design discussion. Don't reintroduce it.
|
reintroduce it.
|
||||||
- **No additive list merges** (e.g., `routes: append` keyword).
|
- **No additive list merges** (e.g., `routes: append` keyword).
|
||||||
The `extends:` design uses full-replace for list-valued fields
|
The `extends:` design uses full-replace for list-valued fields
|
||||||
(see "Merge rules"); if a use case shows up that genuinely
|
(see "Merge rules"); if a use case shows up that genuinely
|
||||||
@@ -167,7 +202,7 @@ Bottles continue to be loaded from `$HOME/.bot-bottle/bottles/`
|
|||||||
only (`Manifest.from_md_dirs` is unchanged). The `extends:` field
|
only (`Manifest.from_md_dirs` is unchanged). The `extends:` field
|
||||||
references another file in that same directory. No cwd-readable
|
references another file in that same directory. No cwd-readable
|
||||||
file gains the ability to declare or modify bottle config — the
|
file gains the ability to declare or modify bottle config — the
|
||||||
attack surface from issue #88's comment thread stays closed.
|
attack surface from option 2 ("Alternatives considered") stays closed.
|
||||||
|
|
||||||
If a future change ever introduces cwd-loaded bottles, the
|
If a future change ever introduces cwd-loaded bottles, the
|
||||||
`extends:` resolver should be gated to forbid a `$CWD` bottle
|
`extends:` resolver should be gated to forbid a `$CWD` bottle
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Product requirement docs
|
||||||
|
|
||||||
|
One PRD per feature: what to build, why, and how it's scoped. The PRD
|
||||||
|
is the durable spec — it should stand on its own without a forge issue
|
||||||
|
thread (see [`../README.md`](../README.md) for when a PRD is the right
|
||||||
|
document vs. a research note or a decision record).
|
||||||
|
|
||||||
|
## Naming and numbering
|
||||||
|
|
||||||
|
`NNNN-kebab-title.md`, zero-padded and sequential (`0024-…`, `0025-…`).
|
||||||
|
Numbers are never reused; gaps are fine (there is no 0005). The number
|
||||||
|
is assigned at creation and stays fixed for the life of the doc.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
The `Status:` line near the top tracks the PRD's lifecycle:
|
||||||
|
|
||||||
|
- **Draft** — proposed, not yet shipped.
|
||||||
|
- **Active** — the design has shipped to `main` and is in effect.
|
||||||
|
- **Superseded by [PRD NNNN](…)** — replaced by a later PRD; kept for history.
|
||||||
|
- **Retargeted by [PRD NNNN](…)** — folded into a later PRD's scope.
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# PRD NNNN: <short title>
|
||||||
|
|
||||||
|
- **Status:** Draft
|
||||||
|
- **Author:** <who>
|
||||||
|
- **Created:** YYYY-MM-DD
|
||||||
|
- **Issue:** #<n> # optional — convenience pointer only
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
One paragraph: what this builds and the pain it solves.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
The current state and why it's inadequate.
|
||||||
|
|
||||||
|
## Goals / Success Criteria
|
||||||
|
Bullets a reviewer can check the finished work against.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
What this explicitly does not do — and won't, to head off scope creep.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
In scope / out of scope, when the boundary needs spelling out.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
How it works: schema, data flow, diagrams, algorithms as needed.
|
||||||
|
|
||||||
|
## Implementation chunks
|
||||||
|
Ordered, mergeable steps (optional; for multi-PR features).
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
Unresolved decisions — resolve or fold into Design before shipping.
|
||||||
|
```
|
||||||
|
|
||||||
|
Sections are a guide, not a straitjacket: drop the ones a given PRD
|
||||||
|
doesn't need (a small change rarely needs Scope or Implementation
|
||||||
|
chunks) and add others where they help (e.g. Testing strategy,
|
||||||
|
Alternatives considered, References). Keep the rationale self-contained
|
||||||
|
— inline the reasoning rather than linking out to an issue thread, so
|
||||||
|
the PRD survives a forge migration.
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
# Tracking feature requests in Gitea vs. in-repo decision history
|
||||||
|
|
||||||
|
Research into whether bot-bottle should track feature requests (and the
|
||||||
|
decision-making around them) as Gitea issues, given that the project
|
||||||
|
already records specs in-repo as PRDs (`docs/prds/`) and rationale as
|
||||||
|
research notes (`docs/research/`). The stated constraint is that the
|
||||||
|
*history of why we decided things* should be durable and portable —
|
||||||
|
not locked into a single forge (Gitea today, conceivably GitHub or
|
||||||
|
something else tomorrow).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Keep using issues, but demote them. The repository — not the forge — is
|
||||||
|
the system of record for any decision you would be unhappy to lose.
|
||||||
|
Issues are an excellent **inbox and coordination surface** (cheap
|
||||||
|
capture, triage, async discussion, notifications, auto-linking) and a
|
||||||
|
**poor archive** (provider-locked storage, brittle numeric references,
|
||||||
|
rationale stranded in comment threads). The failure mode to avoid is the
|
||||||
|
one already present in the repo: a PRD whose reasoning is only complete
|
||||||
|
if you also read a Gitea issue thread.
|
||||||
|
|
||||||
|
The fix is a discipline, not a tool: **every load-bearing decision gets
|
||||||
|
reified into a versioned file in the repo before the issue that prompted
|
||||||
|
it is closed.** PRDs already do this for features; the gap is (a) small
|
||||||
|
requests that never merit a PRD and (b) decisions that aren't features
|
||||||
|
at all (e.g. "we merge with rebase," "author identity is claimed-not-
|
||||||
|
vouched"). Close that gap with a lightweight in-repo decision log. Then
|
||||||
|
issues can be as disposable as the forge makes them, and migrating
|
||||||
|
forges costs you triage state, not history.
|
||||||
|
|
||||||
|
## Why this even comes up here
|
||||||
|
|
||||||
|
The project already leans on the repo for durable artifacts:
|
||||||
|
|
||||||
|
- **PRDs** (`docs/prds/0001…0027`) — the spec and its rationale.
|
||||||
|
- **Research notes** (`docs/research/`) — the "why," with tradeoffs.
|
||||||
|
- **Conventional-commit history** — a machine-greppable change log.
|
||||||
|
|
||||||
|
But the issue layer has quietly become load-bearing in places:
|
||||||
|
|
||||||
|
- PRD 0025 says it picked "option 3" *"from the #88 design
|
||||||
|
discussion"* and that the rejected alternative lives "in issue #88's
|
||||||
|
comment thread." The PRD's rationale is therefore **incomplete without
|
||||||
|
the issue**. If Gitea is gone, the strongest argument for the chosen
|
||||||
|
design is gone with it.
|
||||||
|
- PR #89's description links `…/didericis/claude-bottle/issues/88` —
|
||||||
|
the **pre-rename** repo path (the project was Codex-bottle/claude-
|
||||||
|
bottle before the bot-bottle rebrand). That link is already
|
||||||
|
half-dead: a concrete demonstration that forge URLs rot under the
|
||||||
|
most routine event imaginable, a rename.
|
||||||
|
- Issue/PR numbers (`#88`, `#90`, `#94`, `#95`) are **forge-assigned
|
||||||
|
from a shared sequence**. They cannot be reconstructed from a clone,
|
||||||
|
and they collide/renumber on import into a different tracker.
|
||||||
|
|
||||||
|
So the question isn't academic. The current practice is already
|
||||||
|
producing references that don't survive a rename, let alone a migration.
|
||||||
|
|
||||||
|
## What each medium is actually good at
|
||||||
|
|
||||||
|
| Concern | Gitea issue | In-repo file (PRD / note / log) |
|
||||||
|
|---|---|---|
|
||||||
|
| Capture friction | Near-zero — file a one-line idea | High — a PRD is a heavy artifact; a note less so |
|
||||||
|
| Triage (labels, milestones, open/closed, assignee) | Native, good | Absent / hand-rolled |
|
||||||
|
| Async discussion + notifications | Native (threads, @mentions, watch) | None — needs a PR review or out-of-band chat |
|
||||||
|
| Auto-linking (`Closes #N`, PR↔issue, commit↔issue) | Native | Manual cross-reference |
|
||||||
|
| Version control of the content | None — lives in Gitea's DB | Full — diff, blame, branch, revert |
|
||||||
|
| Travels with `git clone` | No | Yes |
|
||||||
|
| Survives forge migration | Degrades (export/import; threads, authors, timestamps, refs lossy) | Unaffected |
|
||||||
|
| Survives forge outage | Inaccessible | Local clone has it |
|
||||||
|
| Greppable offline / by tooling | Only via API | `grep docs/` |
|
||||||
|
| Reproducible identifiers | Forge-assigned numbers | Filenames you control (`0027-…`) |
|
||||||
|
|
||||||
|
The split is clean: **issues win on the live, social, coordination axes;
|
||||||
|
the repo wins on every durability and portability axis.** Nothing about
|
||||||
|
that table says "pick one." It says "use each for what it's good at, and
|
||||||
|
don't let the durable thing depend on the ephemeral one."
|
||||||
|
|
||||||
|
## Lock-in failure modes (the cons, concretely)
|
||||||
|
|
||||||
|
1. **Stranded rationale.** The single most valuable output of a feature
|
||||||
|
discussion — *why we rejected the obvious alternative* — usually
|
||||||
|
emerges in a thread and dies there unless someone copies it into the
|
||||||
|
spec. PRD 0025 is already in this state.
|
||||||
|
2. **Reference rot.** `Closes #88` / "see issue #90" are meaningful only
|
||||||
|
against one forge instance at one point in time. A rename already
|
||||||
|
broke one such link; a migration would break all of them and
|
||||||
|
silently renumber the survivors.
|
||||||
|
3. **Two sources of truth.** A PRD carries `Status: Draft`; the issue
|
||||||
|
carries open/closed. They drift. Which is authoritative?
|
||||||
|
4. **Availability coupling.** Self-hosted Gitea down (or the Tailscale
|
||||||
|
path to it down) means the backlog and its history are unreachable,
|
||||||
|
even though the code and PRDs are right there in the clone.
|
||||||
|
5. **Export is lossy.** Gitea→GitHub (or the reverse) moves issue *text*
|
||||||
|
tolerably but mangles cross-references, comment authorship for
|
||||||
|
non-mapped users, timestamps, and reactions. The graph of "#88 → PR
|
||||||
|
#89 → commit abc" does not survive intact.
|
||||||
|
|
||||||
|
None of these are arguments against *having* issues. They're arguments
|
||||||
|
against issues being the **only** place a decision is recorded.
|
||||||
|
|
||||||
|
## Pros of keeping issues anyway
|
||||||
|
|
||||||
|
Worth stating plainly, because "just use the repo for everything"
|
||||||
|
overcorrects:
|
||||||
|
|
||||||
|
- A PR per half-formed idea is absurd; issues are the right weight for
|
||||||
|
"someone should look at X someday."
|
||||||
|
- Triage state (priority, milestone, assignee, open/closed) is genuine
|
||||||
|
project-management value the repo does not natively provide.
|
||||||
|
- Notifications and threaded discussion are how a decision *gets made*
|
||||||
|
before it's ready to be written down. Killing issues doesn't move that
|
||||||
|
conversation into the repo — it moves it into chat/DMs, which is
|
||||||
|
*worse* for durability, not better.
|
||||||
|
- `Closes #N` automation and PR↔issue linkage are real ergonomics.
|
||||||
|
|
||||||
|
The goal is not to abandon the tracker. It's to make sure that when the
|
||||||
|
tracker eventually goes away, you lose the *backlog*, not the *history*.
|
||||||
|
|
||||||
|
## What belongs where
|
||||||
|
|
||||||
|
- **Gitea issue** — intake, triage, status, and the live discussion.
|
||||||
|
Treat it as a **cache**: useful now, expendable later.
|
||||||
|
- **PRD (`docs/prds/`)** — the durable spec for anything that warrants
|
||||||
|
one. Rule: a PRD must be **self-contained**. Synthesize the issue
|
||||||
|
discussion into the Problem / Design / Open-questions sections;
|
||||||
|
reference the issue as a convenience pointer, never as the only home
|
||||||
|
of a load-bearing argument. (Retrofit PRD 0025: inline the #88
|
||||||
|
"option 3 vs `bottle_config:`" reasoning so the PRD stands alone.)
|
||||||
|
- **Research note (`docs/research/`)** — the durable "why," exactly like
|
||||||
|
this file. Comparative analysis, landscape surveys, tradeoffs.
|
||||||
|
- **Commit message** — the durable "what changed and why, at this point
|
||||||
|
in the diff."
|
||||||
|
- **Decision log (proposed, see below)** — durable record of decisions
|
||||||
|
that aren't features and don't merit a PRD.
|
||||||
|
|
||||||
|
## Closing the gap: a portable decision record
|
||||||
|
|
||||||
|
Two classes of decision currently have no in-repo home:
|
||||||
|
|
||||||
|
- **Sub-PRD feature requests** — too small for a PRD, but you still want
|
||||||
|
a tracked "we will / won't do this, because." Today these live only as
|
||||||
|
issues.
|
||||||
|
- **Non-feature decisions** — "merge with rebase, not merge-commit,"
|
||||||
|
"agent identity is claimed-not-vouched," "bottles are home-only."
|
||||||
|
Some land inside a PRD that happens to touch them; many are folded
|
||||||
|
into chat and lost.
|
||||||
|
|
||||||
|
Options, cheapest first:
|
||||||
|
|
||||||
|
1. **An ADR-lite log under `docs/decisions/`.** One short Markdown file
|
||||||
|
per decision: context, decision, consequences, date, links. This is
|
||||||
|
the industry-standard Architecture Decision Record pattern, and it's
|
||||||
|
a near-exact fit for "track decision history, portably." Numbered
|
||||||
|
like PRDs (`0001-merge-with-rebase.md`). ~10 lines each; the
|
||||||
|
discipline is writing them, not the format.
|
||||||
|
2. **Reuse the journal.** The repo ships an `init-entry` skill that
|
||||||
|
writes timestamped prose to `docs/JOURNAL.md` (not yet created here).
|
||||||
|
A stream-of-thought journal is a fine home for decision *narrative*
|
||||||
|
and is already part of the toolchain — lower ceremony than ADRs, less
|
||||||
|
structured for later retrieval. The `tag-entries` skill could tag
|
||||||
|
decision entries for grep-ability.
|
||||||
|
3. **Periodic issue export.** Belt-and-suspenders: a scheduled job hits
|
||||||
|
the Gitea API and dumps open/closed issues + comments to JSON under
|
||||||
|
`docs/issues-archive/`, committed. Preserves the raw thread against
|
||||||
|
forge loss without changing daily workflow. Mechanical, not a
|
||||||
|
substitute for reifying rationale (a JSON dump of a thread is
|
||||||
|
evidence, not a decision).
|
||||||
|
|
||||||
|
These compose: ADRs/journal for the *decision*, optional export for the
|
||||||
|
*raw evidence*, issues for *live coordination*.
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
1. **Keep Gitea issues for intake, triage, and discussion.** Don't fight
|
||||||
|
the forge on the things it's good at.
|
||||||
|
2. **Make the repo the system of record.** Adopt the rule: no decision
|
||||||
|
is "done" until its rationale exists in a versioned file (PRD,
|
||||||
|
research note, or decision log). The issue is a pointer, never the
|
||||||
|
sole source.
|
||||||
|
3. **Add `docs/decisions/` (ADR-lite).** Smallest change that closes the
|
||||||
|
real gap — sub-PRD requests and non-feature decisions. Start by
|
||||||
|
back-filling the few decisions already made only in threads or chat
|
||||||
|
(rebase-merge policy; the agent-identity trust call from PRD 0027).
|
||||||
|
4. **Retrofit PRD 0025** to inline its #88 rationale, removing the one
|
||||||
|
existing hard dependency on a forge thread.
|
||||||
|
5. **Treat issue numbers as disposable.** When a PRD/commit cites an
|
||||||
|
issue, ensure the cited content is mirrored in-repo so the citation
|
||||||
|
degrades to a dead-but-harmless link, not lost information. (The
|
||||||
|
already-broken `claude-bottle/issues/88` link is the warning.)
|
||||||
|
6. **Optional:** automate a Gitea issue export into the repo if you want
|
||||||
|
the raw threads preserved without manual transcription.
|
||||||
|
|
||||||
|
Net: issues stay, because the alternative to issues is chat, which is
|
||||||
|
worse. But the project's durable memory must live where the project
|
||||||
|
already lives — in the clone — so that switching forges, or losing one,
|
||||||
|
costs you a backlog you can rebuild, never a history you can't.
|
||||||
Reference in New Issue
Block a user