Skip to content

WP07: Update Rulebook System Files: the CheckForUpdates action, the update workflow, GHTOKENWORKFLOW (D44) and unusedRulebookFiles as paths - #66

Merged
Arthurvdv merged 20 commits into
mainfrom
wp07/update
Oct 7, 2026
Merged

Arthurvdv merged 20 commits into
mainfrom
wp07/update

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Closes #9, #28, #49 (WP07, engine side). The user pages docs/updating.md and docs/ghtokenworkflow.md follow in a pull request to ALCops/rulebook right after this one.

Summary

  • actions/CheckForUpdates (new): check mode (update other than 'Y') and update mode. Inputs templateUrl (empty uses the setting), templatePath / installedTemplatePath / templateSha (local templates for tests and CI), token, update, downloadLatest, directCommit, updateBranch, repositoryRoot, actor; outputs updatesAvailable, pullRequestUrl, templateSha, failure (token, template, validation, push, pull-request, error). Check mode never fails: anything that goes wrong is one warning update check skipped: <reason>.
  • modules/Rulebook.Update (new): file classes with AL-Go's include-list rule (overwrite, settings, generated, customizable, org-owned), the candidate tree, regeneration of skeletons/ and rulesets/, Test-Rulebook on the candidate as the gate before any push, the change list and the sha-only case, the D35 three-way decision for site/**, the in-place settings edit of $schema, templateUrl, templateSha, the workflow rewrite ({TEMPLATEURL}, the ChangeRule levels/stages choice lists from the settings (D30), schedule: from update.schedule) with a line editor ported from AL-Go's yamlclass, release notes newer than the installed copy, the pull request body with the effective diff, and Publish-RulebookUpdate (duplicate title guard, clone, commit, push, labelled pull request, direct commit with fallback to a pull request).
  • modules/Rulebook.GitHub (new): one REST wrapper, the GHTOKENWORKFLOW exchange (personal access token as it is; GitHub App JSON in AL-Go's format exchanged for an installation token limited to the repository, RS256 JWT), zipball download, pull request helpers, and clone and push with the token only in the git process environment (http.<server>/.extraheader through GIT_CONFIG_COUNT), never in a URL or git config.
  • template/.github/workflows/UpdateRulebookSystemFiles.yaml (new): dispatch inputs templateUrl, downloadLatest, directCommit; read-only GITHUB_TOKEN; a settings step resolves the secret name from ghTokenWorkflowSecretName; the update writes schedule: from update.schedule.
  • Validate: checkForUpdates is wired: one notice or warning and a ## Template update check summary section, read with GITHUB_TOKEN, never counted towards warnings= or failOnWarning.
  • Schema and C9 (Settle the format of unusedRulebookFiles entries #49): unusedRulebookFiles entries are repository-relative paths with / (schema pattern); C9 matches them by path only, as the update does. New update.schedule (five-field cron or null).
  • Fixtures: tests/fixtures/templates/v1 and v2 and tests/fixtures/repos/update-org, generated parts written by the engine functions, derivation and the exact v1 -> v2 difference list in tests/fixtures/templates/README.md (asserted by a test).
  • Tests: Rulebook.GitHub.Tests.ps1, Rulebook.Update.Tests.ps1 (including Publish-RulebookUpdate against a bare git repository with a pre-receive hook as branch protection), CheckForUpdates.Action.Tests.ps1, and additions to the Validate, Validate action, Template and Schemas suites. 1196 tests, green on Windows and on Linux.
  • CI: new job update-action (check mode on update-org against v1 and v2, update mode without a token must fail with failure token); the Validate fixture steps pass checkForUpdates: 'false'.
  • Docs: docs/reference/update-mechanics.md (new); ADR D44 closes O4 (O4: Name of the write token secret (reuse GHTOKENWORKFLOW or a Rulebook-specific name) #28); ARCHITECTURE 4, 5.3, 5.5, 7.3, 8, 11, 13; naming.md; template-content.md (42 files, 10 hand-written); dashboard.md 9; al-go-template-mechanics.md 9.1; CONTRIBUTING (layout, CI, ruleset); the template README.

Interview decisions (2026-10-07)

  1. All file classes implemented generically now (site, ChangeRule, release notes on fixtures), consumers later.
  2. Live E2E on two scratch repositories under Arthurvdv with a GitHub App as GHTOKENWORKFLOW.
  3. O4: Name of the write token secret (reuse GHTOKENWORKFLOW or a Rulebook-specific name) #28 and Settle the format of unusedRulebookFiles entries #49 fold into this PR: D44 (reuse GHTOKENWORKFLOW, AL-Go format, ghTokenWorkflowSecretName); unusedRulebookFiles as repository-relative paths.
  4. Secret lookup in a workflow settings step, secrets[steps.settings.outputs.secretName]; the action does the exchange and masks the token.
  5. update.schedule in v1, written into the workflow by the update.
  6. The update validates the candidate; any error fails the run and nothing is pushed.
  7. Engine proof with local template folders and a bare git repository; CI job update-action.
  8. Shared helper module for the action entry scripts (annotations, summary, outputs) #58 stays at WP12 (helper duplication).
  9. Check mode never fails Validate and is never counted.
  10. Docs: update-mechanics.md, ARCHITECTURE touch-ups, D44; user docs in ALCops/rulebook.
  11. skeletons/*.ruleset.json are generated from the settings.
  12. One executor, Sonnet review before each PR, Arthur merges.

Review

Three Sonnet code-review rounds on the engine at effort high: 31 findings, 29 fixed, 1 accepted (the duplicate guard embeds the base head sha, AL-Go behaviour), 2 deferred to #58 (helper duplication, comment on #58). Fixed among others: binary detection by content, a sectioned PR body with truncation by whole parts, fence-aware release-note headings, escaped REST path segments, caller work folders kept, check mode never failing, the installed template downloaded on every differing run with every failure there a note, the original error kept when an exchange fails, tokens masked the moment they are obtained, the push and pull-request failure stages, no duplicate guard for direct commits, summaries cut at a line boundary with fences closed, link loops not followed, and a real https clone test for the token-free git config. The docs review of ALCops/rulebook#4 (three rounds, 29 findings) led to two more engine changes here: ghTokenWorkflowSecretName rejects the GITHUB_ prefix with no silent fallback, and a failed pull request step always says "Branch was pushed.".

Known limitations

  • A push to the update branch while the update pull request is open changes the <sha7> in the title, so the next run opens a second update pull request (AL-Go behaviour).
  • docs/updating.md and docs/ghtokenworkflow.md land with the ALCops/rulebook pull request that follows; the links return 404 until then.
  • A private template needs the GitHub App installed on the template repository too (or a PAT that can read it).
  • Check mode reads the template with GITHUB_TOKEN only: a private template, or a pull request from a fork, gives "update check skipped".

Deviations from the issue text

  • skeletons/*.ruleset.json are generated from the organization's settings on every update, not overwrite; only skeletons/README.md is overwrite.
  • The PR body carries the change table, the effective diff, skipped site files, notes, validation warnings and the release notes (not only the release notes).
  • Scheduling is the settings key update.schedule (the open question answered: in v1).
  • The action takes templatePath and installedTemplatePath for local templates (tests and CI).
  • unusedRulebookFiles entries need at least one / (every managed path has a folder); a bare file name is rejected by the schema.
  • Sha-only also covers the {TEMPLATEURL} placeholder, so a repository fresh from the template gets the "not recorded" notice rather than "updates available".
  • The installed template is downloaded on every run whose recorded commit differs from the new one.

Spin-offs

Live run (2026-10-07)

AC1 to AC10 met on Arthurvdv/rulebook-e2e-template and Arthurvdv/rulebook-e2e-update with a GitHub App as GHTOKENWORKFLOW: the first run's PR with templateSha and {TEMPLATEURL} only, then "No updates available"; "Updates available" in Validate after the template moved; a level change regenerating the chained endpoints with essential.* untouched; "Pull request already exists"; a stage change regenerating only *.ci; an override winning; org files untouched; update.schedule added and removed; directCommit falling back to a PR under a ruleset; and without the secret "The GHTOKENWORKFLOW secret is needed ...". The secrets[steps.settings.outputs.secretName] lookup works, and no token appears in the logs. Runs, PRs and observations: docs/reference/update-mechanics.md section 12 and the comments below. One engine fix came from the run (endpoint order in the effective-diff tables).

Docs

ALCops/rulebook#4 (docs/updating.md, docs/ghtokenworkflow.md); merge this PR first.

🤖 Generated with Claude Code

Arthurvdv and others added 16 commits October 7, 2026 10:06
…elative paths, C9 path-only (#49)

unusedRulebookFiles entries get a pattern (repository-relative paths with /, at
least one folder), the new update object carries schedule (five-field cron or
null), and templateSha says the update writes it. C9 matches an entry by its
path only, so an entry that silences C9 is the entry the update honours.
The template settings and the template-default fixture gain
"update": { "schedule": null }.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l requests, clone and push

Invoke-GitHubApi is the one REST call (non-2xx returned, pagination over Link,
-OutFile), the single mock point of the suites. Get-GitHubAccessToken passes a
personal access token through and exchanges GitHub App JSON (AL-Go format) for
an installation token limited to the repository; New-GitHubAppJwt signs the
RS256 JWT and accepts a PEM whose lines were joined. New-GitHubClone gives git
the token as an extra header through GIT_CONFIG_COUNT in the process
environment only; Publish-GitHubChange commits, pushes a branch or a direct
commit and falls back to a branch when the push is refused. Tests against bare
repositories, with a pre-receive hook as branch protection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The update workflow of an organization repository: dispatch inputs
templateUrl, downloadLatest and directCommit, a read-only GITHUB_TOKEN, a
settings step that reads ghTokenWorkflowSecretName (default GHTOKENWORKFLOW)
and, on a schedule, takes directCommit from commitOptions.createPullRequest,
then the CheckForUpdates action with update 'Y' and the secret by name. No
schedule: in the template; the update writes it from update.schedule. The
Template suite counts it among the 10 hand-written files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on gate, update pull request

ConvertTo-TemplateUrl normalises the template URL AL-Go style;
Get-RulebookTemplate downloads the template zipball (and the installed one when
the site comparison or a removed listed file needs it), retrying a private
template with the exchanged write token, or takes local folders.
Get-RulebookUpdatePlan copies the working tree, writes the managed files the
template ships by class (overwrite, settings, customizable with the D35
three-way decision, unusedRulebookFiles as paths), regenerates skeletons/ and
rulesets/, runs Test-Rulebook on the candidate and lists the changes, with the
sha-only case for a repository whose only difference is templateSha.
Update-RulebookSettingsText edits $schema, templateUrl and templateSha in place;
ConvertTo-UpdatedWorkflowText replaces {TEMPLATEURL}, rewrites the ChangeRule
choice lists from the settings (D30) and adds or removes the schedule with a
line editor ported from AL-Go's yamlclass. Publish-RulebookUpdate guards
against a duplicate pull request by title, clones, commits, pushes and opens
the labelled pull request with the effective diff in its body.

Fixtures: tests/fixtures/templates/v1 and v2 and tests/fixtures/repos/update-org,
generated parts written by the engine functions, derivation in
tests/fixtures/templates/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
action.yaml takes templateUrl (empty uses the setting), templatePath,
installedTemplatePath and templateSha (local templates for tests and CI), the
GHTOKENWORKFLOW token, update, downloadLatest, directCommit, updateBranch,
repositoryRoot and actor, and writes updatesAvailable, pullRequestUrl,
templateSha and failure. CheckForUpdates.ps1 never fails check mode: an
unreachable template or a candidate that would not validate is one warning
"update check skipped". Update mode needs the token before any request, fails
on a candidate that does not validate with its findings and pushes nothing,
masks the exchanged token and reports an open pull request with the same title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With checkForUpdates the Validate action downloads the template of the
settings with GITHUB_TOKEN (now passed to the step, read-only) or takes local
folders (-TemplatePath, -InstalledTemplatePath), builds the update plan and
adds one notice (no updates; templateSha not recorded) or warning (updates
available; check skipped) and the section "## Template update check" to the
summary. Neither counts towards warnings= or failOnWarning, and the check
never fails the step. The result carries UpdateCheck { Status, Reason, Plan }.
A first run's bookkeeping (templateSha and the {TEMPLATEURL} placeholder) now
counts as sha-only, so a repository fresh from the template gets the notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new job runs ./actions/CheckForUpdates in check mode on update-org against
the local templates v1 (no updates) and v2 (updates available), and in update
mode without a token (must fail with failure 'token'); a guard step fails the
job on any other outcome. The three Validate steps of validate-action pass
checkForUpdates 'false', so the fixtures are not compared with ALCops/rulebook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…kFiles as paths

docs/reference/update-mechanics.md describes the file classes and the
include-list rule, the candidate tree, the customizable decision table, the
settings edit, the workflow rewrite, the token exchange and secret lookup,
check mode, update mode, the pull request body, the action and the engine
proof. D44 records the reuse of GHTOKENWORKFLOW in AL-Go's format with
ghTokenWorkflowSecretName and closes O4 (#28). ARCHITECTURE sections 4, 5.3,
5.5, 7.3, 8, 11 and 13, naming.md, template-content.md, dashboard.md section 9,
al-go-template-mechanics.md section 9.1, CONTRIBUTING (layout, the
update-action job, the ruleset) and the template README follow: skeletons are
generated from the settings, C9 matches unusedRulebookFiles by path (#49),
update.schedule is a setting, Validate runs the update check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repository, branch and commit of a template URL may hold '?', '#' or '%'.
Join-ApiPath escapes each segment with EscapeDataString and keeps the slashes
between them, for the installation, branch, zipball, pull request and label
calls and the manual tree link of a refused pull request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a caller's work folder stays

Validate imports Rulebook.GitHub and Rulebook.Update and names its work folder
inside the try, so any failure of the check becomes "update check skipped".
Both entry scripts remove only the temporary work folder they name
themselves; a -WorkPath or -UpdateWorkPath the caller passes is left in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed release notes, one removal rule

- A file is binary by a longer extension list or a NUL byte in its first 8 KB
  (Test-BinaryFile), in the comparison, the copy and the content sha.
- The pull request body is built from separate sections, without searching its
  own Markdown. Above the limit it drops the release notes, then endpoint
  tables of the effective diff from the end, each with an italic note, and
  only then cuts at a line boundary.
- The release-notes heading shift leaves lines inside ``` and ~~~ fences alone.
- Compare-CustomizableFile decides only the site files the template ships;
  removal goes through unusedRulebookFiles alone (the dead -Listed switch and
  the remove and vanished outcomes are gone).
- update-mechanics.md: the binary rule, the body limit, the decision table, and
  the duplicate guard limitation stated plainly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…error kept, pull request stage, tokens masked

- Get-RulebookTemplate downloads the installed commit whenever it differs from
  the new one, so every dropped managed file gets its note; -UpdateMode and
  -UnusedFiles are gone.
- A failed exchange in the private-template retry rethrows the original error
  (its status kept, so a missing installed commit stays a note) and adds the
  exchange error as a note. -OnToken runs with every exchanged token first.
- Publish-RulebookUpdate: the duplicate guard, the body and the opening are
  the pull-request stage; after the push a failure names the pushed branch and
  its tree link. Results carry DiffNote, which the summary renders, with the
  new release notes.
- The sha-only check reads text only for the settings and workflow kinds;
  Get-TreeFile prunes .git, site/data and node_modules while walking.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmary capped

The write token is exchanged and masked right after the token guard, before
the template download and the plan; tokens exchanged for a private template
are masked through -OnToken. In check mode any failure of the template or the
plan is the warning "update check skipped" with exit 0; update mode still
fails. The job summary is capped at 900 KiB at a line boundary, as Validate's
is. Validate passes the new Get-RulebookTemplate parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontent, check limits

update-mechanics.md and ARCHITECTURE 7.3 follow the code: the installed
template is downloaded whenever it differs, a personal access token is used as
it is while GitHub App JSON is exchanged, every token is masked when obtained,
the push and pull-request stages, what the job summary holds and its cap, and
that Validate's check is skipped for a template GITHUB_TOKEN cannot read. The
template README links docs/updating.md and docs/ghtokenworkflow.md plainly and
says when Validate reports an update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e failures as notes, direct commit without the guard

- Get-TreeFile skips directory symlinks and junctions.
- A failed exchange for the new template adds its message to the original
  error (status kept). The installed zipball is read with the token the new
  template needed, without a second exchange; any failure there is a note.
- Publish-RulebookUpdate runs the duplicate guard only for a pull request; a
  direct commit takes <sha7> from the cloned head and builds no body.
- Limit-SummaryText cuts a summary at a line boundary, closes an open code
  fence and adds a footer.
- New-GitHubClone takes extra GIT_CONFIG_* pairs as a test seam; the suite
  clones an https remote through url.<bare>.insteadOf and checks that the token
  is in neither the remote, the config nor any file under .git.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reads with GITHUB_TOKEN only

Both job summaries go through Limit-SummaryText with a footer that says where
the full lists are (the pull request body, or the job log); Validate cuts its
update-check section to what the cap leaves instead of dropping it. Check mode
of CheckForUpdates passes no write token, as Validate. -SummaryLimit and
-PublishCommand are test seams; the suite covers the push and pull-request
failures, the failing plan's failure kind and a cut inside a fence. Docs:
direct commits have no duplicate guard, the exchange order in ARCHITECTURE
7.3, the installed template read with the same token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Arthurvdv Arthurvdv added the enhancement New feature or request label Oct 7, 2026
Group-Object sorts its groups by name, so the effective-diff tables of the
update pull request and of the Validate summary came out in endpoint-name
order (complete before recommended). Both now keep the order of the diff,
which is the settings order. update-mechanics.md section 12 records the live
run of 2026-10-07.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Arthurvdv

Copy link
Copy Markdown
Member Author

Live run (2026-10-07, C1 steps 5 to 8)

Template Arthurvdv/rulebook-e2e-template, organization repository Arthurvdv/rulebook-e2e-update, a GitHub App as GHTOKENWORKFLOW. Full table in docs/reference/update-mechanics.md section 12.

  • Run 1 37626804285: secrets[steps.settings.outputs.secretName] resolved (Part D risk 1 closed), App token exchanged, PR #1 with templateSha and {TEMPLATEURL} only, label rulebook; no token in the log. Run 2: "No updates available" (AC1).
  • Template level change: Validate on a PR warns "Updates available (11 files)", exit 0 (AC10); PR #3 with the level file and recommended.*, strict.*, complete.*, essential.* untouched (AC2); a second dispatch: "Pull request already exists" (AC8).
  • Template stage change plus an override, a quarantine entry and MyNightly.yaml in the org: PR #4 with only stages/ci.json, the three *.ci endpoints and the settings (AC3); the override wins (AC6); the org files untouched (AC7).
  • update.schedule adds and removes schedule: (PRs #5, #6); directCommit under a ruleset that requires PRs falls back to PR #7.
  • Engine fix from the run: the effective-diff tables were in endpoint-name order (Group-Object sorts); now settings order (bf8c373).
  • AC9 (no secret) pending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Arthurvdv

Copy link
Copy Markdown
Member Author

AC9 (2026-10-07): with GHTOKENWORKFLOW deleted, run 37645738825 failed in the action step with "The GHTOKENWORKFLOW secret is needed to update system files. Read https://github.com/ALCops/rulebook/blob/main/docs/ghtokenworkflow.md", before any request. Recorded in update-mechanics.md section 12 (b34cc23). All acceptance criteria of the live run are met.

Arthurvdv and others added 2 commits October 7, 2026 17:58
GitHub forbids secret names that start with GITHUB_ (any case). The settings
schema pattern and the settings step of UpdateRulebookSystemFiles.yaml now
reject them too, with fixtures on both sides. CheckForUpdates no longer falls
back to GHTOKENWORKFLOW when the setting is present but invalid: update mode
fails with failure=token and names the configured value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… branch

When the pull request cannot be opened after the push, the message now always
starts with "Branch <name> was pushed." followed by the API message, which
carries the tree link; the link is added only when it is missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Arthurvdv
Arthurvdv merged commit f878ed8 into main Oct 7, 2026
4 checks passed
@Arthurvdv
Arthurvdv deleted the wp07/update branch October 7, 2026 17:29
Arthurvdv added a commit to ALCops/rulebook that referenced this pull request Oct 7, 2026
User pages for the Update Rulebook System Files workflow, the user side of WP07 (engine side: ALCops/rulebook-engine#66, merged).

- docs/updating.md (new): what an update changes (replaced, kept, regenerated, never touched, site files), running it, how Validate reports an update and when the check is skipped, the pull request (title, branch, labels, body sections, when a second one appears), unusedRulebookFiles as paths, update.schedule, direct commit and its fallback, troubleshooting with the messages as observed or taken from the code.
- docs/ghtokenworkflow.md (new): why GITHUB_TOKEN is not enough, a GitHub App step by step with the one-liner for the compressed JSON, personal access tokens, repository and organization secrets, reuse with AL-Go, ghTokenWorkflowSecretName and its name rule, a private template, the failure messages.
- docs/README.md: both pages listed; al-go.md and troubleshooting.md rows point at ghtokenworkflow.md for the secret.
- README.md: section 1 links both pages.

Review: three Sonnet code-review rounds at effort high, 29 findings, all fixed; two led to engine changes on ALCops/rulebook-engine#66.

Part of ALCops/rulebook-engine#9

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WP07: Update system files

1 participant