Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 213 additions & 0 deletions .agents/skills/plainweave-workflow/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
---
name: plainweave-workflow
description: >
This skill should be used when the user asks "why does this code exist",
"what's our intent coverage", "find orphan code / requirements / goals",
"trace this requirement up to a goal or down to code", "bind this SEI /
entity to a requirement", "draft / approve / supersede a requirement", "show
the requirement dossier", "what's unverified or stale", "baseline the
requirements", or when working in a project that uses Plainweave for code-up
requirements traceability and intent. Provides the read/author/verify
workflow, the doctrine invariants (advisory-only, no-silent-clean,
enrich-only, zero minted SEIs), and the cross-member peer-facts surfaces.
---

# Plainweave Workflow

Plainweave is the Weft federation's **requirements and verification authority** —
"the permission-for-code-to-exist member." It maintains a **code-up intent graph**
(`Loomweave SEI → requirement → goal`) in a local `.plainweave/` store and answers
one question for every public surface: *"why does this exist?"* It is **advisory**:
it surfaces facts and lets agents decide; it never emits an allow/block verdict.

Prefer the MCP tools (`mcp__plainweave__*`) when available; fall back to the
`plainweave` CLI. Every read CLI takes `--json` to emit a versioned envelope.

## The model — three altitudes, one graph

```
goal strategic intent ("ship trustworthy federation seams")
↑ links
requirement a reviewable statement ("the producer must never silent-clean")
↑ binds
code (SEI) a Loomweave-identified public entity (function/class/module)
```

A node with **no upward edge** is an **orphan** — a reviewable question, not an
error. Code that skips its bind is exactly what surfaces. Requirements are
trivially mintable; consolidation ("these three are the same") is **agent-driven**
off the corpus, never an automated verdict.

## Core read workflow — the four intent primitives

```bash
plainweave intent coverage # north-star: fraction of public surfaces that answer "why?"
plainweave intent orphans code # unjustified nodes at an altitude: code | requirement | goal
plainweave intent trace code <node_id> # justification neighborhood up to goals / down to code
plainweave intent corpus # readable dump of requirements + their code/goal links
```

- **`coverage`** is the self-computed north-star. It is **honestly qualified
in-band**: `denominator_complete`, `present_plugins`, namespace scoping, bounded
evidence. Scope the denominator with `--exclude-namespace PREFIX` (default excludes
`scripts.`, `tests.`) and `--surface-class {cli-command,entry-point,exported-api,http-route}`.
It **never reports a silent clean** when the denominator is partial.
- **`orphans <altitude>`** lists nodes with no upward justification edge. Triage,
don't panic — an orphan is "should this be bound, or is it genuinely standalone?"
- **`trace <altitude> <node_id>`** walks both directions from a node.
- **`corpus`** is the artifact a curator reads to spot duplicates before consolidating.

## Authoring workflow — draft → criterion → approve → bind → ladder

Requirements are versioned with a draft/approve lifecycle:

```bash
plainweave req add --title "…" --statement "…" [--actor human:<you>] # create a DRAFT
plainweave criterion add … # acceptance criteria on the active draft
plainweave req approve # promote the active draft
plainweave req supersede … | req deprecate … | req reject # later lifecycle moves
```

Bind code and ladder to strategy:

```bash
plainweave bind sei <entity_id> <requirement_id> [--entity-kind …] [--content-hash …]
plainweave goal add "…" # strategic intent node
plainweave goal link <goal_id> <requirement_id>
```

Trace links (graph edges) have their own propose/review lifecycle — a proposed
link is a *suggestion*, accepted is *fact*, rejected never reads as coverage:

```bash
plainweave trace propose --from-kind … --from-id … --relation … --to-kind … --to-id … [--confidence N]
plainweave trace accept <link_id> | trace reject <link_id>
plainweave trace list
```

> **Rejected ≠ absent coverage.** A reviewed-and-rejected binding does **not**
> read as requirement coverage; the view drops `rejected` trace links before
> computing `present`/`absent`.

## Verification — methods, evidence, status

```bash
plainweave verify method … # define how a requirement is verified
plainweave verify evidence … # record evidence against a method
plainweave status requirement <id> # verification status for one requirement
plainweave status unverified # requirements with no verification
plainweave status stale # requirements whose evidence has gone stale
plainweave dossier <requirement_id> # the full per-requirement dossier (advisory boundary)
```

Lock and compare requirement sets over time:

```bash
plainweave baseline create … # lock the approved-requirement set
plainweave baseline diff <id> # drift of current approved vs a baseline
plainweave baseline list | baseline show <id>
```

