Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ covers that case).
## Configuring branch protection: don't hand-build the rules

Delete **all** classic branch-protection rules and stray rulesets because rulesets are the only
protection mechanism. Create **exactly two rulesets named `develop` and `main`** from the hub's
`repo-config/*.json` payloads. Run `repo-config/configure.sh apply <owner>/<repo>
release|operational` from a hub checkout at `main`. The names are load-bearing because governance
content and workflows reference them. The registry `workflowModel` selects the `develop` payload
for a registered repository. Pass the model explicitly for a repository outside the registry.
See `repo-config/README.md` "Rulesets" for the configured state.
protection mechanism. From a hub checkout at `main`, create **exactly two rulesets named `develop`
and `main`** from the hub's `repo-config/*.json` payloads. Run
`repo-config/configure.sh apply <owner>/<repo> release|operational` from that checkout. The names
are load-bearing because governance content and workflows reference them. The registry
`workflowModel` selects the `develop` payload for a registered repository. Pass the model
explicitly for a repository outside the registry. See the hub's `repo-config/README.md`
"Rulesets" for the configured state.

## Executing a `develop -> main` promotion safely

Two traps, both learned the hard way:

- **Never delete `develop`.** A promotion PR's head *is* `develop`, so `gh pr merge --delete-branch`
(and a repo's "Automatically delete head branches" toggle, kept off in `repo-config/settings.json`
for exactly this reason) deletes `develop` itself. Merge a promotion with a plain
`gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way, restore it to
the merged PR's head SHA, which is still reachable as the merge commit's second parent:
(and a repo's "Automatically delete head branches" toggle, kept off in the hub's
`repo-config/settings.json` for exactly this reason) deletes `develop` itself. Merge a promotion
with a plain `gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way,
restore it to the merged PR's head SHA, which is still reachable as the merge commit's second parent:
`gh api -X POST "repos/<owner>/<repo>/git/refs" -f ref=refs/heads/develop -f sha="$(gh pr view <n> --json headRefOid --jq .headRefOid)"`.
- **Spurious EOL-only conflicts resolve by taking `develop`.** When `develop`'s `.editorconfig`
line-ending default has changed (for example the fleet-wide CRLF-to-LF flip) while `main` hasn't
Expand Down Expand Up @@ -105,5 +106,5 @@ supplies its own input-deterministic generator and wires the codegen reference w

`actions/create-github-app-token` deprecated the numeric `app-id` input in v3.0.0. Use
`client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }}`. When adding new App-token call sites, use the
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See `repo-config/README.md`
"Secrets" for which secrets each mechanism needs.
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See the hub's
`repo-config/README.md` "Secrets" for which secrets each mechanism needs.
2 changes: 1 addition & 1 deletion .claude-plugin/fleet-skills/.source-digest
Original file line number Diff line number Diff line change
@@ -1 +1 @@
33837ae6c41ef01f
096bcb76aa0ac4ae
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ covers that case).
## Configuring branch protection: don't hand-build the rules

Delete **all** classic branch-protection rules and stray rulesets because rulesets are the only
protection mechanism. Create **exactly two rulesets named `develop` and `main`** from the hub's
`repo-config/*.json` payloads. Run `repo-config/configure.sh apply <owner>/<repo>
release|operational` from a hub checkout at `main`. The names are load-bearing because governance
content and workflows reference them. The registry `workflowModel` selects the `develop` payload
for a registered repository. Pass the model explicitly for a repository outside the registry.
See `repo-config/README.md` "Rulesets" for the configured state.
protection mechanism. From a hub checkout at `main`, create **exactly two rulesets named `develop`
and `main`** from the hub's `repo-config/*.json` payloads. Run
`repo-config/configure.sh apply <owner>/<repo> release|operational` from that checkout. The names
are load-bearing because governance content and workflows reference them. The registry
`workflowModel` selects the `develop` payload for a registered repository. Pass the model
explicitly for a repository outside the registry. See the hub's `repo-config/README.md`
"Rulesets" for the configured state.

## Executing a `develop -> main` promotion safely

Two traps, both learned the hard way:

- **Never delete `develop`.** A promotion PR's head *is* `develop`, so `gh pr merge --delete-branch`
(and a repo's "Automatically delete head branches" toggle, kept off in `repo-config/settings.json`
for exactly this reason) deletes `develop` itself. Merge a promotion with a plain
`gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way, restore it to
the merged PR's head SHA, which is still reachable as the merge commit's second parent:
(and a repo's "Automatically delete head branches" toggle, kept off in the hub's
`repo-config/settings.json` for exactly this reason) deletes `develop` itself. Merge a promotion
with a plain `gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way,
restore it to the merged PR's head SHA, which is still reachable as the merge commit's second parent:
`gh api -X POST "repos/<owner>/<repo>/git/refs" -f ref=refs/heads/develop -f sha="$(gh pr view <n> --json headRefOid --jq .headRefOid)"`.
- **Spurious EOL-only conflicts resolve by taking `develop`.** When `develop`'s `.editorconfig`
line-ending default has changed (for example the fleet-wide CRLF-to-LF flip) while `main` hasn't
Expand Down Expand Up @@ -105,5 +106,5 @@ supplies its own input-deterministic generator and wires the codegen reference w

