refactor!: drop the wiki and page layers
The plugin is issues and nothing else now. `skills/page` (the page-tree domain) and `skills/wiki` (its bridge to a Gitea wiki) are gone, and with them the issue domain's `wiki:` field — page titles were the only thing that tied the two domains together, and a field the tracker has no column for never came back from a pull anyway. What is left is the shape AGENTS.md already claimed for the rest of the repo: one domain, one bridge, one transport. The docs, the plugin manifest, and tea-runner's skill table now say so too, and test_payload_root walks the one script directory that remains. Also removes openspec/config.yaml; nothing in the repo referenced it. 378 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,18 +15,15 @@
|
||||
|
||||
## Layers
|
||||
|
||||
The hard rule of this repo. Two domains, two bridges, one transport, and
|
||||
The hard rule of this repo. One domain, one bridge, one transport, and
|
||||
knowledge flows one way only:
|
||||
|
||||
```
|
||||
skills/issue DOMAIN what an issue is: format, validation, dependency graph
|
||||
skills/page DOMAIN what a page tree is: title <-> path, order, the index
|
||||
▲ offline — no tracker, no network, stdlib imports only
|
||||
│ imports
|
||||
skills/sync BRIDGE map.py md <-> Gitea issue JSON, pure, no I/O
|
||||
_gitea.py tea api, pagination, filters, payloads
|
||||
skills/wiki BRIDGE wikimap.py md <-> Gitea wiki JSON, pure, no I/O
|
||||
transport is _gitea.py — there is no second one
|
||||
│ imports
|
||||
▼
|
||||
skills/auth IDENTITY pin the login the whole tracker side runs under
|
||||
@@ -40,19 +37,16 @@ skills/use REFERENCE tea CLI docs for everything that is not an issue
|
||||
agents/ EXECUTION tea-runner: runs the scripts, reports a receipt
|
||||
```
|
||||
|
||||
A domain never imports its bridge, and the two domains do not import each
|
||||
other: delete `skills/sync` and issues still work, delete `skills/wiki` and page
|
||||
trees still work, delete either domain and the other is untouched. The check is
|
||||
mechanical — every import under a domain's `scripts/` is stdlib, and
|
||||
`subprocess` is not among them:
|
||||
The domain never imports its bridge: delete `skills/sync` and issues still
|
||||
work. The check is mechanical — every import under the domain's `scripts/` is
|
||||
stdlib, and `subprocess` is not among them:
|
||||
|
||||
```bash
|
||||
grep -rh '^import \|^from ' skills/issue/scripts/ | sort -u
|
||||
grep -rh '^import \|^from ' skills/page/scripts/ | sort -u
|
||||
```
|
||||
|
||||
If a tracker concept (issue number, login, HTTP call, label color, `sub_url`,
|
||||
`content_base64`) shows up in a domain layer, it is in the wrong place.
|
||||
If a tracker concept (issue number, login, HTTP call, label color) shows up in
|
||||
the domain layer, it is in the wrong place.
|
||||
|
||||
## Repo layout
|
||||
|
||||
@@ -81,20 +75,6 @@ If a tracker concept (issue number, login, HTTP call, label color, `sub_url`,
|
||||
the domain's `issue_evict.run`
|
||||
- `scripts/labels.py` — put the canonical `type/*` and `severity/*` set into a
|
||||
repository; reads the domain taxonomy, never the store
|
||||
- `skills/page` — a discussion's artifacts as a page tree (`/tea:page`),
|
||||
entirely offline
|
||||
- `references/pages.md` — canonical page-tree format; single source of truth
|
||||
- `scripts/page.py` — domain module: title ↔ path, ordering, the manifest,
|
||||
importing a directory of markdown, the index
|
||||
- `scripts/page_import.py` — copy a directory of markdown into a space,
|
||||
titling every file
|
||||
- `scripts/page_index.py` — write the table-of-contents page
|
||||
- `scripts/page_ls.py` — the tree, the titles, one sync-state tag per page
|
||||
- `skills/wiki` — move page trees between a local space and a Gitea wiki
|
||||
(`/tea:wiki`)
|
||||
- `scripts/wikimap.py` — md ↔ Gitea wiki JSON, pure, no I/O
|
||||
- `scripts/wiki_ls.py`, `wiki_pull.py`, `wiki_push.py` — transport is
|
||||
`skills/sync/scripts/_gitea.py`
|
||||
- `skills/use` — `tea` CLI reference for everything that is not an issue
|
||||
(`/tea:use`); `references/tea/` holds the command docs
|
||||
- `agents/tea-runner.md` — subagent on Haiku that executes the scripts and
|
||||
@@ -125,7 +105,7 @@ working tree of any linked worktree** met on the way, reached by reading
|
||||
`gitdir:` out of a `.git` *file* and following `commondir`.
|
||||
|
||||
**The pin is not resolved from `__file__`, and that asymmetry with
|
||||
`issue.store_root`/`page.store_root`/`_gitea.PAYLOAD_ROOT` is deliberate.**
|
||||
`issue.store_root`/`_gitea.PAYLOAD_ROOT` is deliberate.**
|
||||
Where an installation keeps its files is a fact about the installation; whose
|
||||
login a project runs under is a fact about the project. A plugin installed
|
||||
outside any repository and pointed at somebody else's tree must not answer the
|
||||
@@ -149,11 +129,10 @@ stdlib-only and the tests hold the same line. `skills/*/scripts/` are not
|
||||
packages, so a test that needs the domain module imports it with
|
||||
`sys.path.insert`.
|
||||
|
||||
**A test never touches `tmp/issues/`, `tmp/wiki/` or `tmp/payload/`.** Anything
|
||||
that needs a store builds a throwaway repository in a
|
||||
`tempfile.TemporaryDirectory()` — a `.git` marker, a copy of the script layers,
|
||||
fixture issues or artifacts — and runs the real scripts inside it as
|
||||
subprocesses. That is the only way to test behavior that depends on where a
|
||||
**A test never touches `tmp/issues/` or `tmp/payload/`.** Anything that needs a
|
||||
store builds a throwaway repository in a `tempfile.TemporaryDirectory()` — a
|
||||
`.git` marker, a copy of the script layers, fixture issues — and runs the real
|
||||
scripts inside it as subprocesses. That is the only way to test behavior that depends on where a
|
||||
script is run from, and it keeps the developer's own store out of the blast
|
||||
radius.
|
||||
|
||||
@@ -232,42 +211,14 @@ line so plain grep works without a parser.
|
||||
- No drift tracking, and now nothing to track: there is no second copy to
|
||||
diverge from. `synced:` tells you how old your working copy is.
|
||||
|
||||
## Local wiki cache
|
||||
|
||||
`tmp/wiki/<space>/` (gitignored) holds page trees — a discussion's artifacts,
|
||||
organized. Same stance as the issue store, resolved the same way from
|
||||
`page.py`'s own location, with the same `--out` rule.
|
||||
|
||||
- Identity is the **title**, and `/` inside it is the only hierarchy there is.
|
||||
The Gitea wiki is flat: it escapes a title into one filename by rules of its
|
||||
own (`space -> -`, `/ -> %2F`, a literal `-` forces a trailing `.-`).
|
||||
- **`sub_url` is Gitea's address for a page and is never constructed.** It is
|
||||
read back from the API and stored in `.pages.json`. One built by hand that is
|
||||
almost right creates a second page instead of editing the first.
|
||||
- **Never commit a subdirectory into a wiki's git repository.** Gitea does not
|
||||
see it — the page exists on disk and nowhere in the API or the UI. Do not
|
||||
clone the wiki repo to work in; use the scripts.
|
||||
- A title is a decision, not a derivation. A re-import replaces bodies and
|
||||
keeps titles, so editing a heading cannot silently rename a published page.
|
||||
`--retitle` opts in, and the rename reaches the wiki on the next push.
|
||||
- A page with no `sub_url` has never been published — a complete state, the way
|
||||
`origin: local` is for an issue. **The parallel stops at the push**: a pushed
|
||||
page stays on disk, a pushed issue does not.
|
||||
- Change detection is one hash (`pushed`). Pulling overwrites; pushing is
|
||||
additive and never deletes — the one place the two domains deliberately
|
||||
disagree, because a page tree is worked on locally and an issue is not.
|
||||
- The `tea` CLI has no wiki subcommand. `tea api` is the only route, through
|
||||
`_gitea.py`.
|
||||
|
||||
## Request payloads
|
||||
|
||||
`tmp/payload/` (gitignored) holds the JSON bodies `tea api -d @file` was given,
|
||||
one file per named request, kept after the call for a retry or a post-mortem.
|
||||
It is **not a store and holds nobody's only copy** — deleting it costs nothing.
|
||||
|
||||
- One directory for every caller — sync and wiki both — resolved from
|
||||
`_gitea.py`'s own location, so which command wrote a body does not change
|
||||
where it landed. `_gitea.api` takes no directory argument; that it once did
|
||||
- One directory for every caller, resolved from `_gitea.py`'s own location, so
|
||||
which command wrote a body does not change where it landed. `_gitea.api` takes no directory argument; that it once did
|
||||
is exactly how a label bootstrap came to create `tmp/issues/`.
|
||||
- It is created lazily, by the first write of a run, and only then: a `--dry-run`
|
||||
or a run with nothing to send leaves no directory behind.
|
||||
|
||||
Reference in New Issue
Block a user