diff --git a/CHANGELOG.md b/CHANGELOG.md index 95bd776..ba82bc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,23 @@ Versioning. estate, and remove the `--channel` flag and evidence inputs from `gds-release-builder` and the release-candidate command. +## [0.9.20] - 2026-10-03 + +- Provider-account tenancy (ADR 0040): a tenant is the provider account — one + personal account plus each organization by its exact login. Purpose-named + portfolios such as `servers` and `forks` are no longer tenancy axes. +- `include[].match` in the device schema: repositories are placed by facts — + `owner_login`, `names`, `name_prefixes`, `visibility`, `lifecycle` — and the + first matching include wins by declaration order. `selector` includes keep + working unchanged. +- `match.name_prefixes` in the policy schema: a policy can match repository + names (for example `server-*`) without a synthetic portfolio. +- `PLACEMENT_AMBIGUOUS` is retired: ordered first-match-wins replaces the + ambiguity error. +- `match.fork` and `fork_portfolio` remain parseable for compatibility but are + no longer consulted (ADR 0040); the selector matcher reads owner, + name prefixes, lifecycle and visibility only. + ## [0.9.7] - 2026-09-19 - Skip hidden directories during workspace discovery so tool-state and diff --git a/docs/adr/README.md b/docs/adr/README.md index a9a7de8..a22b106 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -8,7 +8,7 @@ clause stops being normative. | ADR | Title | Status | Supersedes | Superseded by | |---|---|---|---|---| -| [0040](0040-tenancy-is-the-provider-account.md) | Tenancy is the provider account | Accepted | — | — | +| [0040](0040-tenancy-is-the-provider-account.md) | Tenancy is the provider account | Accepted | ADR 0026 (in part), ADR 0028 (in part), ADR 0032 (in part) | — | | [0039](0039-repo-scoped-session-evidence.md) | Repo-scoped session evidence | Accepted | — | — | | [0038](0038-release-identity-carries-no-channel.md) | Release identity carries no channel | Accepted | ADR 0016 | — | | [0037](0037-seven-harnesses-one-per-setup-system.md) | Seven harnesses, one per setup system | Accepted | ADR 0011 | — | @@ -16,12 +16,12 @@ clause stops being normative. | [0035](0035-agent-first-explicit-control-plane.md) | Agent-first explicit control plane and evidence-bound mutation | Accepted | — | — | | [0034](0034-gh-cli-credential-provider.md) | gh CLI credential provider and permission superset contract | Accepted | — | — | | [0033](0033-return-the-control-plane-to-private.md) | Return the control plane to a private repository | Accepted | — | — | -| [0032](0032-flat-forks-workspace-root.md) | Place every fork checkout in one flat forks workspace root | Accepted | ADR 0018, ADR 0026 | — | +| [0032](0032-flat-forks-workspace-root.md) | Place every fork checkout in one flat forks workspace root | Accepted | ADR 0018, ADR 0026 | ADR 0040 (in part) | | [0030](0030-solo-owner-merges-without-human-review.md) | Let the sole owner's agent merge and clean up without a second reviewer | Accepted | — | — | | [0029](0029-harness-applications-are-out-of-estate-scope.md) | Keep harness application versions out of estate scope | Accepted | — | — | -| [0028](0028-servers-are-a-first-class-portfolio-entity.md) | Make servers a first-class portfolio entity | Accepted | — | — | +| [0028](0028-servers-are-a-first-class-portfolio-entity.md) | Make servers a first-class portfolio entity | Accepted | — | ADR 0040 (in part) | | [0027](0027-submodule-repositories-have-no-standalone-checkout.md) | Give a submodule-consumed repository no standalone checkout | Accepted | ADR 0018 | — | -| [0026](0026-flat-servers-workspace-root.md) | Place every server checkout in one flat servers workspace root | Accepted | ADR 0025 | ADR 0032 (in part) | +| [0026](0026-flat-servers-workspace-root.md) | Place every server checkout in one flat servers workspace root | Accepted | ADR 0025 | ADR 0032 (in part), ADR 0040 (in part) | | [0025](0025-out-of-estate-external-workspace-root.md) | Keep third-party collaboration checkouts in an out-of-estate external root | Accepted | — | ADR 0026 (in part) | | [0024](0024-device-local-estate-registration.md) | Resolve the control plane through a device-local estate registration | Accepted | — | — | | [0023](0023-separate-github-mutation-capability.md) | Separate GitHub mutation capability and repository-bound writes | Accepted | — | — | diff --git a/tests/fixtures/schemas/v1/cases.json b/tests/fixtures/schemas/v1/cases.json index c85a2da..1b9b6dc 100644 --- a/tests/fixtures/schemas/v1/cases.json +++ b/tests/fixtures/schemas/v1/cases.json @@ -1,16 +1,71 @@ { "schema_version": 1, "cases": [ - {"id":"valid-trust-policy","schema":"trust-policy","path":"valid-trust-policy.yaml","valid":true}, - {"id":"invalid-trust-policy-secret-key","schema":"trust-policy","path":"invalid-trust-policy-secret-key.yaml","valid":false,"expected_code":"GDS_INSTANCE_INVALID"}, - {"id":"valid-approval","schema":"approval","path":"valid-approval.json","valid":true}, - {"id":"invalid-approval-signature","schema":"approval","path":"invalid-approval-signature.json","valid":false,"expected_code":"GDS_INSTANCE_INVALID"}, - {"id":"valid-plan-enablement","schema":"plan-enablement","path":"valid-plan-enablement.json","valid":true}, - {"id":"invalid-plan-enablement-partial-consume","schema":"plan-enablement","path":"invalid-plan-enablement-partial-consume.json","valid":false,"expected_code":"GDS_INSTANCE_INVALID"}, - {"id":"valid-freshness-policy","schema":"freshness-policy","path":"valid-freshness-policy.yaml","valid":true}, - {"id":"invalid-freshness-cached-mutation","schema":"freshness-policy","path":"invalid-freshness-cached-mutation.yaml","valid":false,"expected_code":"GDS_INSTANCE_INVALID"}, - {"id":"valid-field-ownership","schema":"field-ownership","path":"valid-field-ownership.json","valid":true}, - {"id":"invalid-field-ownership-drop-unknown","schema":"field-ownership","path":"invalid-field-ownership-drop-unknown.json","valid":false,"expected_code":"GDS_INSTANCE_INVALID"}, + { + "id": "valid-trust-policy", + "schema": "trust-policy", + "path": "valid-trust-policy.yaml", + "valid": true + }, + { + "id": "invalid-trust-policy-secret-key", + "schema": "trust-policy", + "path": "invalid-trust-policy-secret-key.yaml", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + }, + { + "id": "valid-approval", + "schema": "approval", + "path": "valid-approval.json", + "valid": true + }, + { + "id": "invalid-approval-signature", + "schema": "approval", + "path": "invalid-approval-signature.json", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + }, + { + "id": "valid-plan-enablement", + "schema": "plan-enablement", + "path": "valid-plan-enablement.json", + "valid": true + }, + { + "id": "invalid-plan-enablement-partial-consume", + "schema": "plan-enablement", + "path": "invalid-plan-enablement-partial-consume.json", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + }, + { + "id": "valid-freshness-policy", + "schema": "freshness-policy", + "path": "valid-freshness-policy.yaml", + "valid": true + }, + { + "id": "invalid-freshness-cached-mutation", + "schema": "freshness-policy", + "path": "invalid-freshness-cached-mutation.yaml", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + }, + { + "id": "valid-field-ownership", + "schema": "field-ownership", + "path": "valid-field-ownership.json", + "valid": true + }, + { + "id": "invalid-field-ownership-drop-unknown", + "schema": "field-ownership", + "path": "invalid-field-ownership-drop-unknown.json", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + }, { "id": "valid-assurance-report", "schema": "assurance-report", @@ -273,6 +328,12 @@ "path": "valid-device.yaml", "valid": true }, + { + "id": "valid-device-include-match", + "schema": "device", + "path": "valid-device-include-match.yaml", + "valid": true + }, { "id": "valid-source-register", "schema": "source-register", @@ -515,7 +576,18 @@ "valid": false, "expected_code": "GDS_INSTANCE_INVALID" }, - {"id":"valid-session-evidence","schema":"session-evidence","path":"valid-session-evidence.json","valid":true}, - {"id":"invalid-session-evidence-missing-fields","schema":"session-evidence","path":"invalid-session-evidence-missing-fields.json","valid":false,"expected_code":"GDS_INSTANCE_INVALID"} + { + "id": "valid-session-evidence", + "schema": "session-evidence", + "path": "valid-session-evidence.json", + "valid": true + }, + { + "id": "invalid-session-evidence-missing-fields", + "schema": "session-evidence", + "path": "invalid-session-evidence-missing-fields.json", + "valid": false, + "expected_code": "GDS_INSTANCE_INVALID" + } ] } diff --git a/tests/fixtures/schemas/v1/valid-device-include-match.yaml b/tests/fixtures/schemas/v1/valid-device-include-match.yaml new file mode 100644 index 0000000..37b97c4 --- /dev/null +++ b/tests/fixtures/schemas/v1/valid-device-include-match.yaml @@ -0,0 +1,47 @@ +schema_version: 1 + +device: + id: "device_01JEXAMPZ00000000000000004" + name: "example-workstation-match" + os: "linux" + architecture: "x86_64" + +workspace_roots: + personal: "${HOME}/Developer/personal" + organization: "${HOME}/Developer/organization" + servers: "${HOME}/Developer/servers" + +materialization: + default_mode: "absent" + include: + - match: + owner_login: "example-organization" + name_prefixes: + - "server-" + lifecycle: + - "active" + - "maintenance" + workspace_root: "servers" + mode: "active" + - match: + owner_login: "example-organization" + workspace_root: "organization" + mode: "active" + - match: + owner_login: "example-user" + visibility: + - "private" + workspace_root: "personal" + mode: "active" + +harnesses: + - "codex" + +state: + path: "${XDG_STATE_HOME}/github-device-sync" + +repositories: + - provider: "example-organization/server-example" + workspace_root: "servers" + path: "servers/server-example" + materialization: "checkout"