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
8 changes: 4 additions & 4 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
GENERATED FILE - DO NOT EDIT DIRECTLY
generator: gds
bundle: 0.1.0-dev
source-commit: 4096db90d3a56fe60d43a98deea0eda422a35f72
input-digest: sha256:aa682dc69f657adfe8199181f04e41d7f975179e7a097bebae65768cf7c42c72
output-digest: sha256:398c5b72bcfc9533dadc641e25fd9a51481385304e68f8ed6317e9d40ce335b5
source-commit: 0e64dc713c9045c69f71034f00ed176eca3e34d5
input-digest: sha256:9035c1e5c46301ca11c9335b9fdc582d4ac1faac09b84b9f67c80240a4f3a553
output-digest: sha256:99063599c92612eaa9d1829a06d41cfa1c5b027989ed866da243c984fb35c72c
edit-source:
- .gds/repository.yaml
- policies/base/repository-default.yaml
Expand All @@ -19,7 +19,7 @@ edit-source:
## Scope

- GDS repository ID: `repo_01KX7BV07RHD6KRA4Z4J0KCHGR`.
- Roles: `control-plane`.
- Roles: `control-plane, superproject`.
- Canonical repository facts: `.gds/repository.yaml`.
- Applied policy bundle: `.gds/bundle.lock.yaml` (`0.1.0-dev`).
- This is a first-class Claude Code projection compiled from the same typed
Expand Down
14 changes: 7 additions & 7 deletions .gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ bundle:
version: "0.1.0-dev"
release_sequence: 0
channel: "development"
source_commit: "4096db90d3a56fe60d43a98deea0eda422a35f72"
digest: "sha256:c5431d146bb8a8c251488ee3a96eea801ad054a0c8fecd9fbc61185791b3a991"
source_commit: "0e64dc713c9045c69f71034f00ed176eca3e34d5"
digest: "sha256:d09bd81b3f8b9cafe119d62fd9fcc549a66ce3f9b0d45d18f22b9065e5d6628d"

projection:
input_digest: "sha256:aa682dc69f657adfe8199181f04e41d7f975179e7a097bebae65768cf7c42c72"
output_digest: "sha256:d51d393220248c490e77f20a57c1930a831ee667a4008559002c67fbc499a0e1"
input_digest: "sha256:9035c1e5c46301ca11c9335b9fdc582d4ac1faac09b84b9f67c80240a4f3a553"
output_digest: "sha256:50580247e105ae1e629c6eba93df9da2a65504ed8b9d1d6c7e0299717e626dc1"
files:
- path: ".claude/CLAUDE.md"
digest: "sha256:e063d50d8bceff8282381bdcf15bf3287726b28a7bd663b3924e47757719d1cd"
digest: "sha256:5fcaf99f4e593ebd17bf7c3100e8f94afb5e035fa914df7ba1c0ad1ce676b994"
- path: ".gds/compiled-policy.json"
digest: "sha256:5ce6a147f6bd0573a769cd28bc5609b16c99bbe93cbe8c233110c204bb47d5f2"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:570f2b4b2f8497339017d14075b86fcc8a558ed6202e947d5d40c37f730a92ce"
digest: "sha256:8b8ee2a9d5fa42b033055d1884e38f3cf3b156c8cb40a3610b776a60460cf562"
- path: "AGENTS.md"
digest: "sha256:b861c844edc2b2f3ad7a6a66fcd3a8dca187d9bfb511e6623e2c993c19a442e0"
digest: "sha256:04f0737e9d7bee60e88c6fb7d325b767701adab663b8616bd0cc2496c3445a18"
1 change: 1 addition & 0 deletions .gds/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ repository:
display_name: "github-device-sync"
roles:
- "control-plane"
- "superproject"
lifecycle: "active"

