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
19 changes: 11 additions & 8 deletions docs/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This is the current architecture overview of the `github-device-sync` (gds)
control plane. It carries no release version in its title deliberately: it
describes the system as it is now, and a version stamped here goes stale
silently every time a release ships without an architecture change. Release
boundaries live in `CHANGELOG.md` and `docs/version-ledger.md`. For the
boundaries live in `CHANGELOG.md` and release tags. For the
migration design baseline that preceded the Go implementation, see
the ADRs in `docs/adr/`.

Expand Down Expand Up @@ -37,20 +37,23 @@ provenance, and rejects equal-priority selector ambiguity.

- **Five GitHub installations**: `example-user` (user), `example-org`,
`example-media`, `NDDev-OpenNetwork`, and `example-guild`
(read-only member, no mutation capability). Four carry separate Mutation Apps
(read-only member, no mutation capability). Four have separate mutation
capability declarations
with distinct identity and secret locators from their read Installation Apps.
- **Three devices**: `example-workstation` (linux/x86_64, desktop),
`example-user-mac2` (macos/arm64, desktop), and `example-user-ubuntu-1`
(linux/x86_64, desktop-builds, rootful Docker).
- **Repository visibility**: public. The control-plane repository carries
`visibility_contract: public` and declares no submodules, so no module
visibility has to be reconciled against it.
- **Posture**: `mutation_mode: "pull-request"` for managed NDDev sources;
every other selector remains `observe-only`, and every write remains gated by
- **Posture**: `mutation_mode: "pull-request"`; `organization-sources` and
`opennetwork-sources` are managed, while the other shipped selectors remain
`observe-only`. Every write remains gated by
exact signed approval plus one-shot enablement.
- **Selectors** classify observed repositories by owner, fork flag, name
prefix, and archived state into portfolios. Priority bands: `100` generic,
`200` specialized non-fork, `300` state override (archived precedence).
- **Selectors** classify observed repositories by owner, visibility, and name
prefix into portfolios. The seven shipped selectors use priorities `100`
(sources) and `200` (server-name overrides). The example declares no
archived-state selector.

## Package layout (Go core)

Expand All @@ -67,7 +70,7 @@ provenance, and rejects equal-priority selector ambiguity.
| Control-plane service | `state`, `controller`, `reconciler`, `webhooks`, `audit` | SQLite journal, webhook worker, drift reconciler, signed audit snapshots. |
| Orchestration | `app`, `cli`, `cmd/*` | Use-case wiring, Cobra adapter, seven binaries. |

Six binaries: `gds`, `gds-controller`, `gds-assurance`,
Seven binaries: `gds`, `gds-controller`, `gds-assurance`,
`gds-performance-evidence`, `gds-release-builder`, and
`gds-{claude,codex}-runtime-driver`.

Expand Down
9 changes: 9 additions & 0 deletions docs/contracts/cli-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ Validates semantic Serena memory names, strict metadata, committed versus
working-tree status, required body sections, safe repository-relative sources,
and deterministic source digests. It reports drift but never rewrites memory.

### `gds memory generate <name>`, `gds memory verify <name>`

Both return a complete read-only candidate for an existing semantic memory
whose declared sources have been committed. `generate` demotes stale source
provenance to `generated-unverified`; `verify` stamps `verified` and
`verified_at` after the caller has reviewed the body against those sources.
Neither writes the candidate. In JSON output, apply `data.content` to
`data.path`, then run `gds memory validate`.

### `gds validate plan --file <path>`

Validates schema, expiry ordering, exact scope/precondition coverage, step
Expand Down
48 changes: 17 additions & 31 deletions docs/contracts/estate-v1.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# GDS estate desired-configuration v1 contract

Status: implemented with controlled mutation enabled for managed NDDev sources.
Status: implemented. The shipped example estate enables controlled mutation
only for selectors and operation capabilities that explicitly allow it.

## Source boundary

