# WikiTicket SDD
**Wiki ticket** — pronounced *"wicked ticket"*. WikiTicket Spec-Driven
Development.
## Why
Teams want spec-driven development that works across multiple teams and is
visible to everyone — **fishbowl** AI-assisted development. You can use AI
heavily, but the work is never hidden: every plan becomes tracked tickets,
the history of what was done is readily available, and roadmaps and status
reports are generated artifacts anyone can read. No "the agent did a bunch
of stuff last week and nobody knows what."
Two things set this apart from other git-native agent trackers:
1. **Plans are frozen.** Written once, never regenerated. A changed design
gets a new dated plan that supersedes the old one. The record of why an
approach was abandoned is the most valuable artifact.
2. **Documents carry the commit they were written against.** `git_hash` in
front matter names the tree the author had open; `worklog doc-verify`
resolves every `path — symbol(), lines N–M` citation *at that commit*,
telling a fabricated citation (wrong even then) apart from drift (right
then, the code moved). It never falls back to HEAD.
The JSONL event log is the mechanism, not the headline. An essay mapping
the machinery onto the mid-2026 "graph engineering" framing lives at
[`docs/graph-engineering.md`](docs/graph-engineering.md).
## Compared with beads, spec-kit, and GitHub Projects
The append-only JSONL-in-git log overlaps beads and backlog.md-style agent
trackers. That's table stakes. The difference is what sits on top of the log.
| | Frozen plans | Citation provenance | Tracker / wiki |
|---|---|---|---|
| **WikiTicket SDD** | Written once, superseded not edited | `git_hash` + `doc-verify` at that commit | Bidirectional, to the team's existing systems |
| **beads** | No | No | Local-first log; not a wiki/tracker publisher |
| **spec-kit / OpenSpec** | Living specs, updated in place | No | Spec artifacts, not tickets |
| **GitHub Projects** | No | No | The board *is* GitHub |
spec-kit and OpenSpec generate living `spec.md` / `plan.md` / `tasks.md`.
WikiTicket can ingest their task lists via `plan-capture`, then freeze the
why as a dated plan. GitHub Projects is a board on issues; WikiTicket is
local-first and optionally publishes *to* GitHub (or Jira, ADO, …).
A plan is the why of a piece of work; an ADR is the why of a standing
decision. Neither is a living spec.
## What it does
- **Plans are the permanent design record.** Exiting plan mode captures the
plan to `docs/plans/` and emits tracked items. Planning never evaporates;
when you need the *why*, read the plan. Plans are written once and
superseded, never edited.
- **Documents carry the commit they were written against**, and a gate reads
it back. `git_hash` in front matter says which tree the author had open;
`worklog doc-verify` resolves every `path — symbol(), lines N–M` citation
*at that commit*, telling a fabricated citation (wrong even then — a
defect) apart from drift (right then, the code moved since). It never falls
back to HEAD. `worklog provenance-backfill` adds `merged_in` once the
document lands. At release, `doc-verify --strict` also reports `STALE`
when a live design doc's `git_hash` predates the latest tag or the tag has
no freeze record in `docs/designs/`, so a release cannot ship with the
live pair left unregenerated.
- **Append-only, git-native work log.** Epics, stories, tasks, subtasks, and
bugs live in `.work/todo.jsonl` — an event log that multiple people (and
agents) can work against concurrently. Union merge, event fold: branches
merge cleanly, state is derived by folding events. Nightly compaction
snapshots closed items into `done.jsonl` and ages them into
`archive.jsonl` per level; nothing is deleted, and `show` and `list --all`
fold all three files.
- **A four-axis work taxonomy.** Every item sits on `level`
(epic/story/task/subtask), `kind` (feature/bug/ops/triage), `milestone`
(what ships together), and planned-vs-discovered. The unclassified default
is `triage` — visible in the roadmap's needs-classification queue, never a
silent guess.
- **A small fixed core, plus optional fields your team switches on.**
`estimate`, `owner`, `risk`, and `acceptance_criteria` ship enabled;
`value`, `confidence`, `due_date`, and `severity` are available and off. A
disabled field is invisible, not rejected — no flag is built for it, so it
never appears in `--help` or in anything an agent reads. `worklog fields`
prints the catalog and what each field means; `work_item_fields` in
`.work/config.yml` is the switch.
- **Generated roadmap and status reports.** `docs/roadmap.md` is rendered
from the log — no hand-editing, no drift.
- **Design docs and code walkthroughs**, generated from the actual code at
every release: a live `current_*` pair plus a short freeze note (tag +
git_hash + delta from the previous freeze). Existing full dated copies stay
frozen. Prose uses `document-specialist` v3.2.2 (STE100 default). Diagrams
default to `design-doc-mermaid` v1.1.1. `plantuml` v1.2.2 is leftover types
only and always PNG/SVG. Install the suite from
`SpillwaveSolutions/spillwave-documentation-marketplace` v0.2.1.
- **Information architecture & content model.** Stable `wiki_key` identity,
`truth_state` (current vs snapshot/superseded), a generated reader plane
(Home, Sidebar, Decisions/Releases/Status/Traceability indexes), and a
typed-edge graph (`worklog ia-index`, `ia-graph`, `trace-check`,
`ticket-body`, `link-pr`). Wiki publish follows
`docs/.index/publish-manifest.json`, which also carries a generated page
per ticket, PR, and release (`worklog ia-ticket <ULID>` to preview one).
- **Syncs to the team's OWN systems** — wiki *and* tickets. Your work log
publishes to whatever your team already uses. Create-vs-update consults
the remote: a clone with no link memory finds the item's ticket by its
marker and updates it instead of filing a second one, and
`worklog sync --explain <ULID>` says which source answered.
- **Living integration guides**, one per SDD tool and ticket/wiki system
(Superpowers, GSD, SpecKit, OpenSpec, Jira, Confluence, GitHub, GitLab,
Azure DevOps, AWS CodeCatalyst, Google Cloud DevOps), fetched from the
wiki at runtime with a local fallback under `docs/integrations/` — see the
`integration-guide` skill.
## System-agnostic edges, deliberately
Works equally with GitHub, GitLab, Azure DevOps (wiki + work items), Jira,
Linear, AWS CodeCatalyst, Confluence — and `other` is a first-class config
value for anything else. The team picks. (GCP ships no native tracker;
GCP-hosted teams pick from the list like everyone else.)
The core never contains per-system code. Publishing and sync are done by
*skills* that instruct the AI to use whatever CLI, MCP server, or skill is
available for the chosen system, researching missing tooling at runtime. The
`integration-guide` skill points at a living, wiki-hosted setup guide per
system (Jira, Confluence, GitHub, GitLab, Azure DevOps, AWS CodeCatalyst,
Google Cloud DevOps — plus Superpowers, GSD, SpecKit, and OpenSpec on the SDD
side), with a local fallback copy under `docs/integrations/` when the wiki
page is unreachable — knowledge that lives in docs, not hard-coded into the
skill set. You could even run two trackers at once (GitHub + Jira) — the
workflow stays identical.
## Quick start (no plugin)
Clone, then wire the git hooks:
```sh
git config core.hooksPath hooks
```
Track work with the CLI:
```sh
bin/wiki-ticket/worklog add "Extract auth middleware" --level task --kind feature \
--milestone v0.7.0 --priority P0
bin/wiki-ticket/worklog add "Fix flaky retry" --kind bug --unplanned --discovered-during <id>
bin/wiki-ticket/worklog update <id> --status in_progress
bin/wiki-ticket/worklog close <id>
bin/wiki-ticket/worklog list
bin/wiki-ticket/worklog fields # which item fields this repo has switched on
bin/wiki-ticket/worklog find <text> # search the generated inventory and graph
```
Capture a plan and regenerate the roadmap:
```sh
bin/wiki-ticket/worklog plan-capture --slug my-plan --title "My plan" --file draft.md
bin/wiki-ticket/worklog roadmap-render
```
The pre-commit and pre-merge-commit hooks enforce the invariants: trailing
newline on the log, event schema validation, no unresolved conflict markers in
any staged file (**no merge exemption** — a merge is exactly when a missed
hunk slips through), and `roadmap.md` freshness — plus branch discipline:
`pre-commit` rejects a commit authored directly on `main`/`master` (merges via
`git merge origin/main` are exempt), and `commit-msg` requires every commit
message to reference a worklog item (26-char ULID) or a ticket (`#123`), merge
commits exempt.
Nightly compaction runs in CI on `main`. When one lands mid-session and blocks
your merge — union merge resurrects events a compaction already folded, and
would silently drop the ones your branch wrote below the watermark —
`bin/wiki-ticket/worklog merge-rescue`, run from inside the blocked merge, keeps the
compacted log and replays your branch's own events above the watermark.
Recompacting is *not* the fix: it would verify green while making the loss
permanent.
## House rules, enforced
Policy that holds because tooling holds it, not because people remember:
- **PRs merge only when every gate is green.** `/worklog:merge` arms
`gh pr merge --auto --merge` so GitHub merges when required checks pass
(ADR-0011, which supersedes ADR-0010: bot PRs land through the same gate
under a PAT identity, never a bypass actor); the poll loop is fallback. Never `--admin`, never squash
(ADR-0008), never bypass. Teams that want a human on the trigger set
`features.auto_merge_on_green: false` in `.work/config.yml` (advisory
mode: report green, human merges).
- **Coverage floor.** CI enforces >=80% line coverage on `bin/*.py`; the
target is 95%.
- **Frozen artifacts.** Plans, roadmap snapshots, and published status
reports are written once and never regenerated — corrections go in new
documents. Frozen means the *prose*: metadata stamps in front matter are
still allowed, and since the publish manifest hashes the body, they no
longer look like edits.
- **The roadmap is generated.** Never hand-edited; to change it, change the
work items and re-render.
- **One session per working directory.** `worklog` warns when two assistant
sessions share a checkout — one switches branches under the other
mid-operation and both "fix" the same thing differently. The warning is
advisory and arrives after the fact; the fix is a `git worktree` per
session.
- **Provenance on every event, and on every generated document.** Each event
carries the short HEAD sha it was written at
(`WORKLOG_NO_GIT_PROVENANCE` opts out). Ids keep their full
entropy — provenance is a field, never spent out of the identifier. Plans,
status reports, ADRs and the roadmap carry a `git_hash` naming the tree
they were written against, which is what makes `worklog doc-verify`
possible. It depends on this repo using merge commits (ADR-0008); under
squash-merge the verifier reports `unresolvable` and refuses rather than
checking against the wrong tree.
## Multi-host
| Host | How it loads |
|------|----------------|
| Claude Code | Marketplace `worklog@worklog-marketplace` (`plugin/.claude-plugin`) |
| Grok Build | Claude-compatible, zero-config |
| Codex | `plugin/.codex-plugin` + `plugin/hooks/codex-hooks.json` |
| Agent Plugins 1.0 | Root `plugin.json` and `plugin/plugin.json` |
| Grok Bot | Skills + [docs/GROK_BOT.md](docs/GROK_BOT.md) |
| LangChain Deep Agents | `plugin/skills/` — [docs/LANG_CHAIN_DEEP_AGENTS.md](docs/LANG_CHAIN_DEEP_AGENTS.md) |
Two merge models: worklog is append-only ULID events; knowledge-tree writes use worktree + PR. See [docs/ISOLATION.md](docs/ISOLATION.md). Public examples use fictional **lumenfield-detector** and **northstar-console** only.
Also: [Onboarding](./docs/ONBOARDING.md).
## Claude Code, Codex, and Grok Build plugins
The plugin (in [plugin/](plugin/)) packages the skills, `/worklog:*`
commands (including `/worklog:merge`, the green-gates merge loop), and the
ExitPlanMode capture hook. Install from this repo's marketplace:
```sh
claude plugin marketplace add <this-repo-url-or-path>
claude plugin install worklog@worklog-marketplace
```
Codex uses the same marketplace and the native manifest at
`plugin/.codex-plugin/plugin.json`:
```sh
codex plugin marketplace add SpillwaveSolutions/wiki_ticket_sdd
codex
# Open /plugins, install worklog, then start a new session.
```
Codex discovers the bundled skills natively, and gets the enforcement hooks
too — the prompt reminder, the stop check, and the session doctor all run
there. The scripts are shared, not ported: Codex sets `CLAUDE_PLUGIN_ROOT`
for plugin-sourced hooks and reads the same `additionalContext` output, so
only the wrapper differs (`plugin/hooks/codex-hooks.json` nests the event map
under a `hooks` key).
Plan capture is the exception. That hook fires on `PostToolUse` matching the
`ExitPlanMode` tool, and Codex has the event but no such tool — so on Codex
plan capture is what `AGENTS.md` says it is, and you run
`bin/wiki-ticket/worklog plan-capture ...` before implementing. Claude Code and Grok Build
are unchanged.
Two install levels, deliberately distinct:
- **Plugin install** (above) — every session gets the skills, commands, and
hooks. Global; changes no repo. **Upgrade if you installed before 0.22.1**:
the hook manifest declared its events at the top level and the loader reads
them from under a `hooks` key, so every hook silently did nothing for
everyone who installed the plugin. Nothing failed, nothing warned. After
upgrading, hooks start firing that never did — the prompt reminder, session
doctor, stop check and plan-capture prompt are not new, they are what you
already installed, working for the first time.
- **Repo install** — inside a target repo, run `/worklog:init`. It scaffolds
`bin/`, the git hooks, an empty `.work/` with `config.yml`, and the CI
check (`.github/workflows/worklog.yml`, or `azure-pipelines.yml` when the
origin remote is Azure DevOps). The copies are committed, so hooks and CI
work for teammates who don't have the plugin.
Also: `/worklog:uninstall` removes the tooling but never touches `.work/`
data, `docs/plans/`, `docs/status/`, or `docs/roadmap.md` — the data
outlives the tooling. `/worklog:doctor` reports version drift and invariant
violations without changing anything. Ticket sync runs through a typed
adapter contract: `worklog adapter check` validates an adapter against the
contract before anything activates.
### Other harnesses (OpenCode and Grok Build)
The repo scaffold is harness-independent: `bin/wiki-ticket/worklog`, the git hooks, and
CI are committed and work from any shell. The `AGENTS.md` symlink (created
by `/worklog:init`, pointing at `CLAUDE.md`) carries the full worklog policy
to any harness that reads `AGENTS.md` — but init only *creates* those two
files, it does not commit them. Run `git ls-files CLAUDE.md AGENTS.md`; empty
output means a fresh clone gets no policy, and `git ls-files -s AGENTS.md`
should read mode `120000` so the symlink was stored as a symlink, not a copy.
Grok Build is fully compatible with
Claude Code with zero configuration — per the xAI docs it automatically reads
Claude Code marketplaces, plugins, skills, MCPs, agents, hooks, and
instruction files. See
[plugin/PORTS.md](plugin/PORTS.md) for the support matrix and porting guide.
## How it works
The full design — event schema, fold semantics, merge behavior, sync model —
is in [docs/worklog-spec.md](docs/worklog-spec.md). Task-oriented guides
(concepts, CLI reference, plugin guide) live in
[docs/user_guide/](docs/user_guide/).
## Layout
| Path | What |
|---|---|
| `.work/` | Append-only event log (`todo.jsonl`, `done.jsonl`, `archive.jsonl`), `config.yml`, publish ledger |
| `adapters/` | Ticket-sync adapters: shipped `fake` (CI double) and `github` (worked example), plus authoring rules |
| `schema/` | JSON Schemas: adapter contract, ADR, document IA (`doc.schema.json`), entity/item |
| `bin/` | `worklog` CLI plus its Python modules (fold, sync, roadmap, plan capture, IA, …) |
| `docs/plans/` | Captured plan documents (frontmatter links plans to items) |
| `docs/status/` | Generated status reports |
| `docs/roadmap.md` | Generated roadmap — do not edit |
| `docs/adr/` | Architecture Decision Records |
| `docs/designs/` | Current + frozen design docs and code walkthroughs |
| `docs/.index/` | IA inventory, graph, publish manifest, sidecars, rendered Home/Sidebar/indexes |
| `docs/user_guide/` | User guide, CLI reference, plugin guide |
| `docs/integrations/` | Living per-system setup guides (11 SDD tools + ticket/wiki systems), published to the wiki and used as the `integration-guide` skill's offline fallback |
| `hooks/` | `pre-commit`, `pre-merge-commit`, `commit-msg`, `exit-plan-capture.sh`, `prompt-reminder.sh`, `session-doctor.sh`, `session-end.sh`, `stop-worklog-check.sh` |
| `plugin/` | Claude Code, Codex, and Grok Build manifests, commands, skills, hooks, and canonical scripts — **v0.24.13** |
| `tests/` | Unit and integration tests |
## Testing
```sh
for t in tests/test_*.py; do python3 "$t"; done
```
Stdlib `unittest` only — no pytest, no dependencies. Unit suites cover the
fold, ULIDs, roadmap rendering, and plan capture; `tests/test_integration.py`
simulates full PR flows (branch, track, merge, fold) in throwaway git repos.