provider:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/gds-ci.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# generator: gds
# bundle: 0.1.0-dev
# source-commit: 4096db90d3a56fe60d43a98deea0eda422a35f72
# input-digest: sha256:aa682dc69f657adfe8199181f04e41d7f975179e7a097bebae65768cf7c42c72
# source-commit: 0e64dc713c9045c69f71034f00ed176eca3e34d5
# input-digest: sha256:9035c1e5c46301ca11c9335b9fdc582d4ac1faac09b84b9f67c80240a4f3a553
# output-digest: sha256:7e8d64d5f0249163fa46ed044b7fb9c98cb0fb3b96445c730c49e50e89755dcb
# edit-source:
# - .gds/repository.yaml
Expand Down
6 changes: 3 additions & 3 deletions .serena/memories/core-context-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ gds_memory_schema: 1
scope_id: repo_01KX7BV07RHD6KRA4Z4J0KCHGR
status: verified
visibility: private
source_commit: 91ce9ee653b7a77001a6cdcf8e60ada89f6ea53a
source_commit: 0e64dc713c9045c69f71034f00ed176eca3e34d5
source_state: committed
source_digest: sha256:bdb7c732ee1fb9806b31b3087c6128d05507a41ae4734de5d8c37b717f955f71
source_digest: sha256:021989abfd6ca2642d0acaae77e4c433a892e7cd2a85273be788e5fc779d3cef
generated_by: gds-memory-compiler
bundle_version: 0.1.0-dev
verified_at: "2026-07-24T14:00:00Z"
verified_at: "2026-07-26T07:05:58Z"
refresh_triggers:
- context-resolver-change
- estate-registration-change
Expand Down
30 changes: 24 additions & 6 deletions .serena/memories/core-estate-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ gds_memory_schema: 1
scope_id: repo_01KX7BV07RHD6KRA4Z4J0KCHGR
status: verified
visibility: private
source_commit: 1af6b65676c7fc9302764b0a0f3d4cb03e38987d
source_commit: cfabe5ec42a2c7b27a6f194ed1ee23a8ca63e70b
source_state: committed
source_digest: sha256:671a4a9e2b8be92fd0bac193983830be9073a341ef12d4852311c7b27793e379
source_digest: sha256:471caa60f6e3cbe8933517bff2a622cf78ba95053ab9586d01b6b28f398eec0e
generated_by: gds-memory-compiler
bundle_version: 0.1.0-dev
verified_at: "2026-07-24T18:10:00Z"
verified_at: "2026-07-26T07:20:11Z"
refresh_triggers:
- architecture-source-change
- repository-schema-change
Expand All @@ -20,6 +20,9 @@ sources:
- docs/adr/0004-portfolio-and-superproject-terminology.md
- docs/adr/0005-typed-relationships.md
- docs/adr/0018-device-workspaces-and-metadata-repository-retirement.md
- docs/adr/0025-out-of-estate-external-workspace-root.md
- docs/adr/0026-flat-servers-workspace-root.md
- docs/adr/0027-submodule-repositories-have-no-standalone-checkout.md
- docs/contracts/lifecycles-v1.md
- core/estate/compiler.go
- core/estate/index.go
Expand Down Expand Up @@ -68,8 +71,11 @@ filesystem hierarchy as machine identity.
device selector and provider repository name, never from an implicit
four-level filesystem hierarchy.
- Server selectors are higher-priority specialized non-fork selectors. A
fork whose name starts with `server-` remains in the owner's fork portfolio;
organization and personal server portfolios use distinct workspace roots.
fork whose name starts with `server-` remains in the owner's fork portfolio.
Both server selectors assign one shared `portfolio:servers`, which resolves to
a single flat `servers` workspace root regardless of owning account. Owner
splitting stays correct for forks, where cross-owner name reuse is expected,
and is deliberately absent for servers.
- A device may assign each portfolio selector once, may reference only declared
workspace roots, and may not reuse one root for different selectors. Go and
Python validators prove these semantic rules with the same negative fixtures.
Expand All @@ -81,20 +87,32 @@ filesystem hierarchy as machine identity.
- An embedded submodule remains an independent repository boundary even when
checked out detached. Onboarding is safe only at the exact clean stage-zero
superproject gitlink; filesystem nesting never supplies identity.
- A submodule-consumed repository is materialized only as its superproject's
gitlink and never as a standalone checkout. Both together make one stable ID
resolve to two Git stores, which layout analysis rejects. Embedded placement
requires role `superproject` on the superproject and role `module` on the
module, and role `module` obliges a `module` block in the repository anchor.
- Workspace audit classifies each observed boundary by actual Git mode.
Standalone checkouts resolve through device selectors; embedded modules
resolve through the Git-reported superproject and exactly one typed
`git-submodule-consumer` relationship.
- Device placement drift and gitlink OID drift are independent findings.
`gds workspace audit` owns the former; `gds validate gitlinks` owns the
latter.
- A repository owned outside the estate has no owner, installation, or
selector and therefore no portfolio. Its checkout is placed by convention in
the out-of-estate `external` device root, is never declared in
`workspace_roots`, and is never a materialization target. Its single expected
audit finding is `GDS_WORKSPACE_ANCHOR_REQUIRED`; that is accepted steady
state, not remediable drift. Leaving the estate root families is a
classification statement, not merely a local move.

## Sources