`actions/create-github-app-token` deprecated the numeric `app-id` input in v3.0.0. Use
`client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }}`. When adding new App-token call sites, use the
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See `repo-config/README.md`
"Secrets" for which secrets each mechanism needs.
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See the hub's
`repo-config/README.md` "Secrets" for which secrets each mechanism needs.
12 changes: 11 additions & 1 deletion .github/actions/prose-gate/prose_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,17 @@ def changed_lines(base: str, root: Path) -> dict[str, set[int]] | None:
"""
try:
d = subprocess.run(
["git", "-C", str(root), "diff", "--unified=0", "--no-color", base, "--"],
[
"git",
"-C",
str(root),
"diff",
"--unified=0",
"--no-color",
"--ignore-cr-at-eol",
base,
"--",
],
capture_output=True,
text=True,
check=True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ covers that case).
## Configuring branch protection: don't hand-build the rules

Delete **all** classic branch-protection rules and stray rulesets because rulesets are the only
protection mechanism. Create **exactly two rulesets named `develop` and `main`** from the hub's
`repo-config/*.json` payloads. Run `repo-config/configure.sh apply <owner>/<repo>
release|operational` from a hub checkout at `main`. The names are load-bearing because governance
content and workflows reference them. The registry `workflowModel` selects the `develop` payload
for a registered repository. Pass the model explicitly for a repository outside the registry.
See `repo-config/README.md` "Rulesets" for the configured state.
protection mechanism. From a hub checkout at `main`, create **exactly two rulesets named `develop`
and `main`** from the hub's `repo-config/*.json` payloads. Run
`repo-config/configure.sh apply <owner>/<repo> release|operational` from that checkout. The names
are load-bearing because governance content and workflows reference them. The registry
`workflowModel` selects the `develop` payload for a registered repository. Pass the model
explicitly for a repository outside the registry. See the hub's `repo-config/README.md`
"Rulesets" for the configured state.

## Executing a `develop -> main` promotion safely

Two traps, both learned the hard way:

- **Never delete `develop`.** A promotion PR's head *is* `develop`, so `gh pr merge --delete-branch`
(and a repo's "Automatically delete head branches" toggle, kept off in `repo-config/settings.json`
for exactly this reason) deletes `develop` itself. Merge a promotion with a plain
`gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way, restore it to
the merged PR's head SHA, which is still reachable as the merge commit's second parent:
(and a repo's "Automatically delete head branches" toggle, kept off in the hub's
`repo-config/settings.json` for exactly this reason) deletes `develop` itself. Merge a promotion
with a plain `gh pr merge --merge`, no `--delete-branch`. If `develop` is ever lost this way,
restore it to the merged PR's head SHA, which is still reachable as the merge commit's second parent:
`gh api -X POST "repos/<owner>/<repo>/git/refs" -f ref=refs/heads/develop -f sha="$(gh pr view <n> --json headRefOid --jq .headRefOid)"`.
- **Spurious EOL-only conflicts resolve by taking `develop`.** When `develop`'s `.editorconfig`
line-ending default has changed (for example the fleet-wide CRLF-to-LF flip) while `main` hasn't
Expand Down Expand Up @@ -105,5 +106,5 @@ supplies its own input-deterministic generator and wires the codegen reference w

`actions/create-github-app-token` deprecated the numeric `app-id` input in v3.0.0. Use
`client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }}`. When adding new App-token call sites, use the
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See `repo-config/README.md`
"Secrets" for which secrets each mechanism needs.
same form, and do not reintroduce `app-id` / `CODEGEN_APP_ID`. See the hub's
`repo-config/README.md` "Secrets" for which secrets each mechanism needs.
23 changes: 23 additions & 0 deletions scripts/tests/test_prose_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,29 @@ def test_a_hunk_before_its_file_header_is_not_attributed_to_the_previous_file(se
"""Reading a stray hunk against whichever file came last invents a scope."""
self.assertEqual({}, self.run_diff("@@ -1 +1 @@\n+orphan\n"))

def test_a_line_ending_only_change_adds_no_lines(self) -> None:
"""Renormalizing CRLF to LF does not make existing prose newly authored."""
root = Path(self.enterContext(tempfile.TemporaryDirectory()))
subprocess.run(["git", "init", "--quiet", str(root)], check=True)
subprocess.run(["git", "-C", str(root), "config", "core.autocrlf", "false"], check=True)
subprocess.run(["git", "-C", str(root), "config", "commit.gpgsign", "false"], check=True)
subprocess.run(["git", "-C", str(root), "config", "user.name", "Test"], check=True)
subprocess.run(
["git", "-C", str(root), "config", "user.email", "test@example.invalid"], check=True
)
bait = root / "bait.md"
bait.write_bytes(b"Existing prose.\r\n")
subprocess.run(["git", "-C", str(root), "add", "bait.md"], check=True)
subprocess.run(["git", "-C", str(root), "commit", "--quiet", "-m", "baseline"], check=True)

bait.write_bytes(b"Existing prose.\n")

self.assertEqual({}, prose_lint.changed_lines("HEAD", root))

bait.write_bytes(b"New prose.\n")

self.assertEqual({"bait.md": {1}}, prose_lint.changed_lines("HEAD", root))

def test_a_git_failure_is_none_rather_than_an_empty_scope(self) -> None:
"""An empty scope filters every file out and reports a clean run, which is a false pass."""
with mock.patch.object(
Expand Down