docs: adopt workspace-wide agent standards and retire sow-docs
ci / ci (pull_request) Canceled after 21s
ci / ci (pull_request) Canceled after 21s
Scaffold the per-repo configuration the engineering skills expect, and put
the three-way split of work into standing law:
- Live work -> wayfinder maps + Gitea issues (closeable, assignable,
queryable).
- Settled decisions -> ADR files in docs/adr/ (immutable, findable, never
closed).
- Standing law -> DOCTRINE.md / AGENTS.md / CONTEXT.md.
Adds docs/agents/{issue-tracker,triage-labels,domain}.md and an
"Agent skills" section in AGENTS.md. The tracker doc records Gitea via the
tea CLI, the wayfinder map/ticket/blocking operations, and the rule that
issues follow ownership: file work in the repo that owns it, not the one you
happen to be standing in.
ADR-0001 (markdown is reference, law, or an ADR - nothing else) is adopted
from sow-codebase so the rule is local to every repo.
sow-docs is retired: its 35 decisions become ADRs in the repos that own them,
its living docs move to sow-platform and sow-assets-manifest, and its
unfinished intent becomes Gitea issues.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -87,3 +87,21 @@ make smoke # assert fail-closed contract
|
|||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
Tests must survive harmless changes to constants, defaults, wording, ordering, fixture data, and internal implementation details. A test that fails merely because a basic value changed is usually a bad test. Only assert exact values when the value is part of a documented public contract, external protocol, compatibility requirement, security rule, migration, or business rule.
|
Tests must survive harmless changes to constants, defaults, wording, ordering, fixture data, and internal implementation details. A test that fails merely because a basic value changed is usually a bad test. Only assert exact values when the value is part of a documented public contract, external protocol, compatibility requirement, security rule, migration, or business rule.
|
||||||
|
|
||||||
|
## Agent skills
|
||||||
|
|
||||||
|
### Issue tracker
|
||||||
|
|
||||||
|
Issues live in Gitea at git.westgate.pw (`ShadowsOverWestgate/sow-tools`), managed with the `tea` CLI. Issues follow ownership — file work in the repo that owns it, not the one you happen to be standing in. See `docs/agents/issue-tracker.md`.
|
||||||
|
|
||||||
|
### Triage labels
|
||||||
|
|
||||||
|
Default label vocabulary (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). See `docs/agents/triage-labels.md`.
|
||||||
|
|
||||||
|
### Domain docs
|
||||||
|
|
||||||
|
Single-context: `CONTEXT.md` at the repo root plus `docs/adr/`. See `docs/agents/domain.md`.
|
||||||
|
|
||||||
|
### Where work lives
|
||||||
|
|
||||||
|
Markdown here is **reference, law, or an ADR — nothing else** (`sow-codebase` ADR-0001). Live work -> wayfinder maps + Gitea issues (closeable, assignable, queryable). Settled decisions -> ADR files in `docs/adr/` (immutable, never closed, only superseded). Standing law -> `DOCTRINE.md` / `AGENTS.md` / `CONTEXT.md`. Current-state reference -> docs describing what the code does now. Everything else — plans, specs, concepts, handoffs, trackers — is process: it belongs in a Gitea issue, not a file. Harvest unfinished intent to an issue before deleting a process doc. `sow-docs` is deprecated and read-only.
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# ADR-0001: Markdown in this repo is reference, law, or an ADR — nothing else
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-07-24
|
||||||
|
- **Origin:** Adopted workspace-wide from `sow-codebase` ADR-0001, which records
|
||||||
|
the full context (a `docs/` tree that had grown to 434 files, ~27MB, most of
|
||||||
|
it dead process artifacts). This repo adopts the same law so the rule is
|
||||||
|
local, not a cross-repo reference.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Markdown that *claims to describe current state or a plan* rots, because
|
||||||
|
reality diverges and nobody edits the file. Markdown that claims neither — a
|
||||||
|
dated, immutable decision record, or a standing rule — does not rot.
|
||||||
|
|
||||||
|
Two different things get conflated:
|
||||||
|
|
||||||
|
- **Rationale** — why a system is shaped the way it is. Worth keeping.
|
||||||
|
- **Process artifacts** — plans, specs, concepts, handoffs, trackers. A record
|
||||||
|
of how work happened, not what is true now.
|
||||||
|
|
||||||
|
Left unchecked, every completed effort leaves its planning behind and the repo
|
||||||
|
accumulates a "historical" pile that agents and humans must route around to
|
||||||
|
find the few live docs.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Markdown may exist in this repo only as one of three genres:
|
||||||
|
|
||||||
|
1. **Current-state reference** — describes what the code does now, kept honest
|
||||||
|
by code review touching it.
|
||||||
|
2. **Standing law** — `DOCTRINE.md`, root and folder-scoped `AGENTS.md`,
|
||||||
|
`CONTEXT.md`. States rules and vocabulary, not plans.
|
||||||
|
3. **Immutable decision record** — ADRs under `docs/adr/`. Append-only; never
|
||||||
|
edited, only superseded by a later ADR that points back.
|
||||||
|
|
||||||
|
Anything else — implementation plans, design specs, concepts, handoffs,
|
||||||
|
progress trackers, scratch — is **process** and does not live in the repo. It
|
||||||
|
lives in Gitea issues and wayfinder maps, where it can be assigned, closed, and
|
||||||
|
superseded. Small tasks need no written plan at all.
|
||||||
|
|
||||||
|
Deleting a process document is not destroying history: `git log -- <path>`
|
||||||
|
recovers it. The exception is *unfinished intent* (a design never built, an
|
||||||
|
open question still wanted) — that is live, not history, and must be harvested
|
||||||
|
to a Gitea ticket before its file is deleted. A citation from a living doc or
|
||||||
|
from code must be resolved before the cited file is deleted.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The documentation map lists only current truth; there is no "historical" pile
|
||||||
|
to route around.
|
||||||
|
- No agent, under any plugin or skill, writes process-markdown into the repo.
|
||||||
|
The rule is plugin-agnostic on purpose — it binds the agent, not a named tool.
|
||||||
|
- History of deleted process docs is in git; unfinished intent is in the issue
|
||||||
|
tracker; rationale is in ADRs and law. Each thing has exactly one home.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# ADR-0002: `sow-tools` becomes the Crucible suite
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-06-11
|
||||||
|
- **Migrated from:** `sow-docs` `07-decisions-log.md` entry **D11**, on the
|
||||||
|
retirement of `sow-docs`. Content is the original decision, unchanged.
|
||||||
|
|
||||||
|
- **Decision:** Rename the future toolchain surface to Crucible: one Go module,
|
||||||
|
multiple `cmd/` binaries, plus a dispatcher so wrapper commands can stay
|
||||||
|
stable.
|
||||||
|
- **Rationale:** Depot, HAK, module, topdata, and wiki tooling share internals
|
||||||
|
but have different command surfaces. One module avoids premature repo splits.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# ADR-0003: Crucible binary contract: standalone shims, fail-closed scaffold, no committed binaries
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-06-11
|
||||||
|
- **Migrated from:** `sow-docs` `07-decisions-log.md` entry **D19**, on the
|
||||||
|
retirement of `sow-docs`. Content is the original decision, unchanged.
|
||||||
|
|
||||||
|
- **Decision:** Phase 5 builds `migration/sow-tools` (Crucible, D11) as a
|
||||||
|
multi-binary Go scaffold:
|
||||||
|
- One `crucible` dispatcher plus standalone `crucible-{depot,hak,module,topdata,wiki}`
|
||||||
|
binaries sharing one registry (`internal/dispatch`). The dispatcher is for
|
||||||
|
humans/CI; the **standalone shims are canonical for consumers** so wrapper
|
||||||
|
scripts resolve a single-token command and `"$builder" args` quoting stays
|
||||||
|
correct.
|
||||||
|
- Consumer resolution order: explicit env override
|
||||||
|
(`$SOW_MODULE_BUILD`/`$SOW_TOPDATA_BUILD`/`$CRUCIBLE`) → `crucible-<name>` →
|
||||||
|
legacy `sow-<name>-build`. CI runs inside the pinned `crucible:<sha>` image.
|
||||||
|
- Every builder is **unwired** in the scaffold and fails closed (exit 70);
|
||||||
|
it never fakes an artifact. The `internal/` logic is migrated from
|
||||||
|
`gitea/sow-tools` by the operator at cutover (hard rule: no source
|
||||||
|
transplant by tooling).
|
||||||
|
- **Binaries are never committed** — they are CI artifacts / image layers.
|
||||||
|
Retires the committed `nwn-tool` / `tools/sow-toolkit`.
|
||||||
|
- Builders take `NWN_ROOT` only via explicit env/flag; no `$HOME` defaulting.
|
||||||
|
- **Rationale:** Locks the command surface, container, and CI shape so migrated
|
||||||
|
logic drops into a stable frame; aligns the three artifact repos onto one
|
||||||
|
Crucible contract while keeping back-compat with the pre-D11 skeletons.
|
||||||
|
- **Image name:** `registry.westgate.pw/deployment/crucible:<git-sha>` (the bare
|
||||||
|
`crucible:<sha>` is the local/dev tag).
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# ADR-0004: Nix-built OCI images start server-side with Crucible; local Anvil images deferred
|
||||||
|
|
||||||
|
- **Status:** Accepted
|
||||||
|
- **Date:** 2026-06-12
|
||||||
|
- **Migrated from:** `sow-docs` `07-decisions-log.md` entry **D29**, on the
|
||||||
|
retirement of `sow-docs`. Content is the original decision, unchanged.
|
||||||
|
|
||||||
|
- **Decision:** Introduce Nix-built OCI images as an artifact-production option,
|
||||||
|
starting with `sow-tools`/Crucible in server-side CI. `sow-tools` will grow a
|
||||||
|
Nix-built `crucible` package plus `crucible-image`; PR CI builds/smokes it and
|
||||||
|
`main` publishes the normal pinned OCI image
|
||||||
|
`registry.westgate.pw/deployment/crucible:<sha>`. `docker/Dockerfile` stays during
|
||||||
|
parity and as the client-compatible fallback.
|
||||||
|
- **Local rule:** Nix users may get optional wrappers that provide tools and
|
||||||
|
call the existing Dockerfiles for local Anvil/NWServer testing. A true
|
||||||
|
`nix build .#nwserver-test-image` is explicitly deferred until real
|
||||||
|
`sow-codebase/src` exists and the Dockerfile image is proven.
|
||||||
|
- **Rationale:** Server-side image build shape is harder to change once deploy
|
||||||
|
promotion and registry gates are active, so prove Nix image builds before
|
||||||
|
cutover. Crucible is the lowest-risk pilot because it is a Go toolchain image;
|
||||||
|
NodeBB and Anvil/NWServer depend more heavily on upstream container filesystem
|
||||||
|
semantics.
|
||||||
|
- **Non-goals:** no source builds in `sow-platform`; no `nix-sidecar` or
|
||||||
|
Kubernetes-style runtime cache layer; no removal of client Dockerfiles.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Domain Docs
|
||||||
|
|
||||||
|
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
|
||||||
|
|
||||||
|
## Before exploring, read these
|
||||||
|
|
||||||
|
- **`CONTEXT.md`** at the repo root, or
|
||||||
|
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
||||||
|
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
||||||
|
|
||||||
|
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
||||||
|
|
||||||
|
## File structure
|
||||||
|
|
||||||
|
Single-context repo (most repos):
|
||||||
|
|
||||||
|
```
|
||||||
|
/
|
||||||
|
├── CONTEXT.md
|
||||||
|
├── docs/adr/
|
||||||
|
│ ├── 0001-event-sourced-orders.md
|
||||||
|
│ └── 0002-postgres-for-write-model.md
|
||||||
|
└── src/
|
||||||
|
```
|
||||||
|
|
||||||
|
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
||||||
|
|
||||||
|
```
|
||||||
|
/
|
||||||
|
├── CONTEXT-MAP.md
|
||||||
|
├── docs/adr/ ← system-wide decisions
|
||||||
|
└── src/
|
||||||
|
├── ordering/
|
||||||
|
│ ├── CONTEXT.md
|
||||||
|
│ └── docs/adr/ ← context-specific decisions
|
||||||
|
└── billing/
|
||||||
|
├── CONTEXT.md
|
||||||
|
└── docs/adr/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use the glossary's vocabulary
|
||||||
|
|
||||||
|
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
||||||
|
|
||||||
|
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
||||||
|
|
||||||
|
## Flag ADR conflicts
|
||||||
|
|
||||||
|
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
||||||
|
|
||||||
|
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Issue tracker: Gitea (via tea)
|
||||||
|
|
||||||
|
Issues for this repo live in Gitea at `git.westgate.pw`, repo
|
||||||
|
`ShadowsOverWestgate/sow-tools`. Use the `tea` CLI for all operations —
|
||||||
|
`gh` does not work here. For anything `tea` lacks a subcommand for, use
|
||||||
|
`tea api <endpoint>` (Gitea's API mirrors GitHub's closely).
|
||||||
|
|
||||||
|
Authenticate with your own `tea` login (`tea login add`); never commit tokens
|
||||||
|
or tea config into this repo. Note Gitea blocks self-review, so approving a PR
|
||||||
|
needs a different account than the one that opened it.
|
||||||
|
|
||||||
|
## Where work lives
|
||||||
|
|
||||||
|
Markdown in this repo is **reference, law, or an ADR — nothing else**
|
||||||
|
(`sow-codebase` ADR-0001). Four homes, no overlap:
|
||||||
|
|
||||||
|
- **Live work** → wayfinder maps + Gitea issues. Closeable, assignable,
|
||||||
|
queryable. Never a markdown file.
|
||||||
|
- **Settled decisions** → ADR files in `docs/adr/`. Immutable, findable, never
|
||||||
|
closed, never edited — only superseded by a later ADR pointing back. When an
|
||||||
|
issue ends in a durable decision, write the ADR, then close the issue
|
||||||
|
pointing at it. Decisions spanning repos go to `sow-platform/docs/adr/`.
|
||||||
|
- **Standing law** → `DOCTRINE.md`, `AGENTS.md`, `CONTEXT.md`. Rules that are
|
||||||
|
always true and vocabulary everyone shares — not the record of one decision.
|
||||||
|
- **Current-state reference** → docs describing what the code does now, kept
|
||||||
|
honest by review touching them.
|
||||||
|
|
||||||
|
Anything else — implementation plans, design specs, concepts, handoffs,
|
||||||
|
progress trackers, scratch — is **process**. It does not live in this repo. It
|
||||||
|
lives in a Gitea issue or a wayfinder map, where it can be assigned, closed,
|
||||||
|
and superseded. Small tasks need no written plan at all.
|
||||||
|
|
||||||
|
Deleting a process doc is not destroying history — `git log -- <path>` recovers
|
||||||
|
it. But *unfinished intent* (a design never built, an open question still
|
||||||
|
wanted) is live, not history: harvest it to a Gitea issue before deleting.
|
||||||
|
|
||||||
|
`sow-docs` is deprecated and read-only. Never add to it, never send work there.
|
||||||
|
|
||||||
|
## Which repo gets the issue
|
||||||
|
|
||||||
|
Issues follow ownership. File the issue in the repo that **owns the work** —
|
||||||
|
see the Repo/Owns/Produces table in the workspace root `AGENTS.md`. Standing
|
||||||
|
in one repo is not a reason to file there.
|
||||||
|
|
||||||
|
If work spans repos, file it in the repo that owns the *outcome* and reference
|
||||||
|
the others from it. A wrong-repo issue is a routing bug, not a filing
|
||||||
|
preference — move it.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- **Create an issue**: `tea issues create --title "..." --description "..."`
|
||||||
|
- **Read an issue**: `tea issues <number>` and
|
||||||
|
`tea api repos/ShadowsOverWestgate/sow-tools/issues/<number>/comments` for comments.
|
||||||
|
- **List issues**: `tea issues list --state open` (add `--labels ...` to filter).
|
||||||
|
- **Comment**: `tea comment <number> "..."`
|
||||||
|
- **Apply / remove labels**: `tea api --method PATCH` on the issue, or
|
||||||
|
`tea api repos/ShadowsOverWestgate/sow-tools/issues/<number>/labels` endpoints.
|
||||||
|
- **Close**: `tea issues close <number>`
|
||||||
|
|
||||||
|
`tea` infers the repo from the git remote when run inside the clone.
|
||||||
|
Gitea shares one number space across issues and PRs.
|
||||||
|
|
||||||
|
## Pull requests as a triage surface
|
||||||
|
|
||||||
|
**PRs as a request surface: no.**
|
||||||
|
|
||||||
|
## When a skill says "publish to the issue tracker"
|
||||||
|
|
||||||
|
Create a Gitea issue with `tea issues create`.
|
||||||
|
|
||||||
|
## When a skill says "fetch the relevant ticket"
|
||||||
|
|
||||||
|
Run `tea issues <number>` plus the comments API call above.
|
||||||
|
|
||||||
|
## Wayfinding operations
|
||||||
|
|
||||||
|
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
||||||
|
|
||||||
|
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `tea issues create --title "..." --description "..." --labels wayfinder:map`.
|
||||||
|
- **Child ticket**: this Gitea instance (v1.27) has no native sub-issue hierarchy, so a child issue carries `Part of #<map>` at the top of its description, and is also added to a task list in the map body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
||||||
|
- **Blocking**: Gitea's **native dependencies API** — the canonical, UI-visible representation (shows as "Depends on" / "Blocks" on the issue page). Add an edge with `tea api -X POST repos/ShadowsOverWestgate/sow-tools/issues/<child>/dependencies -f owner=ShadowsOverWestgate -f repo=sow-tools -F index=<blocker>`, where `<blocker>` is the blocker's issue **index** (its `#number` — Gitea's dependency API takes the index directly, unlike GitHub's numeric database id). Check status with `tea api repos/ShadowsOverWestgate/sow-tools/issues/<child>/dependencies` (GET) — a ticket is unblocked when every returned issue's `state` is `closed`.
|
||||||
|
- **Frontier query**: list the map's open children (`tea issues list --state open`, keep the ones whose description contains `Part of #<map>`), drop any with an open dependency (per the GET above) or an assignee; first in map order wins.
|
||||||
|
- **Claim**: `tea issues edit <n> --add-assignees <username>` — the session's first write.
|
||||||
|
- **Resolve**: `tea comment <n> "<answer>"`, then `tea issues close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Triage Labels
|
||||||
|
|
||||||
|
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker (Gitea — see `issue-tracker.md` for how to apply labels with `tea`).
|
||||||
|
|
||||||
|
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
||||||
|
| -------------------------- | -------------------- | ---------------------------------------- |
|
||||||
|
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
||||||
|
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
||||||
|
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
||||||
|
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
||||||
|
| `wontfix` | `wontfix` | Will not be actioned |
|
||||||
|
|
||||||
|
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
||||||
|
|
||||||
|
Edit the right-hand column to match whatever vocabulary you actually use.
|
||||||
Reference in New Issue
Block a user