## Doctrine — the hard invariants (do not violate)

Plainweave's behaviour is contract-validated against these; violating them is
rejected by the shared validators, not merely discouraged.

- **Advisory, never gates.** Zero release allow/block/approved/verdict tokens.
Coverage facts ride out at the git/CI boundary *through Legis* — Plainweave adds
no enforcement of its own. Any teeth are dialled up by the consumer via Legis cells.
- **No silent-clean.** A degraded or language-partial denominator, an unavailable
adapter, or an unresolved identity is **flagged in-band** — never collapsed to a
clean-empty result. `unavailable` ("I can't tell") is distinct from `absent`
("definitively none") and from a clean `present`.
- **Enrich-only.** Plainweave absent → Loomweave, Legis, and the code are
unaffected; solo mode degrades to manual file/symbol refs.
- **Zero minted SEIs.** Sibling identity (`loomweave:eid:…`) and sibling fact
bodies are consumed **opaquely** — surfaced, never parsed or minted.
- **Local-only.** Reads compute against the local store / re-scanned scope; no live
peer calls in the producers (`local_only: true`, `live_peer_calls: false`).

## Cross-member peer facts (advisory producers)

Two local-first producers expose Plainweave facts to siblings, each with a frozen
`.v1` contract, explicit degraded state, and no silent-clean:

```bash
plainweave wardline-peer-facts [--limit N] [--offset N] [--json] # weft.plainweave.wardline_peer_facts.v1
plainweave requirements-enrichment <entity_ref>... [--json] # weft.plainweave.requirements_enrichment.v1
```

- **`wardline-peer-facts`** surfaces Wardline findings (active / waived / baselined
/ judged) computed against the actually re-scanned scope. Reads `.wardline/*-findings.jsonl`
only; an absent `.wardline/` reports `freshness: unavailable`, **never clean**.
- **`requirements-enrichment`** is the Plainweave-owned producer for **Warpline's**
reserved `enrichment.requirements` slot. `entity_ref` is a SEI or dotted locator
(batch many in one call). Per-entity `status`: `present` (≥1 alive binding) /
`absent` (entity known, none bound) / `unavailable` (identity unresolved or store
error — "I can't tell," **never** "no requirements"). This is consumed live by the
Warpline `consult_federation` requirements member — see `references/cross-member-seams.md`.

## Health and diagnostics

```bash
plainweave init # create a .plainweave/ store
plainweave doctor # federation-parity health: store/schema, Loomweave catalog binding, MCP surface
plainweave doctor --fix # idempotent in-place store repairs, then re-check
plainweave doctor --root <dir> # inspect a root other than cwd (remediation is root-aware)
```

`doctor` exits non-zero on unresolved problems; `--fix` is safe and idempotent.

## Response shapes & exit codes

Read surfaces emit a versioned envelope under `--json` (and the MCP tools return the
same shape):

- **Success** → `{schema: "weft.plainweave.<contract>.v1", ok: true, …, authority_boundary: {local_only: true, live_peer_calls: false, governance_verdicts: false}}`.
- **Failure** → `{ok: false, error: {code, …}}`. `code` ∈ `VALIDATION`, `NOT_FOUND`,
`CONFLICT`, `POLICY_REQUIRED`, `PEER_ABSENT`, `PEER_STALE`, `PEER_CONTRACT`,
`LOCKED`, `UNSUPPORTED`, `INTERNAL`. Branch on `code`, not message text.
- **Exit codes** (surface commands): `ok:true → 0`; `INTERNAL → 4`; any other
failure (e.g. `NOT_FOUND`, uninitialised store) → `2`. An uninitialised store is a
*genuine* producer failure (exit 2 / `ok:false`), never a faked clean — that would
break no-silent-clean. NB: a piped exit capture (`… | head; echo $?`) reports the
pipe tail's status, not plainweave's.

## MCP parity (read-only mirror)

The MCP server (`plainweave-mcp`) mirrors the reads — `mutates: false`,
`local_only: true`, no peer side effects: `plainweave_intent_coverage` /
`_orphans` / `_trace` / `_corpus`, `plainweave_requirement_get` / `_search` /
`_dossier`, `plainweave_verification_status_get` / `_list`, `plainweave_baseline_*`,
`plainweave_trace_link_list`, `plainweave_entity_intent_context_get`,
`plainweave_loomweave_catalog_list`, `plainweave_preflight_facts_get`,
`plainweave_project_context_get`, `plainweave_wardline_peer_facts_list`,
`plainweave_requirements_enrichment_get`.

## Reference sheets

Load these when you hit a specific challenge, not upfront:

- **`references/intent-graph-patterns.md`** — coverage-denominator scoping,
orphan triage at each altitude, corpus-driven consolidation, baselines & drift,
the verification lifecycle.
- **`references/cross-member-seams.md`** — the peer-facts producers and their
freshness/degraded vocab, the Loomweave catalog/SEI seam, the Legis boundary,
and the opacity rules for sibling identity.

## Quick decision guide

| Situation | Action |
|-----------|--------|
| "Why does this code exist?" | `plainweave intent trace code <sei>` |
| "What's our intent coverage?" | `plainweave intent coverage` (read the in-band qualifiers) |
| "What code is unjustified?" | `plainweave intent orphans code` |
| "These requirements look duplicated" | `plainweave intent corpus`, consolidate by hand |
| "Record a new requirement" | `req add` → `criterion add` → `req approve` |
| "Link this entity to a requirement" | `plainweave bind sei <entity_id> <requirement_id>` |
| "Is this requirement verified?" | `plainweave status requirement <id>` / `dossier <id>` |
| "What's gone stale?" | `plainweave status stale` |
| "Did anything drift from the baseline?" | `plainweave baseline diff <id>` |
| "What requirements bind this entity (for Warpline)?" | `plainweave requirements-enrichment <ref> --json` |
| "Is the store healthy?" | `plainweave doctor` (`--fix` to repair) |
| "Plainweave says `unavailable`" | It can't tell (identity/store) — **not** "none." Resolve identity or `init`. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Cross-member seams

How Plainweave consumes from and produces facts for its Weft siblings. The unifying
rule: **enrich-only, opaque identity, no silent-clean, no verdicts.**

## Loomweave — identity & catalog (Plainweave consumes)

Plainweave never mints code identity. It consumes Loomweave **SEIs**
(`loomweave:eid:<32hex>`) opaquely:

- `plainweave catalog record` registers a public entity discovered by a sibling
catalog tool. The SEI is stored verbatim; Plainweave does not parse or synthesise it.
- `plainweave bind sei <entity_id> <requirement_id>` ties a requirement to that
identity. `--content-hash` captures the entity's hash at attach time so a consumer
can later detect drift.
- The Loomweave catalog adapter has a **frozen degraded-state contract**
(`weft.plainweave.loomweave_catalog.v1`): when the adapter is `unavailable` it
returns an explicit unavailable envelope routed through the same oracle as live
output — it never returns a clean-empty page that would read as "no entities."

If Loomweave is absent, Plainweave degrades to manual file/symbol references; the
graph still works, identity is just less precise.

## Legis — the git/CI boundary (Plainweave's facts ride out)