- `.gds/repository.yaml`
- `schemas/v1/repository.schema.json`
- `docs/architecture/README.md`
- ADRs 0003, 0004, 0005, and 0018
- ADRs 0003, 0004, 0005, 0018, 0025, 0026, and 0027
- `estate/devices/rldyourmnd-mac1.yaml`
- `estate/devices/rldyourmnd-mac2.yaml`
- `estate/selectors/organization-servers.yaml`
Expand Down
6 changes: 3 additions & 3 deletions .serena/memories/core-policy-projection.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ gds_memory_schema: 1
scope_id: repo_01KX7BV07RHD6KRA4Z4J0KCHGR
status: verified
visibility: private
source_commit: 91ce9ee653b7a77001a6cdcf8e60ada89f6ea53a
source_commit: 0e64dc713c9045c69f71034f00ed176eca3e34d5
source_state: committed
source_digest: sha256:e01b4ba403cc9cff8127156084ef8d1d239fd4344c2451d9f2c5836ad18dd981
source_digest: sha256:f7902f83d161db985501efa726aca6ad9c7713815b496ed9eeaa22936ed742f1
generated_by: gds-memory-compiler
bundle_version: 0.1.0-dev
verified_at: "2026-07-24T14:00:00Z"
verified_at: "2026-07-26T07:05:58Z"
refresh_triggers:
- policy-compiler-change
- projection-template-change
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
GENERATED FILE - DO NOT EDIT DIRECTLY
generator: gds
bundle: 0.1.0-dev
source-commit: 4096db90d3a56fe60d43a98deea0eda422a35f72
input-digest: sha256:aa682dc69f657adfe8199181f04e41d7f975179e7a097bebae65768cf7c42c72
output-digest: sha256:6e9805f81c23943faca08511de1521935adb2252a1eacbdce8628ed278c0ea08
source-commit: 0e64dc713c9045c69f71034f00ed176eca3e34d5
input-digest: sha256:9035c1e5c46301ca11c9335b9fdc582d4ac1faac09b84b9f67c80240a4f3a553
output-digest: sha256:8ef035e28c8ee736c295069b828e70f2873be24f8f96b0cf02cf4ae223f4b9fe
edit-source:
- .gds/repository.yaml
- policies/base/repository-default.yaml
Expand All @@ -19,7 +19,7 @@ edit-source:
## Scope

