Skip to content

WP09: ChangeRule workflow, Rulebook.Edit change-set engine and the ChangeRule action - #76

Merged
Arthurvdv merged 8 commits into
mainfrom
wp09/change-rule
Oct 9, 2026
Merged

Arthurvdv merged 8 commits into
mainfrom
wp09/change-rule

Conversation

@Arthurvdv

@Arthurvdv Arthurvdv commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

  • New module modules/Rulebook.Edit (the change-set engine of D32): overrides.json I/O in the template layout (byte-identical round trip; comments and trailing commas read, a date-like justification stays text), Set-RulebookOverride / Remove-RulebookOverride (one entry per selector set, replaced in place by order-insensitive selector equality; duplicates collapse into the effective entry at its position; a remove miss lists the id's entries), Test-RulebookChangeSet, Invoke-RulebookChangeSet (all or nothing on a candidate copy, never the repository; one row per matching endpoint with provenance; the no-op rule of D48, dead new entries dropped also in multi-item sets), the title, table, pull request body and job summary (the git-based effective diff in the summary only), and Publish-RulebookChange (change-rule/<ruleId>/<yyMMddHHmmss>, base-moved guard, direct commit with the pull-request fallback).
  • New action actions/ChangeRule: a one-item client of the engine. The token guard runs after the local plan, so an invalid id or a no-op needs no secret; the token is exchanged and masked before any request. Outputs result (pull-request, direct-commit, no-op), noop, changedEndpoints, pullRequestUrl, branch, failure.
  • New template workflow template/.github/workflows/ChangeRule.yaml: inputs ruleId, action (incl. Remove), levels, stages, justification; no directCommit input, landing follows commitOptions.createPullRequest (D47); contents: read; choice lists rewritten by the update (D30), byte-identical with the shipped settings. Template now 44 files, 12 hand-written. The fixture workflows rename id to ruleId.
  • CI job changerule-action on a copy of valid-minimal: token failure, no-op, invalid id, and a guard on the three outcomes.
  • Tests: Rulebook.Edit.Tests.ps1 (61), ChangeRule.Action.Tests.ps1 (44), the shipped-workflow rewrite case in the Update suite, the template file list.
  • Docs: docs/reference/change-mechanics.md (the live-run section follows the E2E), ADRs D47 and D48, ARCHITECTURE 7.5/7.6 and the new 5.8, naming, template content, effective diff, update mechanics, README, template README, CONTRIBUTING (incl. the ruleset JSON with changerule-action).

Decisions

Verification: Pester 1601 tests (1600 passed, 1 skipped: the Actions-only smoke test), PSScriptAnalyzer clean, Test-Rulebook V1 to V14 clean, Build-Template -WhatIf current.

Review: three Sonnet rounds at effort high. Round 1: two must-fix (dead new entries were written; the empty-justification rule never ran because a [string] parameter turns $null into '') plus the empty-selector binding error, single-line justification in body and summary, the no-changes result mapped to no-op, the base-guard warning, an unreadable candidate as a validation failure, one entry per selector set, and no GITHUB_TOKEN for the action. Round 2: one must-fix (the summary lost its tables for an empty effective diff, an if expression unrolled @() to $null) plus deduplicated as its own outcome, the row-based no-op notice, dead entries dropped in multi-item sets, empty changedEndpoints for a no-op, repeated slugs kept once, and the "given but not stored" justification line. Round 3: one must-fix (collapsing duplicates kept the first position and could flip precedence on a specificity tie; the survivor now takes the effective, last position) plus rows of dead entries computed against the final state, a row-aware no-op sentence, the summary assertion at action level, ADR 0048 wording, and comments and trailing commas in overrides.json. Not applied: the settings step hard-fails on an invalid ghTokenWorkflowSecretName before the action runs (the same as the Update and Scan workflows; consistency wins); the concurrency queue replacing a waiting run (documented in change-mechanics section 9); the Validate default flip (intended, #74); a tag as ref_name on a direct commit (the form is dispatched from a branch); the multi-item no-op notice naming the first item only (WP15); the quadratic body rebuild, the duplicated secret-name regex and other performance notes.

Live run: all eleven E2E steps and the refused-push fallback observed as designed on Arthurvdv/rulebook-e2e-changerule; the table with run ids and PR links is in docs/reference/change-mechanics.md section 11. Close-out with the acceptance-criteria evidence and the deviations: #11 (comment)

User docs: ALCops/rulebook#6 (merge after this pull request).

Spin-off: #75

Closes #11

🤖 Generated with Claude Code

Arthurvdv and others added 7 commits October 8, 2026 21:54
The Change Rule workflow (#11) and later the dashboard write path (WP15)
need one function that applies a change set to overrides.json, all or
nothing, and lands it. Rulebook.Edit reads and writes overrides.json in the
template layout (byte-identical round trip), sets entries in place by
order-insensitive selector equality and removes them with the existing
entries named on a miss, validates a change set against the settings and the
catalog, and plans it on a candidate copy so the repository itself is never
written. The plan has one row per matching endpoint with provenance and the
no-op rule of D48 (no endpoint and not the file changes); the rendering
covers the table, the pull request body and the job summary with the
effective diff; Publish-RulebookChange pushes change-rule/<id>/<timestamp> or
a direct commit with the fallback of D47.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ChangeRule.ps1 builds a one-item change set from the form, plans it, and
only then checks the token, so an invalid id or a no-op answers without the
secret; a real change exchanges and masks the token and lands per
commitOptions.createPullRequest (D47). The template ChangeRule.yaml has the
five inputs, no directCommit input, a read-only workflow token and choice
lists laid out so the update's rewrite (D30) reproduces it with the shipped
settings. The fixture workflows rename the input id to ruleId. The CI job
changerule-action proves the token failure, the no-op and the invalid id on
a copy of valid-minimal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…write

Rulebook.Edit.Tests covers the overrides I/O, set, replace and remove, every
change set check, the acceptance criteria of #11 on valid-minimal and the
publish against a bare repository. ChangeRule.Action.Tests covers action.yaml,
the template workflow with its settings step run in-process, and the entry
script including a bare remote. The Update suite checks the rewrite of the
shipped ChangeRule.yaml; the Template suite counts 12 hand-written files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs/reference/change-mechanics.md is the contract of the change set, the
replace and no-op rules, the plan, the table and the landing, with a
placeholder for the live run. ADR D47 (ChangeRule follows
commitOptions.createPullRequest, no directCommit input) and D48 (no-op
changes are reported and never written). ARCHITECTURE 7.5 links the contract
and gains section 5.8, the naming, template content, effective diff and
update references, README, template README and CONTRIBUTING name the action,
the module, the 44 template files and the CI job.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ries, guards

Arthur decided on 2026-10-08 that an empty justification keeps the entry's
text; only a non-empty one replaces it. A new entry that changes no endpoint
(it repeats what the base or a broader entry gives) is now a no-op and is not
written, as #11 asks. Set keeps one entry per selector set when a hand edit
left duplicates. The justification and the note are written on one line in
the body and the summary, and a remove-only change has no justification
paragraph. An empty levels or stages input reaches the validation finding
instead of a binding error, an unreadable candidate is a validation failure,
nothing to commit after the plan is reported as a no-op, and a missing
checkout head warns that the base-move guard is off. The action no longer
gets GITHUB_TOKEN. The CI no-op step passes no justification again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tries

The job summary of a change with an empty effective diff (a justification-only
edit, a direct commit that changes no endpoint) lost its tables because an if
expression unrolled the empty diff to null; it is wrapped now. Collapsing
duplicate entries of the effective entry is its own outcome, deduplicated,
with the note "duplicate entries removed" instead of "justification updated".
A dead new entry is dropped before overrides.json is written also when
another item of the set changes something, and the others are recomputed. The
no-op notice is worded from the rows, so a more specific entry that keeps
another action is named; a justification given for a dead entry says it was
not stored. Selectors accept an exactly empty value (validation finding) and
keep a repeated slug once; a no-op outputs no changed endpoints.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…state rows

Collapsing duplicate entries kept the first position, which could flip the
precedence on a specificity tie (the later entry wins); the survivor now
takes the position of the last duplicate, the effective one. The rows of a
dead new entry are computed against the final state, after the entry is
dropped, so they show what really decides each endpoint, and its sentence is
worded from the rows when a more specific entry keeps another action. The
overrides reader accepts comments and trailing commas as ConvertFrom-Json
does. ADR 0048 names the deduplicated outcome and the two-pass drop, and the
action suite asserts the summary of an empty effective diff.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Arthurvdv Arthurvdv added the enhancement New feature or request label Oct 8, 2026
The E2E on Arthurvdv/rulebook-e2e-changerule ran steps 1 to 11 of the WP09
plan against the branch code: the invalid id, the token guard, AC1 to AC6,
the justification-only edit, the remove miss, the direct commit and its
fallback under a ruleset, and the choice-list rewrite after adding a level.
The table names the runs, pull requests and observed outcomes, including the
refused push showing as a log warning rather than an annotation and the new
level landing in settings order.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Arthurvdv
Arthurvdv merged commit 15f1503 into main Oct 9, 2026
6 checks passed
@Arthurvdv
Arthurvdv deleted the wp09/change-rule branch October 9, 2026 10:14
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.

WP09: Change-rule workflow

1 participant