Plainweave emits **no enforcement of its own**. Coverage facts cross the git/CI
boundary *through Legis* ("this change adds N public entities bound to no
requirement"). Advisory by default; a repo that wants teeth dials it up through
Legis's policy cells. The preflight advisory cell
(`plainweave_preflight_facts_get`) supplies the facts; Legis decides whether they gate.

## Warpline — requirements enrichment (Plainweave produces, Warpline consumes live)

`plainweave requirements-enrichment <entity_ref>... --json` is the Plainweave-owned
producer (`weft.plainweave.requirements_enrichment.v1`) for Warpline's reserved
`enrichment.requirements` slot. Warpline's `consult_federation` wires it as the
**4th federation member** (alongside filigree/wardline/legis), capability-gated on
the verb being advertised.

Per-entity `status` semantics — **mapped Plainweave-side; consumers pass through, never re-interpret**:

| status | meaning |
|---|---|
| `present` | ≥1 alive requirement bound (the `requirements` array is non-empty) |
| `absent` | entity known, none bound — a definitive "none here" |
| `unavailable` | could not determine (identity unresolved / store error) — **"I can't tell," never "no requirements"** |

The `unavailable` ≠ `absent` distinction is load-bearing and fault-injection-tested:
collapsing `unavailable` to `absent` (or to a clean-empty) would violate no-silent-clean
and let a "can't tell" masquerade as "verified none." Requirement item bodies and SEIs
are **opaque** to the consumer — surfaced, never parsed.

Operational note: a consumer that execs bare `plainweave` from `PATH` runs the
installed uv-tool snapshot, which can lag the dev tree. If the verb isn't advertised,
the consumer reads the member `disabled` even though the code is correct — keep the
installed tool current (`uv tool install --force` / an editable install).

## Wardline — findings as peer facts (Plainweave produces)

`plainweave wardline-peer-facts --json` (`weft.plainweave.wardline_peer_facts.v1`)
surfaces Wardline findings (active / waived / baselined / judged; defect /
non-defect) plus resolved-or-unseen, computed against the **actually re-scanned
scope** — scan-identity manifest primary, with a path-set heuristic fallback that
flags itself in-band. An absent `.wardline/` reports `freshness: unavailable`, never
a clean empty page.

## The freshness / status vocabulary

Across the producers, degraded state is **explicit and distinct** — these are not
interchangeable, and none of them is a silent clean:

- `present` — facts found and returned.
- `absent` — definitively none (the authority looked and there are none).
- `unavailable` — could not determine (peer/store/identity gap). The honest "I can't tell."
- `stale` — facts exist but the thing they describe has moved on.

Surface the exact one; never round `unavailable`/`stale` down to `absent` or up to a clean result.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Intent-graph patterns

Deep-dive patterns for working the `SEI → requirement → goal` graph. Load when you
hit one of these specific situations.

## Reading coverage honestly

`plainweave intent coverage` reports the fraction of public surfaces that answer
"why does this exist?" — but the **number is meaningless without its qualifiers**.
Always read these from the envelope before quoting a percentage:

- **`denominator_complete`** — `false` means the surface inventory is partial
(e.g. a language plugin is absent). A high percentage over an incomplete
denominator is not a clean bill of health; say so.
- **`present_plugins`** — which language/surface extractors actually ran. Coverage
is only as complete as this list.
- **Namespace scope** — the denominator excludes `scripts.` and `tests.` by
default. Add `--exclude-namespace PREFIX` (repeatable) to scope out generated or
vendored namespaces; never silently widen it to flatter the number.
- **Surface class** — `--surface-class {cli-command,entry-point,exported-api,http-route}`
restricts the denominator. Use it to answer "how covered is the HTTP surface
specifically?" rather than the blended figure.
- **`--max-surfaces N`** caps the evidence lists, **not** the counts — counts are
never truncated, so a capped list still reports the true total.

The honest move when coverage looks high: state the figure *and* the denominator
qualifiers in the same breath ("92% of exported-api surfaces, but
`denominator_complete: false` — the Rust plugin didn't run").

## Triaging orphans at each altitude

`plainweave intent orphans {code,requirement,goal}` returns nodes with no upward
edge. The triage question differs by altitude:

- **code orphans** — a public entity bound to no requirement. Ask: should this be
bound (then `bind sei`), or is it genuinely infrastructural/standalone? Don't
reflexively mint a shell requirement just to clear the orphan — that manufactures
vanity coverage.
- **requirement orphans** — an approved requirement laddered to no goal. Either
link it (`goal link`) or accept it as a leaf with a note. A pile of goal-less
requirements is a signal the goal layer is under-modelled.
- **goal orphans** — a goal with no requirements beneath it. Usually means intent
was declared but never decomposed into reviewable requirements.

## Corpus-driven consolidation

`plainweave intent corpus` is the curator's artifact. Consolidation is **agent-
driven, never automated**: read the corpus, spot "these three say the same thing,"
then supersede the duplicates into one canonical requirement (`req supersede`) and
re-bind. Plainweave serves the substrate; it does not auto-merge. The optional
Loomweave semantic-similarity hint *assists* this read — it is explicitly not a
dedup engine and never acts on its own.

## Requirement lifecycle

Requirements are versioned with an explicit draft/approve flow:

1. `req add` creates a **draft** (the "active draft" for the project).
2. `criterion add` attaches acceptance criteria to the active draft.
3. `req approve` promotes it. Approved requirements are what baselines lock.
4. `req supersede` creates a new version that replaces an approved one;
`req deprecate` retires one; `req reject` discards an unwanted draft.

Trace links carry their own review state: `propose → accept | reject`. A **proposed**
link is a suggestion; only **accepted** links count as justification; **rejected**
links are dropped before coverage is computed (a rejected binding never reads as
`present`).

## Baselines & drift

`plainweave baseline create` locks the current approved-requirement set under a
name. Later, `plainweave baseline diff <id>` shows how the live approved set has
drifted (added / removed / superseded). Use a baseline at a release boundary or a
review milestone, then diff against it to answer "what requirements moved since we
agreed this set?" Baselines are immutable once created — `show`/`list` read them.

## Verification, not validation

`verify method` declares *how* a requirement is checked; `verify evidence` records a
concrete result against a method; `status` reports the rollup
(`requirement` / `unverified` / `stale`). Evidence goes **stale** when the thing it
attested to has moved on — `status stale` surfaces exactly those, so re-verification
is targeted rather than wholesale. The `dossier` is the full advisory picture for one
requirement (statement, criteria, bindings, verification) — it reports, it does not
gate.
Loading
Loading