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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ A **new hardened rule** is a minor version, not a major one — it adds a check,
- **`LICENSE` — Apache-2.0.** Chosen for the explicit patent grant and contribution terms; the README badge points at it.
- **Two documentation checks, on the standard tool contract.** `tools/linkcheck.mjs` (`D1`–`D3`) and `tools/mermaidcheck.mjs` (`D4`–`D7`) — same flags, same severity ladder, same `0` / `1` / `2` exit semantics as every other tool, zero dependencies. Documented in `VALIDATION_ENGINE.md § 13`, including the two false positives that shaped them.
- **`.github/workflows/checks.yml`.** Three jobs: documentation integrity, tool and config syntax, and a re-derivation of `examples/signin` by the three browser-free validators — with `git diff --exit-code` on the run's committed artifacts, because a validator that rewrites the run it is validating is not validating it. The browser-driven checks are deliberately **not** in CI, and the workflow says why.
- **`docs/rule-candidates.md` — the waiting room.** Observations that look like hardened rules and have not earned it. Principle 5 says every defect becomes a rule; it does not say *immediately*, and a rule is a claim about a **class** that one occurrence cannot establish. Promotion needs the five conditions in `CONTRIBUTING.md` **plus independent recurrence** — a different project, a different author, or a different state reached without knowledge of the first. Two instances one fix removes are one occurrence. Nothing in the register carries a rule code, is cited in a plan or gate record, is checked by a tool, or blocks anything. Seeded with `RC-1`.
- **`review.harnessFiles`.** The files in the prototype directory that are review chrome, not product surface. `audit.mjs`'s palette sweep and `annotate.mjs`'s network sweep now read one list, resolved once in `config.mjs` with `review.player` always included.
- **[`examples/signin/`](examples/signin/) — the toolkit's own reference run.** One feature, brief to frozen deliverable: all twelve states, all seven validators, three human gates, one revision cycle, zero waivers, machine closed at `DONE` with 7/7 completion rules. Includes the **failing** audit (`audit-signin-01`, 138/138 checks passed and the screenshots failed it on three `major` defects) preserved alongside the passing one.

Expand Down Expand Up @@ -96,7 +97,7 @@ Carried from `DOCS_AUDIT.md`, with the recommendation for each:
- ~~**TK-2** — `audit.paletteExemptSelectors` is dead config.~~ **Resolved** — removed, replaced by `review.harnessFiles`.
- ~~**TK-3** — `tools/cdp.mjs` ignores `--root`.~~ **Resolved** — and `smoke.mjs`, which had the same defect.
- ~~**The contact addresses.**~~ **Resolved** — GitHub advisories for security, a maintainer DM for conduct. **One repository setting is still required:** private vulnerability reporting must be enabled under Settings → Security after the repository is pushed. Until it is, `SECURITY.md` names a channel that is not switched on.
- **Still open — the rule candidate.** *Do not write the name of the thing you are claiming not to use, inside the file being swept for it.* The reference prototype's comment recited the request-API names, and `annotate` E11 blocked on the disclaimer. Candidate for `skills/07`; not yet written, because one occurrence is an anecdote and a hardened rule needs a class.
- ~~**The rule candidate.**~~ **Recorded as `RC-1`** in `docs/rule-candidates.md`, deliberately **not** hardened. One occurrence is an anecdote; the register is what makes a second one recognisable as a second one. It is promoted only if the same class recurs independently, and it is cited, checked and enforced by nothing in the meantime.
- **Note on the reference run.** `examples/signin/` records TK-2 and TK-3 as open, because they were open when that run closed. It is a dated record of a completed run, not a live document, and it is deliberately not being edited to match. The audit report says what the audit found.

---
Expand Down
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ A rule is accepted when all five hold:
4. **It is evidenced by something checkable.** A rule is satisfied by the evidence named in a validation rule, never by understanding it.
5. **It gets a code** in the owning state's namespace — `B`, `M`, `G`, `R`, `P`, `W`, `E`, `F` — continuing that namespace's numbering.

### Seen it once? It goes in the register, not the catalogue

Condition 1 asks for a defect. It also, implicitly, asks for a **class** — and one occurrence cannot establish one. If you have seen this exactly once, open a rule report anyway, and expect the outcome to be a row in [`docs/rule-candidates.md`](docs/rule-candidates.md) rather than a code.