Expand All @@ -19,10 +20,9 @@ estate/owners/*.yaml
estate/selectors/*.yaml
```

The current baseline declares four owners: the personal `example-user` account,
the `example-org` organization, the `example-media` organization, and
the `example-guild` organization (read-only member, no mutation
capability). Installation IDs remain logical; their actual GitHub App and
The shipped example declares five owners: `example-user`, `example-org`,
`example-media`, `example-guild` (observe-only), and the publisher account
`NDDev-OpenNetwork`. Installation IDs remain logical; their actual GitHub App and
provider installation IDs are bound only by a private device-local
`github-runtime` document, or by the gh-CLI credential variant (ADR 0034).
Estate secret references are portable `secret:gds/...` identities; they do not
Expand All @@ -41,12 +41,12 @@ discovery.default_management_mode = observe-only
rollout.mutation_mode = pull-request
```

The generic NDDev source selector assigns `managed`; every other selector
remains `observe-only`. Managed does not authorize an automatic write: apply
The `organization-sources` and `opennetwork-sources` selectors assign
`managed`; the other shipped selectors assign `observe-only`. Managed does not authorize an automatic write: apply
also requires an exact immutable plan, signed approval, one-shot enablement,
fresh compare-and-swap evidence, an operation-scoped mutation capability, a
private runtime, and the device mutation kill switch. Archive, fork, server,
guild, personal, and Example-Media selectors remain outside this rollout.
guild, personal, and example-media selectors remain outside this rollout.

## Discovery and classification

Expand Down Expand Up @@ -76,32 +76,18 @@ retain their own priority. Organization and personal server portfolios
use distinct device workspace roots so their filesystem placement remains
injective even when owners contain repositories with the same name.

Selector priority bands are conventional:
The shipped selectors use these priority bands:

- `100` — generic classification (sources, forks);
- `200` — specialized non-fork overrides (servers, named-prefix families);
- `300` — state overrides that outrank topology and name (archived).

The `archived` state takes precedence over both fork topology and server name.
The estate ships a priority-`300` archived selector for every owner whose
fall-through source portfolio would otherwise misclassify an archived
repository, so a provider-archived repository resolves to
`portfolio:archived-projects` regardless of whether it is a fork, a source, or
a `server-*` superproject:

- `personal-archived` matches `owner:example-user` with `archived: true`;
- `organization-archived` matches `owner:nddev` with `archived: true`.

A higher band must be distinct from every overlapping selector's priority to
avoid the equal-priority ambiguity the compiler rejects. The
`organization-archived` selector is defense-in-depth: no NDDev repository is
archived on the provider today, but without it a future archived NDDev
repository would fall through to `portfolio:organization-projects` under
`organization-sources` instead of `portfolio:archived-projects`. The
`example-media` and `example-guild` owners do not yet declare an
archived selector; add one if an archived repository ever appears under those
owners and would otherwise be misclassified by `example-media-sources`,
`example-media-servers`, or `guild-sources`.

There is no archived selector in the shipped example. The compiler preserves
the provider's `archived` observation, but classification follows the matching
selector. An estate that defines archival by ownership can assign its archive
owner to `portfolio:archived-projects` regardless of the provider flag. An
estate that instead wants provider-archived precedence must declare an
appropriate higher-priority selector. Equal-priority overlapping matches are
rejected by the compiler.

## Monotonic policy fields

Expand Down
49 changes: 21 additions & 28 deletions docs/contracts/memories-v1.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# GDS Serena memory v1 contract

Status: Phase 11 provenance validation implemented; current memories are
verified against committed source inputs.
Status: provenance validation and read-only candidate generation are
implemented. The shipped public repository disables Serena memories in its
anchor and has no tracked `.serena/memories/` set.

## Role

Expand Down Expand Up @@ -92,7 +93,7 @@ contract statement rather than an omission.
claimed assurance is missing, and a repository that keeps memories without
binding them to sources has not promised to keep any.
- `enabled: true, provenance_required: true` -- everything above, including
`GDS_MEMORY_SET_EMPTY`. This control plane declares this.
`GDS_MEMORY_SET_EMPTY`. A consuming estate can declare this.

An anchor that cannot be read falls back to the strictest reading. The opt-out
has to be stated to take effect; inferring it from a file that failed to parse
Expand All @@ -106,32 +107,24 @@ so `gds validate memories` (and the `core/memory` and `core/cli` tests) fail
until the memory is re-synced. This is expected drift, not a defect. The sync is
commit-first:

1. Commit the source change. `gds memory generate <name>` resolves the source
commit from the committed history and refuses a dirty tree with
1. Commit the source change. Both candidate commands resolve the source
commit from committed history and refuse dirty declared sources with
`GDS_MEMORY_COMMITTED_SOURCE_NOT_PROVEN`.
2. Run `gds memory generate <name>`. It preserves the authored body, recomputes
the digest and source commit, and emits `generated-unverified` whenever the
commit or digest changed. Apply the new `source_commit`, `source_digest`, and
a fresh `verified_at` to the frontmatter.
3. Restore `status: verified` only as a deliberate assertion that the body still
describes the current source; add any new stable invariant to the body while
re-reading it. The generator never promotes status on its own.
2. Re-read the authored body against those sources and edit any stale claim.
`gds memory generate <name> --json` preserves that body and returns a
`generated-unverified` candidate when source provenance changed. It does
not write the file or assert that the prose is true.
3. After reviewing the body, run `gds memory verify <name> --json`. It returns
a candidate with current source and body digests, `status: verified`, and a
new `verified_at` later than the source commit. Apply the returned
`data.content` byte-for-byte to `data.path`; the command never writes it.
`bundle_version` is memory metadata and is not the installed CLI version.
4. Commit the memory update on its own, conventionally as a `docs(memory):`
change separate from the source commit.
change separate from the source commit. Run `gds memory validate` again.

## Current semantic set
## Repository selection

```text
core-bundle-rollout
core-context-resolution
core-estate-layout
core-github-controller
core-harness-adapters
core-integrated-assurance
core-operation-safety
core-policy-projection
```

The former numeric memories were retired only after these replacements covered
their still-valid knowledge. Legacy four-level/container claims that conflict
with the typed graph model were not copied forward.
The public GDS example has `agent.serena.enabled: false` and
`provenance_required: false`, so it owes no memory set. A consuming estate
chooses its own semantic names and source paths; do not copy an older GDS
memory list into it.
Loading