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
12 changes: 6 additions & 6 deletions .gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ bundle:
version: "0.8.0-dev"
release_sequence: 0
channel: "development"
source_tree_digest: "sha256:a2b5ad7bdb6a7198d0de86e3de879062c71df5e86ff2f257257693aee319aa4a"
digest: "sha256:010d85cbeeeb4b010e438b0ffcbe5c9211f86d8f170b66e187aa35b23480fc17"
source_tree_digest: "sha256:27c680c6d2fa6dbdb297d8902b19bccd2e9114449d0494fd4866df78b149631e"
digest: "sha256:71191a1e73172a41ffb3a1d1c17a97306d9fd7cc488a9d023f29513e5329c097"

projection:
input_digest: "sha256:f914cbb8b69051475867d7fe14977debf309cb4ee1bd9bcddd6e64c7098bd886"
output_digest: "sha256:0a7d6b71492567ed5728b5b49c40225fab549fc3f7c252920f781aa9a671aa3a"
input_digest: "sha256:66e0e6e6b2788574f5a52479e921db822414f730671be57ed68aa96836aa7ac7"
output_digest: "sha256:9a70d43a686d9e18925ed01d4e078b4d21a31a7600486e28ce06b92da4347479"
files:
- path: ".gds/compiled-policy.json"
digest: "sha256:b5517ed46f67866220c2b18dbfbda4a40d99f00327611e56742a118d0ac59d0b"
digest: "sha256:807282f820294914e1c7e6ad1bf27c54a799d56305c58630254ab50ab286f379"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:1da9d1ccf7c40ec6328f2c957aeb910c9de68ab91d8e07917d1486165cf209cc"
digest: "sha256:f164762300a774d716fcee22720a25c42c8c3fb142bf6546286b34170b1cdc89"
20 changes: 19 additions & 1 deletion .gds/compiled-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"compiled_policy": {
"repository_id": "repo_01M0EZ7TB3KNXNSP78Z8M64WXG",
"bundle_version": "0.8.0-dev",
"digest": "sha256:13fde780e078b9143e913db7cf706c8908b52c21ec51f28d26d3c9fdd715e080"
"digest": "sha256:f901d1860f277a0262d83c6fa06a806f7740677fb14576bbf5816c69af565974"
},
"sources": [
{
Expand All @@ -22,6 +22,14 @@
"path": "policies/roles/public-module.yaml",
"digest": "sha256:084f7b09dbfd85a386e47cf83ea0015154b6b01fa3cf85a68f04ef407b89ed4b"
},
{
"id": "continuous-development",
"tier": "stack",
"priority": 500,
"distribution": "public",
"path": "policies/stacks/continuous-development.yaml",
"digest": "sha256:678b6876cd6cf58d7b97e7e0751e7ec9c0d3f75c2c78c5bfbe0d02930774195d"
},
{
"id": "github-device-sync",
"tier": "repository",
Expand All @@ -42,6 +50,9 @@
"context": {
"private_parent_persistence": "forbidden"
},
"delivery": {
"profile": "continuous-development"
},
"git": {
"branch_cleanup": "merged-only",
"default_branch": "main",
Expand Down Expand Up @@ -188,6 +199,13 @@
"file": "policies/base/repository-default.yaml",
"operation": "set"
},
"/effective/delivery/profile": {
"source": "continuous-development",
"tier": "stack",
"priority": 500,
"file": "policies/stacks/continuous-development.yaml",
"operation": "set"
},
"/effective/git/branch_cleanup": {
"source": "repository-default",
"tier": "base",
Expand Down
1 change: 1 addition & 0 deletions .gds/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ policy:
- "repository-default"
- "public-module"
- "github-device-sync"
- "continuous-development"
rollout_ring: "standard"

git:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/gds-ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# generator: gds
# bundle: 0.8.0-dev
# source-tree-digest: sha256:a2b5ad7bdb6a7198d0de86e3de879062c71df5e86ff2f257257693aee319aa4a
# input-digest: sha256:f914cbb8b69051475867d7fe14977debf309cb4ee1bd9bcddd6e64c7098bd886
# source-tree-digest: sha256:27c680c6d2fa6dbdb297d8902b19bccd2e9114449d0494fd4866df78b149631e
# input-digest: sha256:66e0e6e6b2788574f5a52479e921db822414f730671be57ed68aa96836aa7ac7
# output-digest: sha256:4a875d4ed62fe6b2d279515f9a19274e159ae8ed58f3a1a0843c60a49f663886
# edit-source:
# - .gds/repository.yaml
# - policies/base/repository-default.yaml
# - policies/repositories/github-device-sync.yaml
# - policies/roles/public-module.yaml
# - policies/stacks/continuous-development.yaml
# - templates/agents/repository.md.tmpl
# - templates/github-actions/go.yml.tmpl
# - templates/harnesses/claude.md.tmpl
Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ checkout.
- Keep secrets, runtime state, caches, logs and generated evidence untracked.
- Generated fixtures under `tests/golden` change only through their generator.

## Continuous development

This repository selects the continuous-development policy. Treat broad GitHub
CI as asynchronous evidence for ordinary integration, preserve actual outcomes,
and run checks relevant to the changed behavior. Release integrity, dependency
identity and provider transaction controls remain required.

## Verification

- Lint: `scripts/validate_shell.sh`.
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Versioning.

## [Unreleased]

- Select continuous development for the GDS repository itself and document
explicit ruleset removal plus cautious readback after ambiguous write errors.
Generic policy defaults remain opt-in for other consumers.

- Publish unsuccessful completed self-workflow attempts as unassigned,
repository-local CI evidence; preserve actual conclusions and exact attempts.

Expand Down
51 changes: 24 additions & 27 deletions docs/runbooks/github-ruleset-reconcile.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
# Runbook: reconciling the default-branch ruleset

Status: local implementation runbook. Applying mutates live branch protection on
the control-plane repository; read the whole document before the first apply.
the selected repository; read the whole document before the first apply.

`gds github ruleset` is the only supported way to change the tracked
default-branch ruleset. Editing it in the GitHub UI leaves the tracked contract
and live state disagreeing with nothing to reconcile them.

## What the command owns, and what it must not touch

GDS owns the `required_status_checks` rule **wholesale**: the desired state it
sends replaces the live list entirely. A context missing from the desired state
is a context deleted.
When a desired `required_status_checks` rule is present, GDS owns its contents
wholesale. An omitted rule is preserved unless the compiled delivery profile
projects explicit `remove_required_status_checks` intent into the plan.

That matters because the tracked contract at `.github/rulesets/branch-main.json`
holds only what `generate_required_checks.py` produces. It has to — the generator
compares against that file and would otherwise report every platform-emitted
context as drift. Contexts the platform emits therefore never appear there.
The repository-owned baseline is `.github/rulesets/branch-main.json`.
`requirements/external-required-checks.json` adds contexts with declared owners
to that baseline. A duplicate generated context or an entry without a context
name and owner is rejected.

`requirements/external-required-checks.json` closes the gap. Every context that
lives on the ruleset but is not generated by GDS is declared there with its
owner, and the planner merges those into the desired state so a reconcile carries
them through. Two rules keep the declaration honest:

- a declaration naming a context the generator already produces is rejected —
otherwise two sources of truth claim the same context with no way to tell which
wins;
- an entry missing either its name or its owner is rejected, because an unowned
exception is indistinguishable from a typo.
Selecting `continuous-development` deliberately removes the entire required
status-check rule from the selected repository ruleset, including declared
external contexts. Review the plan's explicit removal flag and exact before/after
state. PR/signature rules, other rulesets, classic protection and environment
gates remain separate contracts; profile selection alone performs no write.

Before any apply, read the plan's own diff rather than trusting either file:

Expand Down Expand Up @@ -102,9 +97,10 @@ re-approving rather than overwriting.

**`GDS_OPERATION_STEP_FAILED`, mutation attempted, not completed.** Preconditions
verified and the handler was called. The operation journal records the step
failure but not the provider's error text, so diagnose from the mutation runtime:
check `gds context` for `mutations.runtime` first. Live state is unchanged —
the engine does not leave partial ruleset writes.
failure. Check `gds context` for `mutations.runtime` and read the live ruleset
again before deciding whether to retry. A failed response or post-write read
can follow an accepted provider update; failure alone does not prove unchanged
live state. Never blindly repeat the write.

Read any operation's journal with:

Expand All @@ -117,8 +113,8 @@ faster than re-deriving it.

## Renaming a required context

A workflow change that renames a check — moving a job to a different runner label
renames a required check — needs the live ruleset
A workflow change that renames a check, such as changing a matrix value used in
the job name, needs the live ruleset
swapped **while the pull request is open**, not after it merges:

1. open the pull request carrying the workflow and tracked-ruleset change;
Expand All @@ -134,11 +130,12 @@ trying to land. Other open pull requests will need a branch update afterwards.
## Verification

`gds github ruleset --verify "$OPERATION_ID"` re-reads live state for a completed
operation. For a read-only check at any time, without credentials or a plan:
operation. To observe drift without a provider write, use the same configured
runtime and exact repository options:

```bash
python3 scripts/report_ruleset_drift.py --ruleset-id <id>
gds github ruleset --plan "${COMMON[@]}" --json
```

It separates owned drift from externally owned contexts and exits non-zero only
on the former.
This may store a local plan when drift exists. It does not apply that plan;
provider writing still requires its signed approval and one-shot enablement.