That is not a rejection. It is the record that makes the *second* occurrence recognisable as a second occurrence instead of another first. A candidate is promoted when the same class appears again independently — a different project, a different author, or a different state reached without knowledge of the first.

Two instances that one fix removes are one occurrence.

### Where it goes — three places, one edit

| Place | What goes there |
Expand Down
10 changes: 5 additions & 5 deletions DOCS_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ A pre-launch review of this repository's documentation, run the way the toolkit

| Metric | Value |
|---|---|
| Markdown files | 86 |
| Internal links checked | 923 |
| Markdown files | 87 |
| Internal links checked | 943 |
| Broken links | **0** (4 fixed during this audit) |
| Mermaid blocks | 39 |
| Mermaid blocks with parse-risk warnings | **0** |
Expand All @@ -35,7 +35,7 @@ Applying the toolkit's own **M3** — *a failing probe is a hypothesis, not a fi

```bash
# every relative link and every in-document anchor, resolved against the filesystem
node tools/linkcheck.mjs # → 923/923 resolve
node tools/linkcheck.mjs # → 943/943 resolve
# every fenced mermaid block: known diagram type, balanced quotes, closed fence
node tools/mermaidcheck.mjs # → 39 blocks, 0 findings
# every GitHub issue form: parses, required keys present, no duplicate field ids
Expand Down Expand Up @@ -162,7 +162,7 @@ It also did what a reference run is *for*: **it found real defects in the toolki
| **TK-1** | `tools/audit.mjs` swept `play.html` — the review player — as product surface, reporting 11 off-palette hexes on every run. `tools/annotate.mjs` in the same toolkit already excluded exactly those three harness files. | **fixed** in `audit.mjs` |
| **TK-2** | `audit.paletteExemptSelectors` is documented in `toolkit.config.json`, `tools/config.mjs`, `VALIDATION_ENGINE.md` and `skills/08` as the mechanism that exempts harness chrome — and is **referenced by no tool**. The sweep is file-level, so a selector list could not exempt anything even if it were read. | **fixed** — key removed, replaced by `review.harnessFiles`, which both sweeps read |
| **TK-3** | `tools/cdp.mjs` calls `loadConfig()` with no root, so it resolves from `cwd` rather than honouring `--root`. Viewport and Chrome path therefore come from the wrong config when a tool is run with `--root` from a different directory. Worked here only because both configs agreed. | **fixed** in `cdp.mjs` — and in `smoke.mjs`, which had the same defect and no `--root` at all |
| **New rule candidate** | *Do not write the name of the thing you are claiming not to use, inside the file being swept for it.* The prototype's comment recited the request-API names; `annotate` E11 blocked on the disclaimer. | candidate for `skills/07` |
| **New rule candidate** | *Do not write the name of the thing you are claiming not to use, inside the file being swept for it.* The prototype's comment recited the request-API names; `annotate` E11 blocked on the disclaimer. | **recorded as `RC-1`**, [not hardened](docs/rule-candidates.md) — one occurrence is an anecdote, and a rule is a claim about a class |

That is the argument for the run in one line: **the documentation described a toolkit nobody had executed, and executing it found three tool defects and a rule.**

Expand Down Expand Up @@ -282,7 +282,7 @@ See [recommendation 5](#5--add-a-ci-workflow).
| 1 | [`templates/ui-plan.md`](templates/ui-plan.md) linked to `../08-self-audit/SKILL.md`, which resolves to `templates/08-self-audit/` and does not exist. | Corrected to `../skills/08-self-audit/SKILL.md`. |
| 2 | [`README.md`](README.md) FAQ linked to `reference/screen-registry.csv`, which does not exist until a user copies it in. | Repointed to [`reference/README.md`](reference/README.md), which explains the file and where to seed it from. |
| 3 | [`VALIDATION_ENGINE.md`](VALIDATION_ENGINE.md) linked to `reference/state-vocabulary.md`, same problem. | Repointed to [`templates/state-vocabulary.md`](templates/state-vocabulary.md). |
| 4 | Three anchor links used a double hyphen where the target heading produces a single one. | Corrected. All 923 links now resolve, and `tools/linkcheck.mjs` keeps them resolving. |
| 4 | Three anchor links used a double hyphen where the target heading produces a single one. | Corrected. All 943 links now resolve, and `tools/linkcheck.mjs` keeps them resolving. |

Findings 1–3 are the same class: **a link to a file that only exists after the user seeds it.** The class was swept, not just the instances — `linkcheck.mjs` resolves every relative target against the filesystem, so any further occurrence would have been reported.

Expand Down
2 changes: 2 additions & 0 deletions GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ Every project-hardened rule carries a code, citable from a plan, a log or a gate
| **W1–W10** | Navigation mapping | [12 flow-visualization](skills/12-flow-visualization/SKILL.md) |
| **E1–E7** | Enterprise extensions | [12 flow-visualization](skills/12-flow-visualization/SKILL.md) |

**`RC-n` is not a rule code.** It indexes [`docs/rule-candidates.md`](docs/rule-candidates.md) — observations seen once, waiting for an independent recurrence. A candidate has no owning state, no validation rule and no enforcement; it acquires a code from one of the namespaces above only on promotion.

### Tool finding codes

| Prefix | Tool | Meaning |
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Three documents that define what the toolkit **is**, as opposed to how to use it
| [`workflow.md`](workflow.md) | The state machine: twelve states, transitions, guards, gates, retry logic, loop logic, completion rules, skill decomposition. | **Source of truth.** Where a skill and this document disagree, the document wins and the skill is the bug. |
| [`method-rules.md`](method-rules.md) | Every project-hardened rule, indexed by the code you cite it as, with the class of failure that produced it. | The catalogue. Each skill carries the full statement of its own set; this file is the index across all of them. |
| [`artifact-contracts.md`](artifact-contracts.md) | The artifact store: what exists, who owns it, the naming scheme, the load-bearing frontmatter fields, gate records, validation shape. | The data contract every skill writes to. |
| [`rule-candidates.md`](rule-candidates.md) | Observations seen exactly once, waiting for an independent recurrence before they can be hardened. | **Not enforced.** Nothing here is cited, checked, or allowed to block. It exists so a second occurrence is recognisable as a second one. |

## Inputs

Expand Down Expand Up @@ -46,6 +47,7 @@ Return edge passes through SELF_AUDIT per R6.
| What does `C_NAVMAP_CLEAN` mean? | [`workflow.md` §4](workflow.md) |
| What is the ceiling on `L_REVISION`, and what happens on breach? | [`workflow.md` §7](workflow.md) |
| Why does the audit look at screenshots? | [`method-rules.md`](method-rules.md) → **M2** |
| I found something once — is it a rule? | [`rule-candidates.md`](rule-candidates.md) → the bar for promotion |
| Why must `reads_versions` be in frontmatter? | [`artifact-contracts.md`](artifact-contracts.md) |
| Which fields does a gate record have to carry? | [`artifact-contracts.md`](artifact-contracts.md) → Gate records |

Expand Down
5 changes: 5 additions & 0 deletions docs/method-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ one product. The right is the skill that owns it.
A rule is not satisfied by understanding it. It is satisfied by the evidence named
in the skill's validation rules.

**What is not in this file.** An observation seen exactly once is not in here. It
waits in [`rule-candidates.md`](rule-candidates.md) until the same class recurs
independently, because a rule is a claim about a class and one occurrence is an
anecdote. Nothing in the register is cited, checked or enforced.

---

## The three that shape everything else
Expand Down
88 changes: 88 additions & 0 deletions docs/rule-candidates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Rule candidates — the waiting room

[← docs/](README.md) · [Repository root](../README.md) · [Method rules →](method-rules.md) · [Contributing →](../CONTRIBUTING.md#contributing-a-hardened-rule)

---

## What this file is

Observations that **look** like hardened rules and have not earned it yet.

[Principle 5](../DESIGN_PRINCIPLES.md#5--every-defect-becomes-a-rule) says every defect becomes a rule. It does not say every defect becomes a rule *immediately*, and the difference is the whole reason this file exists. A rule is a claim about a **class** of failure. One occurrence is an anecdote about one run — it may be the first sighting of a real class, or it may be a local quirk that never recurs. There is no way to tell from inside the run that produced it.

So a single observation goes here, and it waits.

## Why a waiting room rather than a decision

Both ways of resolving a single observation immediately are wrong, and they fail in opposite directions.

| If you harden it on sight | If you discard it |
|---|---|
| The catalogue fills with rules written by one incident each. Every one costs a reader's attention on every run, and the ones that matter get read at the same weight as the ones that never recur. A rule nobody can afford to read is not enforced. | The second occurrence arrives months later with nothing recorded to match it against, and it reads as a first occurrence too. The class stays invisible for as long as nobody remembers the first one. |

The register is how a first sighting survives without being promoted. Recording is cheap; enforcing is not.

## The bar for promotion

A candidate becomes a hardened rule when **all five conditions in [CONTRIBUTING](../CONTRIBUTING.md#the-bar) hold**, plus one that applies only to candidates:

> **6 · It has recurred independently.** The same class of failure has appeared at least twice, in circumstances that did not share a cause with the first.

**Independently** means one of these — not merely a second instance:

- a **different project**, or
- a **different author**, or
- a **different state** of the same project, reached without knowledge of the first occurrence.

Two instances in one sweep of one file are **one** occurrence. So are two instances that a single fix removes. The question the sixth condition asks is not *how many times did I see this*, it is *would this have happened to someone who had never seen the first one*.

## What a candidate is not

Stated explicitly, because the failure mode here is a candidate quietly acquiring the authority of a rule.

- **It has no rule code**, and never gets one until promotion. `RC-n` is a register index, not a namespace in `B` / `F` / `M` / `G` / `R` / `P` / `W` / `E`.
- **It is not cited** in a plan, a revision log, an audit report or a gate record. Citing a candidate is how it becomes a de facto rule without passing the bar.
- **No tool checks it**, and no validation rule references it.
- **It does not block anything.** A candidate never produces a finding, a waiver or a debt item.

A candidate is a note to a future maintainer. That is its entire authority.

## Register

| # | Observation | First seen | Recurrences | Status |
|---|---|---|---|---|
| **RC-1** | Do not write the name of the thing you are claiming not to use, inside the file being swept for it. | `examples/signin`, STATE 12 | **0** | monitoring |

---

### RC-1 · A disclaimer inside a swept file is indistinguishable from the thing it disclaims

**Observation.** A source comment that states *"this prototype makes no network calls"* and then names the APIs it is not using — `fetch`, `XMLHttpRequest`, `WebSocket` — is matched by the sweep that looks for those names. The claim and its counter-evidence are the same bytes.

**First seen.** [`examples/signin`](../examples/signin/README.md), at STATE 12. `tools/annotate.mjs` raised a blocking `E11` for a network call site at `signin.html:262`. Confirmed at source: line 262 was the comment asserting there were no network calls. The sweep is deliberately broad — see [`annotate.mjs`](../tools/annotate.mjs) `NETWORK_RE` — and narrowing it to dodge comments would weaken a check that exists precisely because a claim about network behaviour must be re-derived rather than trusted.

**Why it might be a class.** The general shape is *a file that describes its own contents, inside the scope being scanned for those contents*. Nothing about it is specific to network APIs. The same shape would apply to a comment listing banned colours in a file swept for hex values, or naming a deprecated component in a file swept for that component.

**Why it is not a rule yet.** One occurrence, in one file, written by one author, removed by one edit. Every part of it could be local. Manufacturing a second instance to justify the rule would be the thing this method exists to prevent — a rule needs a defect that happened, not a defect that was arranged.

**What would promote it.** A second occurrence in a different project, or by a different author, where a descriptive comment trips a source sweep and the first occurrence played no part in it. On promotion the likely owner is [`skills/07-prototype`](../skills/07-prototype/SKILL.md) — the state that writes the file — with the statement generalised past network APIs.

**Workaround in the meantime.** The claim belongs in the traceability record, which is the artifact that exists to carry claims about the build, and which nothing sweeps for API names.

---

## Reviewing this file

Review the register whenever a run closes at `FINAL_OUTPUT`. Three outcomes:

| Outcome | Action |
|---|---|
| The class recurred independently | Promote it. Follow [Contributing a hardened rule](../CONTRIBUTING.md#contributing-a-hardened-rule) — three places, one edit — and replace the row here with a link to the code it became. |
| It did not recur, and the observation still looks plausible | Leave it. A candidate does not expire on a schedule; a class that recurs once every two years is still a class. |
| It did not recur, and the mechanism turned out to be wrong | **Retire it**, with the reason written into the row. A retired candidate stays in the file — deleting it loses the record that the question was asked and answered. |

Recording a candidate that never recurs costs one table row. Missing the second occurrence of a real class costs the run it happens in.

---

[← docs/](README.md) · [Method rules →](method-rules.md) · [Design principles →](../DESIGN_PRINCIPLES.md) · [Contributing →](../CONTRIBUTING.md)
Loading