Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .claude/skills/rfc/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ details are the implementer's call and need no prior approval here.

## Mechanics

- File `docs/rfcs/<kebab-title>.md` from [template.md](template.md). **Do not number it**
- File `docs/rfcs/<kebab-title>.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
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

Expand Down
28 changes: 0 additions & 28 deletions .claude/skills/rfc/template.md

This file was deleted.

89 changes: 73 additions & 16 deletions .github/workflows/check-rfc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
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
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"
11 changes: 6 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
30 changes: 12 additions & 18 deletions docs/rfcs/0001-template.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion docs/rfcs/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down