- Repository ID: `repo_01KX7BV07RHD6KRA4Z4J0KCHGR`.
- Roles: `control-plane`.
- Roles: `control-plane, superproject`.
- Canonical repository facts: `.gds/repository.yaml`.
- Applied bundle: `.gds/bundle.lock.yaml` (`0.1.0-dev`).
- Compiled policy: `.gds/compiled-policy.json`.
Expand Down
4 changes: 2 additions & 2 deletions core/estate/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,15 @@ func TestCompileRoutesServerRepositoriesByNamePrefix(t *testing.T) {
byID[assignment.ProviderID] = assignment
}
if got := byID[10]; got.MatchedSelector != "organization-servers" ||
len(got.Portfolios) != 1 || got.Portfolios[0] != "portfolio:organization-servers" {
len(got.Portfolios) != 1 || got.Portfolios[0] != "portfolio:servers" {
t.Fatalf("organization server repository = %#v", got)
}
if got := byID[11]; got.MatchedSelector != "organization-sources" ||
!containsString(got.Portfolios, "portfolio:organization-projects") {
t.Fatalf("organization non-server repository = %#v", got)
}
if got := byID[12]; got.MatchedSelector != "personal-servers" ||
len(got.Portfolios) != 1 || got.Portfolios[0] != "portfolio:personal-servers" {
len(got.Portfolios) != 1 || got.Portfolios[0] != "portfolio:servers" {
t.Fatalf("personal server repository = %#v", got)
}
if got := byID[13]; got.MatchedSelector != "organization-forks" ||
Expand Down
71 changes: 71 additions & 0 deletions docs/adr/0025-out-of-estate-external-workspace-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# ADR 0025: Keep third-party collaboration checkouts in an out-of-estate external root

Status: Accepted

Date: 2026-07-26

## Context

ADR 0018 established that device checkout placement is declared by typed
assignments in `estate/devices/*.yaml`, where a portfolio selector resolves to
one named `workspace_root`. Every selector in `estate/selectors/` matches
`owner:rldyourmnd` or `owner:nddev`, and both installations in
`estate/installations/` are scoped to those two accounts.

A repository owned by a third-party GitHub account therefore cannot be
classified, discovered, or observed by this estate, even though work on it is
legitimate and routine — a collaborator checkout where the owner has write
access is a normal case, not an anomaly.

Two placements were considered and rejected:

- Placing it under an estate-managed root such as the `rldyourmnd`
(`portfolio:personal-projects`) root asserts a portfolio the repository can
never match, producing permanent classification drift.
- Forking it into the `rldyourmnd-forks` root changes the collaboration model.
The fork portfolio exists for upstreams without write access; forking a
private repository the owner can already push to adds cross-fork pull-request
friction and leaves the counterparty unable to push.

Adding an `external` entry to `workspace_roots` was also rejected. Workspace
roots are read only through `materialization.include[].workspace_root`, so a
root no selector can reference is unreachable configuration that falsely
implies GDS materializes there.

## Decision

1. Checkouts of repositories owned by accounts outside the estate live under the
device-local root `${HOME}/Developer/external`.
2. That root is a convention for humans and agents only. It is deliberately not
declared in any device descriptor's `workspace_roots`, holds no portfolio
assignment, and is never a materialization target.
3. Such checkouts carry no `.gds/repository.yaml` anchor. Anchoring a repository
owned by another party would commit estate governance artifacts into a tree
this estate does not own.
4. `gds workspace audit` raises exactly one expected finding per external
checkout, `GDS_WORKSPACE_ANCHOR_REQUIRED` with `anchor_state: missing`. This
is the accepted steady state, not drift to remediate. Because the root is
outside every declared `workspace_root`, no placement finding
(`GDS_WORKSPACE_PLACEMENT_DRIFT`, `GDS_WORKSPACE_ROOT_NOT_READY`) is produced
and no bogus `expected_path` is computed.
5. Promotion out of `external` is an explicit estate change: it requires a new
owner, an installation that can observe the account, and a selector — that
is, transfer or adoption of the repository, never a local move alone.

## Consequences

- Third-party collaboration has one predictable device location, distinct from
the four estate-managed root families (`control-plane`, `nddev`, `rldyourmnd`,
and the owner-specific `forks` and `servers` roots).
- Estate audits stay truthful: an external checkout is reported as unanchored
rather than silently absent or wrongly classified.
- The anchor finding is permanent noise in `gds workspace audit --root
${HOME}/Developer`. It is already the state of every unanchored boundary on
the device and is not introduced by this decision.
- No typed estate object changes, so no policy compilation, bundle, or
projection input is affected by placing a repository in `external`.

## Rollback

Delete or relocate the external checkout. Nothing in the estate references it,
so no plan, approval, or regeneration is required to reverse this placement.
63 changes: 63 additions & 0 deletions docs/adr/0026-flat-servers-workspace-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# ADR 0026: Place every server checkout in one flat servers workspace root

Status: Accepted

Date: 2026-07-26

## Context

ADR 0018 made fork workspaces owner-specific so equal repository names cannot
collide across owners, and the server selectors were modelled the same way:
`portfolio:organization-servers` resolved to `${HOME}/Developer/servers/nddev`
and `portfolio:personal-servers` to `${HOME}/Developer/servers/rldyourmnd`.

That owner split was never the intended shape for servers, and it was never
materialized. On `rldyourmnd-mac1` neither subroot exists; every server
checkout lives directly in `${HOME}/Developer/servers`. The declared layout and
the actual one have disagreed since the roots were declared, which is why
`gds workspace audit` reports `GDS_WORKSPACE_PLACEMENT_DRIFT` for
`servers/ci-workflows`.

A device may not assign one workspace root to two selectors: both
`core/validation/schema.go` and `scripts/validate_gds_schemas.py` raise
`GDS_DEVICE_WORKSPACE_ROOT_REUSED`, proven by a negative fixture in
`tests/fixtures/schemas/v1/cases.json`. Pointing the two existing server
selectors at one flat root is therefore rejected by validation, and one flat
root requires one server portfolio.

Servers are addressed by host, not by owning account. Splitting them by owner
adds a directory level that carries no operational meaning.

## Decision

1. `portfolio:organization-servers` and `portfolio:personal-servers` are
replaced by a single `portfolio:servers`.
2. Both server selectors keep their own `match` blocks and IDs. Selection stays
owner-scoped and `server-`-prefixed, so a fork named `server-*` still lands
in its owner's fork portfolio; only the assigned portfolio is now shared.
3. Every device declares one `servers` workspace root at
`${HOME}/Developer/servers` and exactly one materialization assignment for
`portfolio:servers`. The per-owner `servers/nddev` and `servers/rldyourmnd`
subroots are removed.
4. Server checkout placement is `${HOME}/Developer/servers/<provider-repository-name>`,
flat, regardless of owning account.

## Consequences

- The declared layout now matches the materialized one, removing a standing
source of placement drift.
- Two server repositories with the same name under different owners would
resolve to one path. Layout analysis already rejects duplicate local paths, so
such a collision surfaces as a finding rather than silently overwriting a
checkout. Server repositories are host-named and unique in practice.
- Owner-specific roots remain correct for forks, where cross-owner name reuse is
expected. This decision does not change fork placement.
- The estate compiler's server assignments now carry `portfolio:servers`;
`core/estate/compiler_test.go` asserts the shared portfolio while still
asserting the distinct matched selectors.

## Rollback

Restore the two portfolio names in the server selectors and the two per-owner
roots and assignments in each device descriptor. No provider state is involved,
so rollback is a source-only change followed by regeneration.
Loading
Loading