feat(kyberforge): make Research doc name one Research registry #139

Merged
Defame1297 merged 10 commits from feat/121-research-doc-grammar into main 2026-09-21 19:52:19 +00:00
6 changed files with 40 additions and 30 deletions
Showing only changes of commit 58a3f402a6 - Show all commits

View File

@@ -86,7 +86,7 @@ A plugin's research `sources.md` (e.g. `plugins/git/docs/research/docs/git/sourc
headings are the source slugs. A skill's `Research doc:` field names exactly one, and
`factory-audit` resolves each entry's slug against it. An entry with no registry declares
`Research doc: none` and names what it was actually drawn from in `Basis:`.
_Avoid_: research doc, sources file, topic doc (a topic doc is a digest of sources, not the registry)
_Avoid_: bare "research doc" (the noun; `Research doc:` is the field name), sources file, topic doc (a topic doc is a digest of sources, not the registry)
### Governance

View File

@@ -5,6 +5,9 @@ merged into `factory-audit`, which dispatches to a skill flow and an agent flow
`skill-audit` below as `factory-audit`'s skill flow. The decision itself is unchanged — ADR-0025
carried every audit criterion, tier and finding level across as-is.
**Amended by ADR-0028 (2026-09-21).** INFO stays for a check that cannot run. A check that ran and
found a mismatch in `Research doc:` is now a FAIL, so INFO no longer covers it.
`skill-audit` shipped with two finding levels: FAIL (blocks shipping) and
SUGGESTION (optional improvement). Provenance validation introduced observations
that are worth surfacing but not actionable: a `references/*.md` file with no

View File

@@ -9,8 +9,8 @@ whose `## H2` headings are the source slugs. The corpus did something else: 29 o
entries pointed at a research topic doc annotated `(whole-document reference)`, and 6 values were not
a single path (comma-separated lists and shell brace expansion, plus a semicolon pair in
`gitea-releases`). Checks 7 and 8 of `validate-provenance.sh` look the slug up as an H2 in the named
file, so 36 entries reported INFO and nothing failed. Measured by running the script over all 39 skill
directories, since nothing else runs it over the corpus.
file, so 36 entries reported INFO and nothing failed. Measured by running the script over all 38 skill
directories (27 with a `references/sources.md`, 11 without), since nothing else runs it over the corpus.
We decided that `Research doc:` names exactly one **Research registry** (the term is in
`CONTEXT.md`), as the spec always said. Slug-to-H2 lookup in the registry is the only provenance link
@@ -56,6 +56,17 @@ pairs are rejected outright, since nothing expands them in a markdown field.
- **(c) Everything FAIL (rejected).** Fails a correctly-provenanced skill audited from a deployed
copy, which the file-structure exemption exists to prevent.
**Q4 — enforcement.** A corpus-wide sweep gate lands in the same change: a test or pre-push hook that
runs `validate-provenance.sh` over every `plugins/*/.apm/skills/*/` and fails on any FAIL. Deferring it
was rejected because without a caller the FAIL tier is inert; nothing but `check-scope-walkup-sync.sh`
(on fixtures) invokes the validator today.
**Q5 — parser parity.** `parse_research_doc` accepts the bullet spelling (`- **Research doc:**`) as
`parse_contributing_files` already does, with a regression test. `parse_status` was removed from the
validator during this change, so it gets no test. Included because it is the same failure shape as
#111 and #118 (a parser returns "nothing found", the caller reads it as "nothing declared"), sits in
the same file, and `gitea-releases` already writes the unhyphenated form.
**Q6 — what happens to check 8.** Found unsatisfiable during the migration, after Q3 was decided.
Check 8 requires every `extracted` slug in the research doc to appear in the skill's `sources.md`.
That worked while entries pointed at topic docs, and was dormant. Under Q1(a) the named file is a
@@ -77,8 +88,10 @@ check on every `Basis:` bullet would fail them.
- **(a) A bullet annotated `(removed in <sha>)` skips the existence check (chosen).** The check stays
for live paths, which is what catches a renamed ADR, and deletion becomes an explicit, auditable
annotation. Weakness: the annotation can be written on any bullet to avoid the check. Verifying the
sha with `git cat-file -e` would close that, and was left out as over-engineering for three bullets.
annotation. The annotation is anchored at the end of the value and the sha is 7-40 hex characters.
Weakness: the annotation can be written on any bullet to avoid the check. Verifying the sha with
`git cat-file -e` would close that; the user decided against it as over-engineering for three
bullets, so the sha is format-checked only, not verified.
- **(b) `Basis:` becomes free prose with no existence check (rejected).** Gives up the one check that
catches a renamed or moved ADR.
- **(c) Drop those `Basis:` lines and keep `none` with a prose reason (rejected).** Loses the
@@ -92,13 +105,15 @@ fixtures in this repo". No such fixture or test exists in the tree or in history
in `d1afdbe` with no test files, and the only vale test ever deleted (`4de5b6b`) guards an unrelated
`E100`. Under Q2 it FAILed for a missing `Basis:`.
- **(e) Remove the entry and its `source_keys` citations (chosen).** The stated basis was false, so
there is nothing honest to declare. The behavioural rules stay in the skills; only the provenance
claim goes. The gate needs no allowlist.
- **(e) Remove the entry and its `source_keys` citations (chosen, as the interim state).** The stated
basis was false, so there is nothing honest to declare. The behavioural rules stay in the skills;
only the provenance claim goes. The gate needs no allowlist.
- **(a) `Basis: tests/test-vale-wrap.sh` (rejected).** Backs about one of six claims and overstates the
rest.
- **(b) Commit reproduction fixtures (rejected for now).** The right fix if the behaviours matter, but
separate work from this issue.
- **(b) Commit reproduction fixtures (chosen, supersedes the interim removal).** The user decided to
commit real Vale reproduction fixtures under `plugins/lint` rather than soften the wording. The
`house-vale-3-15-2-repro` claim is restored only once it is backed by committed fixtures, and it
names them via `Basis:` (with `Research doc: none`). Until they land, the claim stays absent.
- **(c) Allow `none` without `Basis:` for "house-verified" entries (rejected).** Reopens Q2 and gives
an escape hatch for unverified claims.
- **(d) Keep the entry and allowlist the two skills in the gate (rejected).** Keeps a false claim in
@@ -107,16 +122,6 @@ in `d1afdbe` with no test files, and the only vale test ever deleted (`4de5b6b`)
`configuration-reference.md` still says its rows were "reproduced against Vale 3.15.2"; that wording
now has no provenance entry behind it and is left for a separate decision.
**Q4 — enforcement.** A corpus-wide sweep gate lands in the same change: a test or pre-push hook that
runs `validate-provenance.sh` over every `plugins/*/.apm/skills/*/` and fails on any FAIL. Deferring it
was rejected because without a caller the FAIL tier is inert; nothing but `check-scope-walkup-sync.sh`
(on fixtures) invokes the validator today.
**Q5 — parser parity.** `parse_status` and `parse_research_doc` accept the bullet spelling
(`- **Status:**`) as `parse_contributing_files` already does, with a regression test. Included because
it is the same failure shape as #111 and #118 (a parser returns "nothing found", the caller reads it as
"nothing declared"), sits in the same file, and `gitea-releases` already writes the unhyphenated form.
## Consequences
- About 40 `references/sources.md` entries migrate: roughly 30 repoint from a topic doc to the registry,
@@ -133,4 +138,6 @@ it is the same failure shape as #111 and #118 (a parser returns "nothing found",
its mention from `skill-file-structure.md` and `create.md` where present.
- The corpus-wide sweep is a new gate: register it in `docs/spec/gates.md` and
`.pre-commit-config.yaml`. The corpus must be migrated in the same change or the suite goes red.
- The validator rejects an absolute path or one that escapes the repo with `..` in `Research doc:` and
`Basis:`, and rejects a `Research doc:` value with internal whitespace, backticks, or a comma list.
- Reversing this means re-migrating the same entries, which is why it is recorded.

View File

