From 37bccbee5028e8105f7c375ec9c8f93491da205d Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Fri, 28 Aug 2026 07:48:48 +0200 Subject: [PATCH 1/3] ci: validate RFC contents instead of requiring truapi changes --- .github/workflows/check-rfc.yml | 89 +++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/.github/workflows/check-rfc.yml b/.github/workflows/check-rfc.yml index 8fb0e43fc..7954ba647 100644 --- a/.github/workflows/check-rfc.yml +++ b/.github/workflows/check-rfc.yml @@ -19,25 +19,82 @@ jobs: fetch-depth: 0 persist-credentials: false - # ── A new RFC must land with the interfaces it specifies ── - # Edits to an already-accepted RFC (addenda recording what the host - # actually does) are not gated: they describe behavior that often lives - # entirely in the runtime crates. - - name: Require truapi interface changes for new RFCs + - name: Validate RFC documents + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} run: | - BASE=${{ github.event.pull_request.base.sha }} - NEW_RFCS=$(git diff --name-only --diff-filter=A "$BASE"...HEAD -- docs/rfcs/ ':!docs/rfcs/_index.md') - if [ -z "$NEW_RFCS" ]; then - echo "No new RFC documents; nothing to gate." + set -euo pipefail + + MERGE_BASE=$(git merge-base "$BASE_SHA" HEAD) + EXCLUDE=(':!docs/rfcs/_index.md' ':!docs/rfcs/0001-template.md') + CHANGED=$(git diff --name-only --diff-filter=d "$MERGE_BASE" HEAD -- 'docs/rfcs/*.md' "${EXCLUDE[@]}") + ADDED=$(git diff --name-only --diff-filter=A "$MERGE_BASE" HEAD -- 'docs/rfcs/*.md' "${EXCLUDE[@]}") + + if [ -z "$CHANGED" ]; then + echo "No RFC documents in this change." exit 0 fi - echo "New RFCs:" - echo "$NEW_RFCS" - API_CHANGED=$(git diff --name-only "$BASE"...HEAD -- rust/crates/truapi/) - if [ -z "$API_CHANGED" ]; then - echo "::error::PRs adding an RFC must include corresponding changes to the truapi interfaces in rust/crates/truapi/." + failed=0 + fail() { + echo "::error file=$1::$2" + failed=1 + } + + frontmatter_value() { + awk -v key="$2" ' + NR == 1 && $0 != "---" { exit } + NR > 1 && $0 == "---" { exit } + $0 ~ "^" key ": *[^ ]" { sub("^" key ": *", ""); gsub(/^"|"$/, ""); print; exit } + ' "$1" + } + + # A new RFC is held to the full shape of docs/rfcs/0001-template.md. + # An edit to an existing one is not: several predate the template and + # would fail on defects their author never introduced. + for file in $ADDED; do + for key in title owner; do + if [ -z "$(frontmatter_value "$file" "$key")" ]; then + fail "$file" "frontmatter is missing a non-empty '$key'" + fi + done + for section in Summary Motivation; do + if ! grep -qx "## $section" "$file"; then + fail "$file" "missing required section: ## $section" + fi + done + + # Three names for the same section are in circulation: docs/rfcs/ + # holds "Detailed Design" and the Fellowship's "Explanation", and + # the rfc skill's template emits "Approach". Any of them counts; + # settling on one is a template cleanup, not this gate's business. + if ! grep -qxE '## (Detailed Design|Explanation|Approach)' "$file"; then + fail "$file" "missing a design section: ## Detailed Design, ## Explanation or ## Approach" + fi + done + + for file in $CHANGED; do + while IFS= read -r placeholder; do + if grep -qF "$placeholder" "$file"; then + fail "$file" "unedited text from docs/rfcs/0001-template.md: '$placeholder'" + fi + done <<'PLACEHOLDERS' + RFC Title + @ownerhandle + One-paragraph explanation of the proposal. + Why are we doing this? + What parts of the design are still open? + PLACEHOLDERS + + if grep -qE '\b(TODO|TBD|FIXME)\b' "$file"; then + fail "$file" "unresolved TODO, TBD or FIXME" + fi + done + + if [ "$failed" -eq 1 ]; then + echo "::error::RFC validation failed. docs/rfcs/0001-template.md defines the expected shape." exit 1 fi - echo "API changes detected:" - echo "$API_CHANGED" + + echo "RFC documents validated:" + echo "$CHANGED" From 794ae25d35481d6710940f081be9c63041e539f1 Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Fri, 28 Aug 2026 08:10:59 +0200 Subject: [PATCH 2/3] docs: align the RFC template and its docs with the rfc skill --- .claude/skills/rfc/SKILL.md | 7 ++++--- .github/workflows/check-rfc.yml | 10 +++++----- CONTRIBUTING.md | 11 ++++++----- docs/rfcs/0001-template.md | 30 ++++++++++++------------------ docs/rfcs/_index.md | 2 +- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/.claude/skills/rfc/SKILL.md b/.claude/skills/rfc/SKILL.md index df025e4ae..f5cb87167 100644 --- a/.claude/skills/rfc/SKILL.md +++ b/.claude/skills/rfc/SKILL.md @@ -19,9 +19,10 @@ details are the implementer's call and need no prior approval here. exact form to inject the number. - Set `status: draft` in the frontmatter. Omitting it makes CI index the RFC as `accepted`. -- `check-rfc.yml` fails any PR touching `docs/rfcs/**` that does not also change - `rust/crates/truapi/`. A host-side proposal cannot satisfy that and belongs in - `docs/features/` instead. +- `check-rfc.yml` reads the document: a new RFC needs `title` and `owner` in its + frontmatter, a `## Summary`, a `## Motivation`, and a section covering the + approach, and no draft may keep unedited template text or a `TODO`. Rust + changes are not required in the same PR. ## Writing it diff --git a/.github/workflows/check-rfc.yml b/.github/workflows/check-rfc.yml index 7954ba647..b46f256ee 100644 --- a/.github/workflows/check-rfc.yml +++ b/.github/workflows/check-rfc.yml @@ -79,11 +79,11 @@ jobs: fail "$file" "unedited text from docs/rfcs/0001-template.md: '$placeholder'" fi done <<'PLACEHOLDERS' - RFC Title - @ownerhandle - One-paragraph explanation of the proposal. - Why are we doing this? - What parts of the design are still open? + title: "Title" + owner: "@handle" + Two or three sentences: what changes, and for whom. + How it works, at the level a reviewer needs in order to agree or object. + What this costs, and what was considered and dropped. PLACEHOLDERS if grep -qE '\b(TODO|TBD|FIXME)\b' "$file"; then diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a65c296a0..f3b97b501 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,11 +25,12 @@ For larger changes that need cross-team discussion, use the RFC process: 4. The PR will be auto-added to the project board for tracking and review 5. When the PR is approved and merged, CI automatically assigns the next sequential number, renames the file, and appends it to `docs/rfcs/_index.md` -**Important:** RFC PRs must include corresponding changes to the TrUAPI Rust -interfaces in `rust/crates/truapi/`. A CI check (`check-rfc.yml`) enforces -this — PRs that touch `docs/rfcs/` without also modifying `rust/crates/truapi/` -will fail. This ensures every RFC ships with a concrete API change, not just -prose. +A CI check (`check-rfc.yml`) reads the RFC documents a PR touches. A new RFC +needs frontmatter with a `title` and an `owner`, a `## Summary`, a +`## Motivation`, and a section describing the approach. Any RFC the PR touches +must also be free of unedited template text and of `TODO`, `TBD` or `FIXME`. +Implementation is not required in the same PR: it is tracked on the RFC's issue, +which carries a task per host alongside the Rust one. If you use Claude Code, the [`rfc`](.claude/skills/rfc/SKILL.md) skill is highly recommended for drafting RFCs — invoke it with `/rfc` to turn your notes into a well-structured document that follows the template above. diff --git a/docs/rfcs/0001-template.md b/docs/rfcs/0001-template.md index 938ee9100..86842e698 100644 --- a/docs/rfcs/0001-template.md +++ b/docs/rfcs/0001-template.md @@ -1,34 +1,28 @@ --- -title: "RFC Title" -owner: "@ownerhandle" +title: "Title" +owner: "@handle" +status: draft --- # RFC — Title ## Summary -One-paragraph explanation of the proposal. +Two or three sentences: what changes, and for whom. ## Motivation -Why are we doing this? What problem does it solve? What use cases does it support? +The problem, concretely. What is broken or impossible today, and why it matters now. -## Detailed Design +## Approach -Explain the design in enough detail that someone familiar with the codebase can implement it. Include: +How it works, at the level a reviewer needs in order to agree or object. +Not an implementation plan — the implementer owns the details. -- API changes -- Data model changes -- Migration strategy (if applicable) +## Trade-offs -## Drawbacks +What this costs, and what was considered and dropped. Bullets. -Why should we _not_ do this? Consider impact on complexity, maintenance, and scope. +## Open questions -## Alternatives - -What other designs were considered? Why were they rejected? - -## Unresolved Questions - -What parts of the design are still open? +Only genuine ones. Delete this section if there are none. diff --git a/docs/rfcs/_index.md b/docs/rfcs/_index.md index a72a0cd1e..daa061f95 100644 --- a/docs/rfcs/_index.md +++ b/docs/rfcs/_index.md @@ -10,7 +10,7 @@ created: 2026-03-13 | Number | Title | Status | Author | PR | | ------ | -------------------------------------------------------------------------------------------------------- | -------- | ----------------- | --------------------------------------------------------------- | -| 0001 | [RFC Title](0001-template.md) | accepted | @ownerhandle | — | +| 0001 | [Title](0001-template.md) | draft | @handle | — | | 0002 | [Permission Model for Host API](0002-permission-model.md) | accepted | @johnthecat | [#66](https://github.com/paritytech/triangle-js-sdks/pull/66) | | 0004 | [Redesign `host_account_create_proof`](0004-ringlocation-redesign.md) | draft | Valentin Sergeev | [#18](https://github.com/paritytech/host-rust-core/pull/18) | | 0006 | [Payment Host API](0006-payments.md) | accepted | Valentin Sergeev | [#94](https://github.com/paritytech/triangle-js-sdks/pull/94) | From fda321e7764955728de9ee8c5ce62d36180a7be7 Mon Sep 17 00:00:00 2001 From: Filippo Vecchiato Date: Fri, 28 Aug 2026 08:18:07 +0200 Subject: [PATCH 3/3] docs: drop the duplicate RFC template from the rfc skill --- .claude/skills/rfc/SKILL.md | 3 ++- .claude/skills/rfc/template.md | 28 ---------------------------- 2 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 .claude/skills/rfc/template.md diff --git a/.claude/skills/rfc/SKILL.md b/.claude/skills/rfc/SKILL.md index f5cb87167..edeaac813 100644 --- a/.claude/skills/rfc/SKILL.md +++ b/.claude/skills/rfc/SKILL.md @@ -12,7 +12,8 @@ details are the implementer's call and need no prior approval here. ## Mechanics -- File `docs/rfcs/.md` from [template.md](template.md). **Do not number it** +- File `docs/rfcs/.md` from + [docs/rfcs/0001-template.md](../../../docs/rfcs/0001-template.md). **Do not number it** and **do not touch `_index.md`** — `number-rfc.yml` assigns the number on merge to `main` and rebuilds the index from the files on disk. - Keep the H1 as `# RFC — Title`, em dash included; the numbering step rewrites that diff --git a/.claude/skills/rfc/template.md b/.claude/skills/rfc/template.md deleted file mode 100644 index 86842e698..000000000 --- a/.claude/skills/rfc/template.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Title" -owner: "@handle" -status: draft ---- - -# RFC — Title - -## Summary - -Two or three sentences: what changes, and for whom. - -## Motivation - -The problem, concretely. What is broken or impossible today, and why it matters now. - -## Approach - -How it works, at the level a reviewer needs in order to agree or object. -Not an implementation plan — the implementer owns the details. - -## Trade-offs - -What this costs, and what was considered and dropped. Bullets. - -## Open questions - -Only genuine ones. Delete this section if there are none.