Compare commits

...

7 commits

Author SHA1 Message Date
21b91e870e Merge pull request 'docs: pin governance records to Forgejo ceremony 0.6.3' (#42) from build/36-ceremony-pin-proof into main
All checks were successful
ci / test (push) Successful in 16s
Reviewed-on: #42
Reviewed-by: claude-bot-andresmgsl <andres+1@heavyduty.builders>
Reviewed-by: kimi-bot-andresmgsl <andres+4@heavyduty.builders>
Reviewed-by: glm-bot-andresmgsl <andres+5@heavyduty.builders>
2026-08-31 19:46:36 +00:00
codex-bot-andresmgsl
c900f47d77 test: reject ceremony pin suffix drift
All checks were successful
labels / labels (pull_request) Successful in 10s
ci / test (pull_request) Successful in 15s
2026-08-31 19:30:33 +00:00
codex-bot-andresmgsl
21fcb1fdb1 docs: qualify every ceremony source record
All checks were successful
labels / labels (pull_request) Successful in 10s
ci / test (pull_request) Successful in 15s
2026-08-31 19:29:55 +00:00
codex-bot-andresmgsl
ed16f824ef docs: record governance pin enforcement
All checks were successful
labels / labels (pull_request) Successful in 9s
ci / test (pull_request) Successful in 15s
2026-08-31 19:27:00 +00:00
codex-bot-andresmgsl
c5e0d25c49 Merge remote-tracking branch 'origin/main' into build/36-ceremony-pin-proof
All checks were successful
labels / labels (pull_request) Successful in 10s
ci / test (pull_request) Successful in 16s
2026-08-31 19:25:51 +00:00
codex-bot-andresmgsl
3fac8096f7 test: require exact ceremony workflow pins
All checks were successful
labels / labels (pull_request) Successful in 11s
ci / test (pull_request) Successful in 18s
2026-08-31 18:38:54 +00:00
codex-bot-andresmgsl
52e8d45b78 test: pin ceremony governance source
All checks were successful
labels / labels (pull_request) Successful in 11s
ci / test (pull_request) Successful in 18s
2026-08-31 18:32:58 +00:00
4 changed files with 35 additions and 6 deletions

View file

@ -2,12 +2,13 @@
Never edit these files ad hoc. The six manifest-listed doctrine files are Never edit these files ad hoc. The six manifest-listed doctrine files are
byte-identical copies of byte-identical copies of
[heavy-duty/ceremony](https://github.com/heavy-duty/ceremony) at 0.6.3, but [heavy-duty/ceremony](https://forgejo.heavyduty.builders/heavy-duty/ceremony) at 0.6.3, but
stoke does not run `docs-sync` or re-diff the mirror in CI. Change doctrine stoke does not run `docs-sync` or re-diff the mirror in CI. Change doctrine
upstream through its own flow, then re-vendor it here when the pin moves. upstream through its own flow, then re-vendor it here when the pin moves.
The labels doctrine is vendored manually from heavy-duty/ceremony at 0.6.3 The labels doctrine is vendored manually from
because stoke keeps its own tag-driven `.forgejo/workflows/release.yml`. [heavy-duty/ceremony](https://forgejo.heavyduty.builders/heavy-duty/ceremony) at 0.6.3 because
stoke keeps its own tag-driven `.forgejo/workflows/release.yml`.
`docs-sync --fix` cannot run until or unless a future issue adopts the `docs-sync --fix` cannot run until or unless a future issue adopts the
ceremony release-workflow pin; until then, doctrine updates must re-vendor ceremony release-workflow pin; until then, doctrine updates must re-vendor
the pinned manifest manually. the pinned manifest manually.

View file

@ -1,7 +1,7 @@
# AGENTS.md — start at .ceremony/ # AGENTS.md — start at .ceremony/
This repository is governed by This repository is governed by
[heavy-duty/ceremony](https://github.com/heavy-duty/ceremony). Read [heavy-duty/ceremony](https://forgejo.heavyduty.builders/heavy-duty/ceremony). Read
`.ceremony/AGENTS.md` first — it routes you to your role file, vendored `.ceremony/AGENTS.md` first — it routes you to your role file, vendored
beside it. Repo specifics (the review panel roster, the scope labels, what beside it. Repo specifics (the review panel roster, the scope labels, what
a drill means here, code conventions) live in CONTRIBUTING.md. a drill means here, code conventions) live in CONTRIBUTING.md.

1
changelog.d/36.md Normal file
View file

@ -0,0 +1 @@
- Enforced the exact Forgejo ceremony source and version across governance records and workflow pins. (#36).

View file

@ -11,6 +11,9 @@ const REPOSITORY_CONFIG = path.join(__dirname, '..', '.github', 'labels.conf');
const REPOSITORY_LABELER = path.join(__dirname, '..', '.github', 'labeler.yml'); const REPOSITORY_LABELER = path.join(__dirname, '..', '.github', 'labeler.yml');
const REPOSITORY_MIRROR = path.join(__dirname, '..', '.ceremony'); const REPOSITORY_MIRROR = path.join(__dirname, '..', '.ceremony');
const ROOT_AGENTS = path.join(__dirname, '..', 'AGENTS.md'); const ROOT_AGENTS = path.join(__dirname, '..', 'AGENTS.md');
const CEREMONY_REPOSITORY = 'https://forgejo.heavyduty.builders/heavy-duty/ceremony';
const CEREMONY_VERSION = '0.6.3';
const CEREMONY_WORKFLOWS = ['labels.yml', 'labels-sweep.yml'];
const cleanups = []; const cleanups = [];
process.on('exit', () => { process.on('exit', () => {
for (const dir of cleanups) fs.rmSync(dir, { recursive: true, force: true }); for (const dir of cleanups) fs.rmSync(dir, { recursive: true, force: true });
@ -131,14 +134,38 @@ test('repository scope mapping covers every configured scope with the ruled path
} }
}); });
test('repository carries the complete 0.6.1 doctrine mirror and root router', () => { test('repository carries the complete Forgejo 0.6.3 doctrine mirror and root router', () => {
const vendored = ['AGENTS.md', 'TRIAGE.md', 'BUILDER.md', 'REVIEWER.md', 'LABELS.md', 'RELEASES.md']; const vendored = ['AGENTS.md', 'TRIAGE.md', 'BUILDER.md', 'REVIEWER.md', 'LABELS.md', 'RELEASES.md'];
for (const filename of vendored) { for (const filename of vendored) {
assert.ok(fs.statSync(path.join(REPOSITORY_MIRROR, filename)).isFile(), `${filename} is missing`); assert.ok(fs.statSync(path.join(REPOSITORY_MIRROR, filename)).isFile(), `${filename} is missing`);
} }
const mirrorReadme = fs.readFileSync(path.join(REPOSITORY_MIRROR, 'README.md'), 'utf8'); const mirrorReadme = fs.readFileSync(path.join(REPOSITORY_MIRROR, 'README.md'), 'utf8');
const sourceVersionRecord = `[heavy-duty/ceremony](${CEREMONY_REPOSITORY}) at ${CEREMONY_VERSION}`;
assert.equal(
mirrorReadme.split(sourceVersionRecord).length - 1,
2,
'mirror README does not identify the exact Forgejo ceremony source and version in both records',
);
assert.match(mirrorReadme, /labels doctrine is vendored manually/); assert.match(mirrorReadme, /labels doctrine is vendored manually/);
assert.doesNotMatch(mirrorReadme, /The pin lives in `.github\/workflows\/release\.yml`/); assert.doesNotMatch(mirrorReadme, /The pin lives in `.github\/workflows\/release\.yml`/);
assert.doesNotMatch(mirrorReadme, /Machine-managed by|CI re-diffs them/); assert.doesNotMatch(mirrorReadme, /Machine-managed by|CI re-diffs them/);
assert.match(fs.readFileSync(ROOT_AGENTS, 'utf8'), /read\s+`.ceremony\/AGENTS\.md` first/i); const rootAgents = fs.readFileSync(ROOT_AGENTS, 'utf8');
assert.ok(
rootAgents.includes(`[heavy-duty/ceremony](${CEREMONY_REPOSITORY})`),
'root router does not identify the Forgejo ceremony repository',
);
assert.match(rootAgents, /read\s+`.ceremony\/AGENTS\.md` first/i);
});
test('repository workflow pins use the exact Forgejo ceremony version', () => {
for (const workflow of CEREMONY_WORKFLOWS) {
const contents = fs.readFileSync(path.join(__dirname, '..', '.forgejo', 'workflows', workflow), 'utf8');
const prefix = `uses: heavy-duty/ceremony/.github/workflows/${workflow}@`;
const pins = contents.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith(prefix));
assert.deepEqual(
pins,
[`${prefix}${CEREMONY_VERSION}`],
`${workflow} does not pin ceremony ${CEREMONY_VERSION}`,
);
}
}); });