@@ -21,12 +21,12 @@ Install hooks via `pc-run`, wiring **all three stages**. This repo's `.pre-commi
`default_install_hook_types`, so a plain install silently skips `commit-msg` (Conventional Commits)
and `pre-push` (everything below).
The pre-push command reports **10** hooks, not 8. The extra two are pre-commit's own `meta` hooks,
The pre-push command reports **11** hooks, not 9. The extra two are pre-commit's own `meta` hooks,
`check-hooks-apply` and `check-useless-excludes`: they declare no `stages:`, so they run at every
stage including this one. Both are declared in this repo's `.pre-commit-config.yaml` like everything
else — what separates them is `repo: meta` (pre-commit's own built-ins) from `repo: local`. Eight
else — what separates them is `repo: meta` (pre-commit's own built-ins) from `repo: local`. Nine
is the count of hooks this repo authors itself, and `--hook-stage pre-push --all-files` is a full
rehearsal of all eight. A PR merged through Gitea's merge button runs none of them: no local push
rehearsal of all nine. A PR merged through Gitea's merge button runs none of them: no local push
happens at all.
A real push has a gap of its own. When one `git push` carries several refs

View File

@@ -4,7 +4,7 @@
- **URL:** https://gitea.com/gitea/gitea-mcp
- **Description:** Official gitea-mcp repository; operation/*.go source files documenting the MCP tools, their parameters, and CLI flags. Originally extracted at v1.3.0; the input parameter schemas in `references/call-signatures.md` were re-verified live via `ToolSearch` against the deployed server, **last verified at v1.7.0** as reported by `get_gitea_mcp_server_version`.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digested in gitea/api-reference.md, Releases and Tags section, and gitea/troubleshooting.md, `delete_release` numeric-id gotcha and `per_page` defaults)
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digest: plugins/gitea/docs/research/docs/gitea/api-reference.md, Releases and Tags section; also plugins/gitea/docs/research/docs/gitea/troubleshooting.md, `delete_release` numeric-id gotcha and `per_page` defaults)
**Contributing files:**
- SKILL.md (Dispatch table, Gotchas)
@@ -16,7 +16,7 @@
- **URL:** https://gitea.com/gitea/gitea-mcp/raw/branch/main/operation/repo/slim.go
- **Description:** Slim response shape structs from gitea-mcp source; defines exactly which fields the MCP server returns for tags and releases.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digested in gitea/api-reference.md, Releases and Tags response shapes)
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digest: plugins/gitea/docs/research/docs/gitea/api-reference.md, Releases and Tags response shapes)
**Contributing files:**
- references/call-signatures.md (release/tag object shapes)
@@ -27,7 +27,7 @@
- **URL:** context7:/websites/gitea
- **Description:** Official Gitea docs mirror on Context7 (docs.gitea.com content) — release and tag semantics, draft/prerelease behavior.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digested in gitea/workflow-conventions.md, Release and tag conventions section)
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digest: plugins/gitea/docs/research/docs/gitea/workflow-conventions.md, Release and tag conventions section)
**Contributing files:**
- SKILL.md (Gotchas — draft/prerelease as explicit flags)
@@ -39,7 +39,7 @@
- **URL:** context7:/git_gitea_com/gitea_tea
- **Description:** Official `tea` CLI (reference Gitea client) docs on Context7 — practitioner release/tag command patterns, semver tag conventions, draft/prerelease flags, release-notes-from-file conventions.
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digested in gitea/workflow-conventions.md, Release and tag conventions section)
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md (digest: plugins/gitea/docs/research/docs/gitea/workflow-conventions.md, Release and tag conventions section)
**Contributing files:**
- references/conventions.md (semver tag naming, release-notes sourcing)

View File

@@ -177,11 +177,11 @@ If a research `sources.md` is present in the conversation context:
source slugs (e.g. `plugins/myplugin/docs/research/docs/<topic>/sources.md`) — never a topic
document, and never a list: no brace expansion, no comma- or semicolon-separated paths, no
second `Research doc:` line. A pointer to the topic document that digested the source goes in
an annotation after the path, e.g. `<registry path> (digested in remotes.md)`, where it is not
an annotation after the path, e.g. `<registry path> (digest: <full plugins/... path of the topic doc>)`, where it is not
checked. `/factory-audit` fails a slug missing from the registry it names.
If the entry has no Research registry — an org convention, an ADR, a house-verified
reproduction — write `- **Research doc:** none` and name what it was drawn from with one
If the entry has no Research registry — an org convention, an ADR, a reproduction
backed by committed fixtures or tests named in `Basis:` — write `- **Research doc:** none` and name what it was drawn from with one
`- **Basis:** <repo path>` line per path. Each Basis path is checked to exist; annotate one
that has since been deleted `(removed in <sha>)` and the check is skipped. `none` with no Basis
is a FAIL.