feat(engine): per-repo heading alias closes the Non-goals/nb-repo gap
The Non-goals contract is one literal English heading, matched case-insensitively but never translated — a repo whose readers were declared `nb` in `locales` could only go green by planting an English heading inside an otherwise-Norwegian document. Measured on ki-produktivitetsmodell (order, census 09): `## Virkeområde og forbehold` already does the job Non-goals exists for, HEADING-MISSING fired anyway. `heading_aliases` in the register is the same shape `titles` already is for the README H1 — decision in the repo, bookkeeping here — keyed per repo so two nb-repos need not share a Norwegian phrasing. Satisfying a requirement through it is its own OK (HEADING-ALIAS), never folded silently into the aggregate. Also books two operator title decisions verified against the live clones (not the coord messages that reported them): .profile's H1 became "From AI to Chitta — open" (org landing page rebuild, commit 9898a6e), and ki-produktivitetsmodell's H1 "Tre nivå av organisatorisk produktivitet med KI" is deliberate, not drift. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gh6T6iHHkMvgeBbGaLmLQD
This commit is contained in:
parent
e061c1d219
commit
45a2bf30de
5 changed files with 118 additions and 6 deletions
15
CLAUDE.md
15
CLAUDE.md
|
|
@ -225,6 +225,21 @@ would recreate, in data, exactly the drift this plugin exists to remove.
|
|||
stands. What the gate must never do is make "we decided this" and "nobody
|
||||
looked" the same output. The wanted side effect is exposure, not silence —
|
||||
`ai-psychosis` is deliberately unregistered so it stands alone.
|
||||
- **A fixed-language contract has a blind spot the `titles` pattern already
|
||||
knows how to close.** `## Non-goals` is one literal English string, matched
|
||||
case-insensitively but not translated, so it cannot be satisfied by a repo
|
||||
whose readers were declared `nb` in `locales` — the only way to go green was
|
||||
planting an English heading inside an otherwise-Norwegian document. Measured
|
||||
on `ki-produktivitetsmodell` (order, 2026-08-18, census 09 on that repo):
|
||||
`nb` locale, `## Virkeområde og forbehold` already doing the job Non-goals
|
||||
exists for (a stranger sees what the repo does not try to be), `HEADING-
|
||||
MISSING` `ERROR` regardless. `heading_aliases` in the register is the same
|
||||
shape `titles` already is for the H1 — the decision is taken in the repo, the
|
||||
bookkeeping happens here — and satisfying a requirement through it is its own
|
||||
`OK` (`HEADING-ALIAS`), never folded silently into the aggregate. Keyed PER
|
||||
REPO, not per locale: a locale-wide translation table would force every
|
||||
nb-repo into the same Norwegian phrasing for the same section, exactly the
|
||||
constraint `titles` already rejects for H1s one requirement over.
|
||||
- **An exemption is a finding, not a deletion.** `readme_desc_match: false`
|
||||
turns off README-DESC equality for a class, and the check still emits an `OK`
|
||||
naming why. An exception nobody can see reads exactly like a check that
|
||||
|
|
|
|||
22
README.md
22
README.md
|
|
@ -78,7 +78,7 @@ The repository's **class** decides what each check means:
|
|||
| Install block | the form for this class is missing, incomplete, shown over `ssh://`, or points at the wrong marketplace |
|
||||
| Install truth | the plugin is not pinned in the catalog, so the documented command cannot succeed for anyone |
|
||||
| Install pins | a `@v…` or `--branch v…` in an install command names a tag the forge does not have, so the command a stranger copies 404s |
|
||||
| Required headings | `## Install`, `## Non-goals`, `## Changelog` — per class. Present at the wrong depth is its own finding |
|
||||
| Required headings | `## Install`, `## Non-goals`, `## Changelog` — per class. Present at the wrong depth is its own finding. A registered per-repo heading alias (`register/repos.json`'s `heading_aliases`) satisfies it too, for a repo whose readers were declared to speak another language |
|
||||
| Required files | a file this class (or trait) needs is absent |
|
||||
| Repo references | an `open/<name>` in URL position resolves to nothing |
|
||||
| Relative links | a link points at a file that is not tracked |
|
||||
|
|
@ -189,6 +189,26 @@ An H1 that matches neither the repo name nor a registered title is still a
|
|||
warning, and the message names both — that is drift in one of the two, not a
|
||||
title.
|
||||
|
||||
### Registered heading aliases — the same asymmetry, one requirement over
|
||||
|
||||
`## Non-goals` (and every other required heading) is one fixed English string,
|
||||
matched case-insensitively but never translated. A repository whose readers
|
||||
were declared `nb` in the register's `locales` cannot satisfy it honestly —
|
||||
the only way to go green was planting an English heading inside an otherwise
|
||||
Norwegian document, which is worse than the warning it silences.
|
||||
|
||||
A `heading_aliases` entry in the register is the fix, shaped exactly like
|
||||
`titles`: the decision — this section, in this repo's own language, does the
|
||||
job the English heading names — is made in the repo, and the bookkeeping
|
||||
happens here. Set one, and a README carrying the aliased heading instead of
|
||||
the literal one is an `OK` (`HEADING-ALIAS`) that names the alias, never a
|
||||
silent pass. Leave it out, and `HEADING-MISSING` stands exactly as before.
|
||||
|
||||
It is keyed **per repository**, not per locale. A locale-wide translation
|
||||
table would force every `nb` repository into the same Norwegian phrasing for
|
||||
the same section — precisely the constraint `titles` already rejects for H1s,
|
||||
where a human title is a valid choice, not a slot filled from a fixed list.
|
||||
|
||||
### When equality is the wrong demand
|
||||
|
||||
`README-DESC` requires the README's opening line to be the forge description
|
||||
|
|
|
|||
|
|
@ -135,14 +135,27 @@
|
|||
"",
|
||||
"`ai-psychosis` (`# Interaction Awareness`) is DELIBERATELY ABSENT: it is the",
|
||||
"one where a reader cannot connect the title to the repo. Leaving it standing",
|
||||
"alone is the wanted effect of registering the others."
|
||||
"alone is the wanted effect of registering the others.",
|
||||
"",
|
||||
"`.profile` UPDATED 2026-08-18 (org-ops coord, census 09): the org landing",
|
||||
"page was rebuilt the same day (commit 9898a6e, 'rebuild org landing page,",
|
||||
"add LICENSE and SECURITY.md', per that repo's own STATE.md — order from",
|
||||
"`.claude`, AAA+ round 5). The H1 became `# From AI to Chitta — open`; the",
|
||||
"old value here was the H1 it replaced, not drift. Verified against the",
|
||||
"clone, not the coord message — the em-dash is U+2014, not a hyphen.",
|
||||
"",
|
||||
"`ki-produktivitetsmodell` ADDED 2026-08-18 (order from that repo, on its own",
|
||||
"operator decision recorded in its STATE.md the same day): `# Tre nivå av",
|
||||
"organisatorisk produktivitet med KI` is the document's real title, the repo",
|
||||
"name is a slug. Verified against the clone before writing it down."
|
||||
],
|
||||
"titles": {
|
||||
".profile": "fromaitochitta / open",
|
||||
".profile": "From AI to Chitta — open",
|
||||
"llm-ingestion-pipeline-security": "llm-ingestion-guard",
|
||||
"linkedin-studio": "LinkedIn Studio Plugin for Claude Code",
|
||||
"llm-security": "LLM Security Plugin for Claude Code",
|
||||
"ms-ai-architect": "AI Architect Plugin for Claude Code"
|
||||
"ms-ai-architect": "AI Architect Plugin for Claude Code",
|
||||
"ki-produktivitetsmodell": "Tre nivå av organisatorisk produktivitet med KI"
|
||||
},
|
||||
|
||||
"$comment_traits": [
|
||||
|
|
@ -201,6 +214,33 @@
|
|||
"ki-produktivitetsmodell": "nb"
|
||||
},
|
||||
|
||||
"$comment_heading_aliases": [
|
||||
"The Non-goals contract is one fixed English string, matched literally — it",
|
||||
"cannot be satisfied by a repo whose readers were declared `nb` in `locales`",
|
||||
"above, and the only way to go green was to plant an English heading inside",
|
||||
"an otherwise-Norwegian document. Same job `titles` does for a README H1, one",
|
||||
"requirement over: the decision is taken in the repo, the bookkeeping happens",
|
||||
"here — a registered alias satisfies the requirement and is its own OK",
|
||||
"(`HEADING-ALIAS`), never a silent pass.",
|
||||
"",
|
||||
"Keyed PER REPO, not per locale: a locale-wide translation table would force",
|
||||
"every nb-repo into the same Norwegian phrasing for the same section, which",
|
||||
"is exactly the constraint `titles` already rejects for H1s (a human title is",
|
||||
"a valid choice, not a slot to fill from a fixed list).",
|
||||
"",
|
||||
"ADDED 2026-08-18 (order from ki-produktivitetsmodell, census 09 on that",
|
||||
"repo): its README is `nb` (see `locales` above) and carries",
|
||||
"`## Virkeområde og forbehold`, which does the job the Non-goals contract",
|
||||
"exists for — a stranger sees what the repo does not try to be. The operator",
|
||||
"decided the README stays as written; this entry is the mechanism, not an",
|
||||
"exception for one repo. Verified against the clone before writing it down."
|
||||
],
|
||||
"heading_aliases": {
|
||||
"ki-produktivitetsmodell": {
|
||||
"## Non-goals": "## Virkeområde og forbehold"
|
||||
}
|
||||
},
|
||||
|
||||
"$comment_tags_lightweight_accepted": [
|
||||
"Where a decided YES about lightweight tag HISTORY gets written down — the",
|
||||
"same job `titles` does for a README H1, one axis over. A lightweight tag is",
|
||||
|
|
|
|||
|
|
@ -575,16 +575,32 @@ export function checkRequiredFiles({ present, klass, traits }, register) {
|
|||
// on a predictable heading is what agents pattern-match on, and `## Non-goals`
|
||||
// is the cheapest trust-builder there is: it proves someone thought about the
|
||||
// boundary, and it stops misuse before it starts.
|
||||
export function checkHeadings({ readme, klass, traits }, register) {
|
||||
export function checkHeadings({ readme, klass, traits, name }, register) {
|
||||
const { headings: required } = requirementsFor(klass, traits, register);
|
||||
const text = String(readme ?? '');
|
||||
const present = new Set(
|
||||
text.split('\n').map((l) => l.trim()).filter((l) => l.startsWith('#')),
|
||||
);
|
||||
const aliases = register.heading_aliases?.[name] ?? {};
|
||||
const findings = [];
|
||||
for (const h of required) {
|
||||
if ([...present].some((p) => p.toLowerCase() === h.toLowerCase())) continue;
|
||||
|
||||
// Same job `titles` does for a README H1, one requirement over: the
|
||||
// decision (this repo's readers were declared `nb`, so the contract's
|
||||
// English wording is the wrong test) is taken in the repo, the bookkeeping
|
||||
// happens here. Keyed per repo, not per locale — two nb-repos need not
|
||||
// phrase the same section the same way.
|
||||
const alias = aliases[h];
|
||||
if (alias && [...present].some((p) => p.toLowerCase() === alias.toLowerCase())) {
|
||||
findings.push({
|
||||
level: 'OK',
|
||||
code: 'HEADING-ALIAS',
|
||||
msg: `\`${alias}\` satisfies \`${h}\` — the registered heading alias for \`${name}\``,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same title, wrong depth: say that, rather than "missing". The contract
|
||||
// wants a predictable top-level heading because that is what an agent
|
||||
// pattern-matches on — but the section does exist, and the fix is a
|
||||
|
|
@ -1556,7 +1572,7 @@ export function classifyRepo(
|
|||
...checkInstallBlock({ readme, name, klass }, register),
|
||||
...checkInstallTruth({ name, klass, catalogNames }),
|
||||
...checkInstallPins({ readme, forgeTagsByRepo }, register),
|
||||
...checkHeadings({ readme, klass, traits }, register),
|
||||
...checkHeadings({ readme, klass, traits, name }, register),
|
||||
...checkRequiredFiles({ present, klass, traits }, register),
|
||||
...checkLinks({ files }, register),
|
||||
...checkInternalLinks({ files, present }),
|
||||
|
|
|
|||
|
|
@ -770,6 +770,27 @@ test('org-profile requires no headings at all', () => {
|
|||
assert.equal(f.filter((x) => x.level === 'ERROR').length, 0);
|
||||
});
|
||||
|
||||
// The Non-goals contract is written in English and matched literally, which
|
||||
// cannot be satisfied by a repo whose readers were declared `nb` — the fix is
|
||||
// per-repo, not per-locale, because two nb-repos need not phrase the same
|
||||
// section the same way. Same shape as `titles`: the decision is taken in the
|
||||
// repo, the bookkeeping happens in the register.
|
||||
test('a registered heading alias satisfies a required heading for a different-language reader', () => {
|
||||
const aliasRegister = {
|
||||
...REGISTER,
|
||||
heading_aliases: { 'ki-produktivitetsmodell': { '## Non-goals': '## Virkeområde og forbehold' } },
|
||||
};
|
||||
const readme = '# x\n## Virkeområde og forbehold\n';
|
||||
const f = checkHeadings({ readme, klass: 'shared-asset', name: 'ki-produktivitetsmodell' }, aliasRegister);
|
||||
assert.equal(f.some((x) => x.level === 'ERROR'), false);
|
||||
assert.equal(f.some((x) => x.code === 'HEADING-ALIAS'), true);
|
||||
|
||||
// The alias is keyed per-repo: a different repo with the same Norwegian
|
||||
// heading still misses the literal `## Non-goals`.
|
||||
const other = checkHeadings({ readme, klass: 'shared-asset', name: 'some-other-repo' }, aliasRegister);
|
||||
assert.equal(other.some((x) => x.code === 'HEADING-MISSING'), true);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------- file: URL links
|
||||
|
||||
// A `file:///Users/ktg/...` link is dead for every reader but its author, and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue