From b97442221559b2d414005f1eff19a10e1402b73b Mon Sep 17 00:00:00 2001 From: Danil Silantyev Date: Tue, 29 Sep 2026 06:01:32 +0500 Subject: [PATCH] docs: align estate contract with shipped selectors and binaries --- docs/architecture/README.md | 19 ++++++++------ docs/contracts/cli-v1.md | 9 +++++++ docs/contracts/estate-v1.md | 48 ++++++++++++---------------------- docs/contracts/memories-v1.md | 49 +++++++++++++++-------------------- 4 files changed, 58 insertions(+), 67 deletions(-) diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 61dc6ea..3b34910 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -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/`. @@ -37,7 +37,8 @@ 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` @@ -45,12 +46,14 @@ provenance, and rejects equal-priority selector ambiguity. - **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) @@ -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`. diff --git a/docs/contracts/cli-v1.md b/docs/contracts/cli-v1.md index 95c1769..2da249a 100644 --- a/docs/contracts/cli-v1.md +++ b/docs/contracts/cli-v1.md @@ -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 `, `gds memory verify ` + +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 ` Validates schema, expiry ordering, exact scope/precondition coverage, step diff --git a/docs/contracts/estate-v1.md b/docs/contracts/estate-v1.md index bc2e638..16c347a 100644 --- a/docs/contracts/estate-v1.md +++ b/docs/contracts/estate-v1.md @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/docs/contracts/memories-v1.md b/docs/contracts/memories-v1.md index 9173b99..48e3ad9 100644 --- a/docs/contracts/memories-v1.md +++ b/docs/contracts/memories-v1.md @@ -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 @@ -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 @@ -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 ` 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 `. 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 --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 --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.