Resolve the five design-session open items from PLAN.md: - decisions.md: soft write-path, poll-only sync (NAT), lognormal cold-start priors, purity test binds the SQLite cache - pm-state.md: sidecar layout + directive/capacity/calibration schemas + lifecycle inference table - agent-tools.md: query_project read tool + three write tools Also gitignore .env.* (protect the gitea PAT) and record the P0 actual: 10 labels, 5 milestones, 34 tracer-bullet issues + 51 dependencies filed on christian/commitea as the first managed project. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
74 lines
3.7 KiB
Markdown
74 lines
3.7 KiB
Markdown
# Settled design decisions
|
||
|
||
Resolves the "Open items for design session" in [PLAN.md](./PLAN.md). Dated
|
||
2026-07-08. Companion docs: [pm-state.md](./pm-state.md) (sidecar formats),
|
||
[agent-tools.md](./agent-tools.md) (tool schemas).
|
||
|
||
## D1 — Write path is soft, split by semantics
|
||
|
||
Chat (Reginald) is the write path for **PM-semantic** mutations; gitea-native
|
||
content stays directly editable.
|
||
|
||
- **Through the agent** — estimates (`est/*`), priority (`p/*`),
|
||
`deadline/hard`, milestone assignment, dependency edits, and directives.
|
||
Additive ops act directly; destructive ops go propose-approve (Dialog or the
|
||
consequence diff on the Directives screen).
|
||
- **Direct, reconciled** — issue title/description, comments, assignees. Edit
|
||
them in the CommiTea UI ("composer writes to gitea, as you") or in the gitea
|
||
web UI; reconcile absorbs out-of-band edits because **gitea is the source of
|
||
truth** for intent. The agent observes changes on the next reconcile and may
|
||
object in standup, but never blocks them.
|
||
|
||
Rejected: strict (every write through chat). Hostile to quick edits and fights
|
||
the reconcile-from-gitea model — an edit made in gitea's own web UI would be
|
||
un-representable.
|
||
|
||
## D2 — Sync is poll + reconcile, no live webhooks in v1
|
||
|
||
`gitea.stephenmann.io` is remote and the desktop app sits behind NAT, so the
|
||
server cannot POST to a localhost webhook. v1:
|
||
|
||
- **Reconcile on launch** — full read of the work repo(s) + `pm-state` repo into
|
||
the local SQLite cache.
|
||
- **Light poll while running** — `since`/conditional-request poll of issues and
|
||
the issue timeline (target 30–60 s cadence; visible < 2 s is a webhook-era
|
||
goal, relaxed to the poll interval for v1).
|
||
- The change-source is an **interface** (`ChangeSource`) with a polling
|
||
implementation; a webhook implementation can plug in later for a LAN /
|
||
self-hosted / tunnelled instance without touching the reconcile core.
|
||
|
||
Rejected now: reachability-detection hybrid (moving parts, cleanup-on-quit),
|
||
outbound tunnel (runtime dependency + public ingress). Both remain future
|
||
options behind the same interface.
|
||
|
||
## D3 — Cold-start forecasts use lognormal per-bucket priors
|
||
|
||
Before the team has n ≥ 20 closed issues with estimates, Monte Carlo samples a
|
||
**lognormal** actual-duration distribution per estimate bucket
|
||
(`1d/2d/3d/5d/8d`), with a pessimism-skewed median (actuals run long). The
|
||
priors live in code (`@commitea/core`), not in a data file. At n ≥ 20 the
|
||
scheduler switches to the team's own empirical fit (see calibration model in
|
||
[pm-state.md](./pm-state.md)); `byLabel` / `byPerson` bias terms layer on once
|
||
their own sample sizes clear a floor.
|
||
|
||
Rejected: uniform pessimism multiplier (`actual = est × U[1.3, 2.0]`) — cruder,
|
||
dishonest tails, no path to per-bucket calibration.
|
||
|
||
## D4 — Purity test applies to the SQLite cache, not the pm-state repo
|
||
|
||
The plan's invariant — *delete the sidecar → resync → no truth lost* — is about
|
||
the **local SQLite cache**, which is a rebuildable index over two durable
|
||
sources:
|
||
|
||
- **Work repo(s) in gitea** — human-authored intent (issues, milestones + due
|
||
dates, dependencies, assignees, labels, comments).
|
||
- **`pm-state` repo in gitea** — the sidecar's own durable truth that is *not*
|
||
regenerable from the work repo: directive log, capacity config, charter, and
|
||
the (cached-but-committed) calibration model.
|
||
|
||
Delete SQLite → rebuild from both repos → nothing lost. The `pm-state` repo is
|
||
never the thing you delete; it is versioned and backed up in gitea like any
|
||
other repo. Regenerable state (issue mirror, inferred lifecycle timestamps,
|
||
Monte Carlo forecasts, focus snapshot) lives in SQLite only and is recomputed on
|
||
rebuild. See [pm-state.md](./pm-state.md) for the file/table split.
|