| name | pqforge-docs |
| description | Maintain pqforge documentation in lockstep with the code, with zero hallucinated APIs, commands, numbers, or links. Use whenever editing README.md, CHANGELOG.md, anything under doc/ or wiki/, the visibility manifest (tool/visibility/visibility_manifest.json), the generator, or any generated AI-discovery file (llms.txt, llms-full.txt, identity.json, faq-ai.txt, developer-ai.txt, ai.txt, robots*.txt, site/**, .github/copilot-instructions.md, .github/instructions/**, .cursor/rules/**, .windsurfrules). Enforces the verify-against-code protocol, the generated-vs-hand-maintained split, the claim boundary, the main-branch link rule, and the regenerate-and-validate checklist. |
pqforge Documentation Skill
This skill owns documentation quality for pqforge. The single rule it exists to
enforce: every documented fact is verified against the code in this repository
before it is written. No fact is carried over from memory, training data, or a
previous version without re-checking it against the current source.
pqforge is the application/composition layer over
pqcrypto (FIPS 203 ML-KEM, FIPS 204
ML-DSA). Documentation must keep that relationship and the claim boundary intact.
0. Before you write anything
Read the ground truth, in this order:
pubspec.yaml — current version, description, dependencies, SDK.
CHANGELOG.md — the latest released claims (the "as-built" feature list).
AGENTS.md and CLAUDE.md — workflow, hard constraints, and layout.
- The actual source for whatever you are about to document (see §2).
Never document a feature from the changelog without confirming the symbol still
exists in lib/ or bin/.
1. The two documentation classes
Edit them differently. Mixing this up breaks CI.
A. Hand-maintained (edit directly)
README.md, CHANGELOG.md
- everything under
doc/
- everything under
wiki/
AGENTS.md, CLAUDE.md, and repository skills
B. Generated (NEVER hand-edit)
Produced by tool/visibility/generate_visibility.dart from
tool/visibility/visibility_manifest.json:
- root:
llms.txt, llms-full.txt, identity.json, developer-ai.txt,
faq-ai.txt, ai.txt, robots-ai.txt, robots.txt
- editor/agent rules:
.github/copilot-instructions.md,
.github/instructions/pqforge-crypto.instructions.md, .cursor/rules/pqforge.mdc,
.windsurfrules
- the entire
site/ tree except the committed binary site/assets/
To change any file in class B: edit the manifest (or the generator), then
regenerate. Each generated file carries a "Generated by … Edit … manifest
instead." banner; if you are about to edit a file with that banner, stop and go
to the manifest. The mapping from manifest field to output lives in
_generateOutputs in the generator.
2. No-hallucination verification protocol
Before a name, command, flag, number, or path enters any doc, prove it from
source. Use these checks (they are fast and they are the contract).
API / facade / recipe method names
grep -rn '\bMETHOD_NAME\b' lib/
The recipe/facade methods live in lib/src/services/pqforge_service.dart; public
exports are in lib/pqforge.dart and lib/pqforge_io.dart. If a symbol is not
exported there, it is not public API — do not document it as such.
CLI commands and flags
grep -rnoE "name => '[a-z-]+'" bin/src/
grep -rn "'FLAG_NAME'" bin/
Commands are registered in bin/pqforge.dart; shared encrypt flags are in
bin/src/support.dart. Derive command counts from source instead of hard-coding
them. Confirm a flag's behavior by reading its help: text and the command's
run() — do not infer behavior from the name.
Constants and thresholds (e.g. the 8 MiB streaming cutoff)
grep -rn 'streamingThreshold' lib/ bin/
Profiles, cipher suites, engines
Read the enums directly:
lib/src/cipher/pq_cipher_suite.dart (suites + engine provider),
lib/src/algorithms/pq_algorithms.dart (profiles, KEM/sig levels).
Performance numbers
Cite only numbers that appear in
doc/technical/PERFORMANCE_AUDIT_AND_HYBRID_CLI.md (they are measured on a stated
host). When you quote one, keep the qualifier ("pure Dart", "on this host",
"projection"). Do not round away the context. Current as-built anchors:
ChaCha20-Poly1305 30.4 MiB/s vs AES-256-GCM 11.5 MiB/s pure Dart (~2.6×);
cryptography engine ~10× PointyCastle; OpenSSL ceiling ~1.0–1.1 GB/s.
Links
Every relative link must resolve. Run the link sweep in §6.
3. The claim boundary (hard limits on wording)
These come from doc/security/CLAIM_BOUNDARY.md and the manifest's
evidence_boundary.forbidden_claims. Never write, in any doc or generated
file:
- "FIPS validated", "FIPS 140 validated", "CMVP validated", "certified"
- "hard constant-time" Dart guarantee
- "hard memory-erasure" guarantee
- "ML-KEM alone is secure transport"
- "AES signs documents"
- "RC4 is supported"
Allowed framing: "FIPS 203/204-aligned … through pqcrypto",
"application-layer composition helpers", "best-effort zeroization". The
post-quantum security claim is inherited from pqcrypto — say so rather than
implying pqforge validated anything itself.
Also avoid time-bound or provenance phrasing in code/docs (no "we decided", owner
names, bug-ticket narratives, "as of last week"). State architecture as fact plus
rationale. Docs are timeless engineering.
4. Repository link rule (the broken-link trap)
Generated files build GitHub links as
{repository}/blob/{repository_branch}/{path}. The branch comes only from
project.repository_branch in the manifest. Keep it main. A feature-branch
value here is exactly how published llms.txt/site/ links rot when the branch
is deleted.
Verify after any regen:
grep -rn 'blob/' llms.txt llms-full.txt site/index.html | grep -v 'blob/main' \
&& echo 'FAIL: non-main repo links' || echo 'OK: all repo links use main'
Hand-maintained docs link relatively within doc/ — for example, from one
doc/ file the CLI guide is reached as the path CLI.md — so they survive any
branch. Wiki pages use GitHub-wiki slugs: a file named Foo-Bar.md is reached by
the slug Foo-Bar (spaces become hyphens). Cross-repo wiki-to-doc links use
absolute …/blob/main/… URLs.
5. Publishing pipeline (where each file goes)
| Source | Published by | To |
|---|
wiki/** | .github/workflows/sync-wiki.yml (push to main/develop) | the GitHub Wiki |
site/** + generated roots | .github/workflows/pages.yml | GitHub Pages |
| manifest → generated files | visibility.yml + ci.yml run --check | CI gate (fails if stale) |
So: a wiki change ships only when merged to main/develop; a generated-file
change must be regenerated and committed or CI fails.
6. The change checklist (run every time)
- Verify every new name/command/flag/number/path against §2.
- Edit the right class (§1): manifest for generated files, file directly for
hand-maintained.
- If the manifest or generator changed, regenerate:
dart run tool/visibility/generate_visibility.dart
- Sweep links across repository-maintained Markdown:
dart run tool/agent/check_links.dart
- Confirm no stray branch links (§4).
- Validate:
dart run tool/agent/verify.dart docs
Run dart run tool/agent/verify.dart full when examples, public APIs, CLI
behavior, or code-adjacent docs changed.
- Cross-check consistency — the same fact often appears in README, the
manifest (→ llms/site/faq), doc/CLI.md, doc/API.md, and the wiki. When you
change a capability, grep for the old wording and update every surface:
grep -rn 'OLD_PHRASE' README.md doc/ wiki/ tool/visibility/visibility_manifest.json
7. Manifest field guide (what feeds what)
tool/visibility/visibility_manifest.json drives all generated files:
project.* — titles, URLs, repository_branch (keep main), last_updated
(set to today, absolute date). The version is not here — it is read from
pubspec.yaml (the single source of truth) by the generator, so it can never
drift. The CLI version is likewise generated into bin/src/version.g.dart by
tool/version/generate_version.dart (verified by its own --check).
pqcrypto_relationship.* — the differentiation surfaced in llms.txt,
llms-full.txt, the site footer/boundary, and identity.json isBasedOn.
capabilities, does_not_provide, evidence_boundary — boundary text reused
across llms*, ai.txt, copilot rules, and the site.
profiles, recipes — drive the site signal counts (rendered dynamically),
recipe cards, llms*, developer-ai.txt, and the crypto-instructions file.
docs — the doc cards/links (rendered as /blob/{branch}/{url}); every url
must be a real file in the repo.
faq — faq-ai.txt and the site FAQ. Expand here to add Q&A; keep answers
literally true and within the claim boundary.
keywords — identity.json and SEO.
After editing the manifest, bump last_updated and regenerate. If you add a new
manifest field, also teach the generator to read it (and keep --check
green).
8. Done means
- New facts verified against
lib//bin/ (§2).
- Generated files regenerated;
--check is green.
dart format and dart analyze clean.
- Link sweep clean; no non-
main repo links.
- The claim boundary held;
pqcrypto relationship intact.
- The same fact is consistent across README, manifest, doc/, and wiki.