docs+test: le contrat avait dérivé — et le mécanisme ne voyait pas les règles
En vérifiant l'alignement de la surface, cinq sections du contrat s'étaient désynchronisées du code sans que rien ne rougisse : - § 1 montrait `getConfig`, `getStoreRegistryDeps`, `resetConfig` et `resetStoreRegistry` comme exportés — retirés à la fusion des portes ; - § 11 documentait `escapeLiteral` / `escapeIri` / `assertNuri` comme publiés — ils ne le sont plus, et l'absence de garde de type est désormais expliquée par sa raison : les portes valident elles-mêmes (`NuriLike`), publier une garde inviterait le cast que les types servent à empêcher ; - § 12 listait sept fonctions `storeRegistry` — il y en a cinq depuis que les deux fonctions d'ADRESSE d'inbox sont parties (une app nomme un document ou une personne, jamais une adresse) ; - § 13 listait `IdentityStore`, `browserIdentityStore` et `getCurrentUser` comme publiés — retirés le 2026-08-05 ; - `ensureIdentity` était publié **sans aucune règle**, et l'annexe renvoyait à un « § 2bis » qui n'existait pas. **§ 2bis est écrit** : le portail d'accès n'a aucune contrepartie en substance — en amont un utilisateur ouvre SON portefeuille et il n'y a rien à nommer — mais son SITE D'APPEL survit, et c'est pourquoi sa signature ne prend pas d'identifiant : nommer son identité est précisément la partie qui disparaît, donc elle ne doit pas figurer dans les paramètres. **Le mécanisme est étendu.** `test/vocabulary.test.ts` épinglait l'annexe — les NOMS — et ne voyait pas les sections, là où vivent les règles. Une règle périmée est pire qu'une règle absente : elle se lit comme vérifiée. Désormais tout `export` montré dans un bloc « ### Today » doit être réellement exporté ; ce qu'on garde pour mémoire passe en commentaire, que le contrôle ignore par construction. Les cinq dérives ci-dessus auraient été rouges le jour même. Nettoyé aussi : deux commentaires de doc orphelins dans `surface/placement.ts`, restés au-dessus de l'accolade fermante après le retrait des fonctions qu'ils décrivaient. 180 tests unitaires, typecheck bibliothèque / exemple / harnais.
This commit is contained in:
@@ -36,10 +36,9 @@ export {
|
||||
resolveScopeGraph,
|
||||
/** The NURI where GROUPED entities of `scope` are written (no per-entity document). */
|
||||
resolveWriteGraph,
|
||||
/** Open an inbox on a document you OWN, so others can deposit into it. */
|
||||
/** WHERE to deposit for a document — readable by any holder of it. `undefined` if none. */
|
||||
} from "../shared-wallet/account-registry";
|
||||
|
||||
/** Open an inbox on a document you OWN, so others can deposit into it. */
|
||||
export { openDocumentInbox } from "../emulated-verifier/branch-registers";
|
||||
|
||||
// No `linkTo` here, and its absence is deliberate (it existed 2026-08-06, one day).
|
||||
|
||||
@@ -172,6 +172,48 @@ test("a reserved-namespace key cannot be produced by a consumer's normalizeId",
|
||||
|
||||
// --- the contract's inventory must match the code ------------------------
|
||||
|
||||
/**
|
||||
* Every `export` the contract SHOWS in a "### Today" block must actually be exported.
|
||||
*
|
||||
* ── Why this exists beside the appendix check ─────────────────────────────
|
||||
* The appendix check pins the NAMES. It cannot see the rulings — the per-subject
|
||||
* sections where each symbol gets its epistemic label — and on 2026-08-07 three of them
|
||||
* had drifted without anything going red: § 11 documented `escapeLiteral` / `assertNuri`
|
||||
* as published (they are not), § 12 listed seven `storeRegistry` functions (there are
|
||||
* five), § 13 listed `IdentityStore` / `getCurrentUser` (removed two days earlier). A
|
||||
* reader trusting the sections was reading the surface of a fortnight ago.
|
||||
*
|
||||
* A stale ruling is worse than a missing one: it reads as verified. So the sections are
|
||||
* held to the same standard as the appendix — if a block shows `export function X`, X is
|
||||
* exported. Anything kept for the record goes in a comment, which this check ignores by
|
||||
* construction (it only looks at lines beginning with `export`).
|
||||
*/
|
||||
test("every `export` shown in a contract '### Today' block is actually exported", () => {
|
||||
const md = fs.readFileSync(
|
||||
path.join(import.meta.dir, "..", "..", "..", "docs", "api-contract.md"),
|
||||
"utf8",
|
||||
);
|
||||
const published = new Set(publishedNames());
|
||||
const stale: string[] = [];
|
||||
// Sections run from a "### Today" heading to the next heading of any level.
|
||||
for (const m of md.matchAll(/### Today[^\n]*\n([\s\S]*?)(?=\n#{2,3} )/g)) {
|
||||
for (const block of m[1]!.matchAll(/```ts\n([\s\S]*?)```/g)) {
|
||||
for (const line of block[1]!.split("\n")) {
|
||||
const decl = line.match(
|
||||
/^export (?:declare )?(?:async )?(?:const|function|class|interface|type) (\w+)/,
|
||||
);
|
||||
// Namespace members (`inbox.post`, `storeRegistry.createEntityDoc`) are exported
|
||||
// from their module, not from the entry — the same allowance the appendix makes.
|
||||
const name = decl?.[1];
|
||||
if (name && !published.has(name) && !isNamespaceMember(name)) stale.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
// A failure is a question, not a rename: has the symbol been removed (then say so in a
|
||||
// comment and rule on why), or has the entry lost something it should still publish?
|
||||
expect([...new Set(stale)]).toEqual([]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Read the appendix of `docs/api-contract.md` back into `{ group: names }`.
|
||||
* The appendix is a generated block; this parses the same shape.
|
||||
|
||||
Reference in New Issue
Block a user