From 2b42f5bd21e21e1b92246a7ae66df2d5ab187dab Mon Sep 17 00:00:00 2001 From: Wayne Sun Date: Thu, 3 Sep 2026 12:58:31 -0400 Subject: [PATCH 1/2] =?UTF-8?q?docs(adr):=20ADR=200103=20=E2=80=94=20recor?= =?UTF-8?q?ded=20presets=20bumped=20by=20Renovate=20via=20fullsend=20updat?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-repo is the only installation mode (ADR 0044) and the org config tier it removed has no working replacement: config.base.yaml presets record no provenance, agent update skips base: pins and lock.yaml (#5433, #5802), nothing checks pins in CI (#6191), and repos install has no preset concept. The fullsend-ai org bridges this with sync workflows (sed + direct push via a ruleset-bypass App, always agents main HEAD, stale hashes on non-harness paths) that replaced a working Renovate custom manager. Decide: shared configuration is a versioned preset hosted in any repo, recorded per repo in a bot-owned .fullsend/preset.lock.yaml, and bumped by each repo's Renovate job. fullsend ships a Renovate preset with custom managers for its pin formats and runs one idempotent `fullsend update` as the post-upgrade task (re-fetch preset, recompute #sha256=, regenerate lock.yaml). Tracking strategy and cadence are Renovate packageRules, minimumReleaseAge and schedule. No dedicated org repo, no org-level sync workflow, no enrollment list. Human-owned files are never written by the verb; bot-owned files are rewritten whole, so there is no three-way merge and no compile step. Verified on a live per-repo install in nonflux (fullsend-adr0103-preset, fullsend-adr0103-repo-a): one Renovate PR re-pinned agents[].source, the harness base: URL and the shim with recomputed hashes and lock; preset bumps touched only config.base.yaml and preset.lock.yaml. Prerequisites, not decided here: the dispatch Route job reads only config.yaml, and agent add/update/remove write the merged list into the overlay (must land before a preset ships agents). Annotates ADR 0003, 0044 and 0069; updates architecture.md and the governance and agent-infrastructure problem docs. Assisted-by: Claude (draft, coordination) Assisted-by: Claude gh-c-1/gh-c-2/gh-c-3 (research, walkthrough) Signed-off-by: Wayne Sun --- docs/ADRs/0003-org-config-repo-convention.md | 6 + ...044-deprecate-per-org-installation-mode.md | 4 + .../0069-ready-made-configuration-presets.md | 5 + ...ig-presets-converged-by-fullsend-update.md | 125 ++++++++++++++++++ docs/architecture.md | 8 +- docs/problems/agent-infrastructure.md | 2 +- docs/problems/governance.md | 2 +- 7 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md diff --git a/docs/ADRs/0003-org-config-repo-convention.md b/docs/ADRs/0003-org-config-repo-convention.md index a2ecec3b33..41e9e60fdb 100644 --- a/docs/ADRs/0003-org-config-repo-convention.md +++ b/docs/ADRs/0003-org-config-repo-convention.md @@ -22,6 +22,12 @@ Date: 2026-03-25 Accepted +> **Note (2026-09-03):** Per-org installation is deprecated +> ([ADR 0044](0044-deprecate-per-org-installation-mode.md)). A dedicated +> `/.fullsend` repo is no longer required: shared configuration is a +> preset hosted in any repo and bumped per consuming repo by Renovate +> ([ADR 0103](0103-shared-config-presets-converged-by-fullsend-update.md)). + ## Context An organization adopting fullsend must configure it: the intent repo, the diff --git a/docs/ADRs/0044-deprecate-per-org-installation-mode.md b/docs/ADRs/0044-deprecate-per-org-installation-mode.md index efbaf15ca1..f9cab61f04 100644 --- a/docs/ADRs/0044-deprecate-per-org-installation-mode.md +++ b/docs/ADRs/0044-deprecate-per-org-installation-mode.md @@ -38,6 +38,10 @@ Deprecates the per-org installation mode established in Per-repo installation mode ([ADR 0033](0033-per-repo-installation-mode.md)) becomes the sole supported installation model. +> **Note (2026-09-03):** The "no centralized policy" and "per-repo setup +> overhead" costs listed below are decided in recorded presets and +> `fullsend update` ([ADR 0103](0103-shared-config-presets-converged-by-fullsend-update.md)). + ## Context Fullsend's original installation model is per-org: `fullsend admin install ` diff --git a/docs/ADRs/0069-ready-made-configuration-presets.md b/docs/ADRs/0069-ready-made-configuration-presets.md index e0087f2bdb..0be85e07cd 100644 --- a/docs/ADRs/0069-ready-made-configuration-presets.md +++ b/docs/ADRs/0069-ready-made-configuration-presets.md @@ -19,6 +19,11 @@ Date: 2026-06-29 Accepted +> **Note (2026-09-03):** Preset provenance (a bot-owned +> `.fullsend/preset.lock.yaml`) and the refresh path (Renovate running +> `fullsend update` as a post-upgrade task) are decided in +> [ADR 0103](0103-shared-config-presets-converged-by-fullsend-update.md). + ## Context `fullsend github setup` today spreads installation decisions across many CLI diff --git a/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md b/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md new file mode 100644 index 0000000000..2eb209e08b --- /dev/null +++ b/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md @@ -0,0 +1,125 @@ +--- +title: "103. Shared configuration as recorded presets converged by fullsend update" +status: Accepted +relates_to: + - governance + - agent-infrastructure +topics: + - configuration + - installation + - distribution + - presets +--- + +# 103. Shared configuration as recorded presets converged by fullsend update + +Date: 2026-09-03 + +## Status + +Accepted + + + +## Context + +[ADR 0044](0044-deprecate-per-org-installation-mode.md) made per-repo the +only installation mode and named the cost: no central place to share agent +configuration, and every repo bumped one at a time. The pieces meant to fill +that gap exist but do not connect: `config.base.yaml` +([ADR 0069](0069-ready-made-configuration-presets.md)) is written by +`github setup --config` and nothing records where it came from; `agent +update` skips `base:` pins in local harness files and never regenerates +`lock.yaml` (#5433, #5802, #6191); `repos install` +([ADR 0074](0074-repos-command-consolidation.md)) has no preset concept. +Two readers — the dispatch Route job's `yq` gates and `agent +add/update/remove` — read only the overlay, so ADR 0069's layering never +reaches them (#6422's class); those are fixed as prerequisites, not decided +here, and the `agent` one must land before any preset ships agents: today +`agent add` copies preset entries into `config.yaml`, where they shadow +the preset forever. The fullsend-ai org replaced a working Renovate custom manager +(fullsend-ai/.fullsend#174) with a workflow that `sed`-rewrites +`config.yaml` across a hard-coded repo list, pushes to `main` through a +ruleset-bypass App at agents `main` HEAD, and leaves stale hashes on every +non-harness path. + +GitHub Agentic Workflows solves this with a central `agentic-workflows` +repo, a `source:` field in each installed workflow, and `gh aw update --org +--create-pull-request`. It needs a three-way merge because one file mixes +managed fields with user edits (it already carves `source` out of the +merge), a compile step because pins live in generated `.lock.yml`, and it +infers the tracking strategy from the ref's shape — which resolves every +`add` to a SHA and so tracks branch HEAD by default. + +## Decision + +Shared fullsend configuration is a **versioned preset**, recorded in each +consuming repository by provenance, and **bumped by Renovate** — the same +job that bumps the repo's packages and its workflow pins — with one +first-party verb doing the fullsend-specific part. + +1. **A preset is a git-hosted `config.base.yaml`** plus the harness bases, + skills and policies it references by URL, schema-validated on fetch. It + lives in whichever repo already hosts the org's shared Renovate config; + no dedicated org repo, no org-level workflow and no enrollment list are + needed — the `/.fullsend` repo of + [ADR 0003](0003-org-config-repo-convention.md) is one possible host, + not a requirement. +2. **Every pin is a Renovate-bumpable string, and the verb never writes + human-owned files.** `config.yaml` and local harness files are the human + layer. `config.base.yaml` (byte-identical to the fetched preset, so + `--config-hash` keeps working), `lock.yaml`, the shim, and a new + `.fullsend/preset.lock.yaml` whose `source` URL carries the tracked + ref, with the resolved SHA and `sha256` derived from it, are the bot + layer, rewritten whole. + `github setup --config` creates that record from a blob URL on a branch + or tag, resolved the way `agent add` resolves one. Carving every managed + field into its own file is what removes the three-way merge; run-time + resolution removes the compile step. +3. **fullsend ships a Renovate preset** (`custom.regex` managers for the + preset record, `agents[].source` URLs and harness `base:` URLs — never + `config.base.yaml` — with `git-refs` / `github-tags` datasources) that a + repo's `renovate.json` extends. Renovate rewrites the pin strings; + **one idempotent verb, `fullsend update`**, run as its `postUpgradeTasks` + command, does the derived work: re-fetch the preset into + `config.base.yaml`, recompute every `#sha256=`, regenerate `lock.yaml` + without re-resolving unchanged dependencies. Run by hand it also bumps + the shim ref; `--check` writes nothing and exits non-zero on drift from + a tag or SHA. `repos install` applies the preset on a fresh install. +4. **Tracking strategy and cadence are Renovate's**, not new fullsend + fields: `packageRules` per dependency choose branch, major tag, exact tag + or SHA, `minimumReleaseAge` gates adoption, `schedule` sets cadence, and + the org's shared Renovate preset carries those choices to every repo. + The Route job reads both config layers so a preset can carry policy. + +Repos with no explicit `agents:` entry already follow the fullsend build tag +for first-party agents; for them the shim bump is the agent bump. Agents +generated by `fullsend agent new` (#6966) are local files with no +provenance and are left alone except for their `image:` pin. + +## Consequences + +- Agent and preset bumps arrive as ordinary Renovate pull requests next to + package and action bumps, replacing `sed` scripts, direct pushes and + ruleset-bypass Apps; #5433, #5802 and #6191 close on the verb, #6597 and + #6607 become Renovate policy. +- `postUpgradeTasks` runs only on self-hosted Renovate with an anchored + `allowedCommands` entry and the shell executor off, so each repo's + Renovate job (not the Mend-hosted App) runs the verb; a repo without + Renovate runs `fullsend update` by hand. The fullsend-ai sync workflows + are retired. +- Orgs get gh-aw's configurable sharing model without a compile phase; the + org tier ADR 0044 removed returns as a preset URL plus a Renovate preset, + not a config repo. +- The preset URL and its refs are a supply-chain trust surface: the + `allowed_remote_resources` union-with-deny-all floor and `--config-hash` + apply; signing and a `redirect:` for relocated presets are follow-ons. +- Enforcing a policy floor (refusing to run when a repo drifts) is a separate + decision; this ADR only makes drift visible. The model maps onto Tekton + remote resolution (git resolver `revision`, bundle digests) if stages + later run as Tekton tasks. diff --git a/docs/architecture.md b/docs/architecture.md index c7128f52dc..ce61b2feb3 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -57,7 +57,7 @@ the dedicated org-level `/.fullsend` config repo is deprecated - Event-driven stage dispatch: eliminate `workflow_dispatch` + `gh workflow run` fan-out from `dispatch.yml` in favor of synchronous `workflow_call` so the dispatched run stays linked to the caller ([ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)). - Multi-repo management: a `fullsend repos` subcommand group with a declarative `repos.yaml` manifest for managing per-repo installations at scale — install, convergence (provision, sync, upgrade), status, and uninstall across repos and orgs ([ADR 0057](ADRs/0057-repos-management.md), [ADR 0074](ADRs/0074-repos-command-consolidation.md)). - Dispatch version-skew resolution: per-repo `reusable-dispatch.yml` inlines stage workflow jobs directly, eliminating `@v0` references to `reusable-{stage}.yml` ([ADR 0062](ADRs/0062-dispatch-version-skew.md)). -- Ready-made configuration presets: `fullsend github setup --config ` installs a vendor preset as `.fullsend/config.base.yaml` and a stub `.fullsend/config.yaml` overlay in the target repository; mint URL, inference backend, and related settings live in configuration files resolved through accessor methods, not CLI flags. Shared-infrastructure presets will reduce per-adopter enrollment (target state): mint via `job_workflow_ref` trust per [ADR 0059](ADRs/0059-public-mint-mode-with-wildcard-allowlists.md); inference authorization model undecided ([ADR 0069](ADRs/0069-ready-made-configuration-presets.md)); enrollment remains required until follow-on ADRs land. +- Ready-made configuration presets: `fullsend github setup --config ` installs a vendor preset as `.fullsend/config.base.yaml` and a stub `.fullsend/config.yaml` overlay in the target repository; a bot-owned `.fullsend/preset.lock.yaml` records the preset's source, tracking ref and hash; every pin is a Renovate-bumpable string, fullsend ships a Renovate preset with the custom managers, and Renovate runs `fullsend update` as its post-upgrade task to re-fetch the preset, recompute `#sha256=`, re-pin `base:` in local harness files and regenerate `lock.yaml` (`--check` reports drift) ([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)); mint URL, inference backend, and related settings live in configuration files resolved through accessor methods, not CLI flags. Shared-infrastructure presets will reduce per-adopter enrollment (target state): mint via `job_workflow_ref` trust per [ADR 0059](ADRs/0059-public-mint-mode-with-wildcard-allowlists.md); inference authorization model undecided ([ADR 0069](ADRs/0069-ready-made-configuration-presets.md)); enrollment remains required until follow-on ADRs land. - GitLab event dispatch: two-path model — native CI triggers (`merge_request_event`) for MR events, cron-based polling for issues/comments/labels. No external infrastructure (no webhook bridge). Bot PAT stored as a protected CI/CD variable. Per-repo only ([ADR 0067](ADRs/0067-gitlab-cron-polling-event-dispatch.md)). **Open questions:** @@ -533,6 +533,12 @@ Fullsend uses a three-tier configuration inheritance model for all configuration In per-repo installation the middle tier is replaced by files inside the target repo: `.fullsend/config.base.yaml` (vendor preset or baseline) and `.fullsend/config.yaml` (repo overlay), with code defaults below both. The +preset is hosted in any repo, typically the one holding the org's shared +Renovate config — no dedicated org repo or org-level workflow; +`config.base.yaml`, `preset.lock.yaml`, `lock.yaml` and the shim are +bot-owned and rewritten whole by Renovate running `fullsend update`; +`config.yaml` and local harness files are human-owned, and the dispatch +Route job reads both config layers ([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)). The org-tier box above describes the historical per-org model, now deprecated ([ADR 0044](ADRs/0044-deprecate-per-org-installation-mode.md), [ADR 0069](ADRs/0069-ready-made-configuration-presets.md)). diff --git a/docs/problems/agent-infrastructure.md b/docs/problems/agent-infrastructure.md index 04859dac0a..c3e65edca9 100644 --- a/docs/problems/agent-infrastructure.md +++ b/docs/problems/agent-infrastructure.md @@ -113,7 +113,7 @@ Many fullsend scenarios skew toward **ephemeral, task-scoped** execution (triage - **Agent architecture** — Instance topology (per-repo vs shared) and “local vs remote” for pre-PR review depend on what infrastructure we have. Infrastructure enables or constrains those choices. - **Security threat model** — Isolation and “separate execution environments” are implemented by this layer. Supply chain (what base images and dependencies the runtime uses) also lives here. -- **Governance** — Policy may be applied at runtime by agents reading from a policy repo; infrastructure determines where that runtime runs and how it accesses policy. +- **Governance** — Policy may be applied at runtime by agents reading from a policy repo; infrastructure determines where that runtime runs and how it accesses policy. Shared configuration reaches per-repo installs as a recorded preset converged by `fullsend update` ([ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)); the same pinned-remote-reference model maps onto Tekton remote resolution if stages later run there. - **Repo readiness** — Repos need reliable CI and signals; agent infrastructure may consume or depend on the same CI (e.g. for “run tests” or “run linters”) and should not conflict with it. Headless runtimes amplify **feedback latency** and **workspace handoff** costs when CI is the only execution path. ## Open questions diff --git a/docs/problems/governance.md b/docs/problems/governance.md index 8deb43e78e..85f5ced262 100644 --- a/docs/problems/governance.md +++ b/docs/problems/governance.md @@ -29,7 +29,7 @@ Agent configuration is itself a security-critical attack surface. If someone can **Open design questions:** - Where does agent policy live? In the repos it governs (as CLAUDE.md, agent config files)? In a separate policy repo? In a central configuration system? -- If policy lives in a separate repo, how does it get applied to target repos? Push-based (policy repo pushes to targets) or pull-based (agents read from policy repo at runtime)? +- If policy lives in a separate repo, how does it get applied to target repos? Push-based (policy repo pushes to targets) or pull-based (agents read from policy repo at runtime)? (Push-based, as pull requests: a preset published from an org-owned repo is recorded per repo and converged by `fullsend update` — decided in [ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md); enforcing a policy floor remains open.) - How do we audit changes to agent configuration? Git history helps if policy is in git, but we also need to detect unauthorized runtime changes. - How do we handle the bootstrap problem — who sets up the initial agent configuration for a new repo, and how is that initial setup secured? (Preset-based install and `config.base.yaml` / `config.yaml` layering decided in [ADR 0069](../ADRs/0069-ready-made-configuration-presets.md); workflow pinning and backend policy remain open.) From 76ef597abc01725ba5dd5d54fe12d47e0d2df536 Mon Sep 17 00:00:00 2001 From: Wayne Sun Date: Fri, 4 Sep 2026 10:51:53 -0400 Subject: [PATCH 2/2] docs(adr): rewrite ADR 0103 for readability, align living docs Short sentences, motivation first, one-sentence consequences, under 100 content lines. Restore the original architecture bullet and add a one-line decided statement; make architecture.md, governance.md and agent-infrastructure.md say what the ADR says (Renovate bumps the pins, the verb refreshes only tooling-owned files, any repo may host the preset). Assisted-by: Claude (draft) Signed-off-by: Wayne Sun --- ...ig-presets-converged-by-fullsend-update.md | 156 ++++++++---------- docs/architecture.md | 13 +- docs/problems/agent-infrastructure.md | 2 +- docs/problems/governance.md | 2 +- 4 files changed, 76 insertions(+), 97 deletions(-) diff --git a/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md b/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md index 2eb209e08b..755807413b 100644 --- a/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md +++ b/docs/ADRs/0103-shared-config-presets-converged-by-fullsend-update.md @@ -1,5 +1,5 @@ --- -title: "103. Shared configuration as recorded presets converged by fullsend update" +title: "103. Shared configuration as recorded presets bumped by Renovate" status: Accepted relates_to: - governance @@ -11,7 +11,7 @@ topics: - presets --- -# 103. Shared configuration as recorded presets converged by fullsend update +# 103. Shared configuration as recorded presets bumped by Renovate Date: 2026-09-03 @@ -28,98 +28,78 @@ Accepted ## Context -[ADR 0044](0044-deprecate-per-org-installation-mode.md) made per-repo the -only installation mode and named the cost: no central place to share agent -configuration, and every repo bumped one at a time. The pieces meant to fill -that gap exist but do not connect: `config.base.yaml` -([ADR 0069](0069-ready-made-configuration-presets.md)) is written by -`github setup --config` and nothing records where it came from; `agent -update` skips `base:` pins in local harness files and never regenerates -`lock.yaml` (#5433, #5802, #6191); `repos install` -([ADR 0074](0074-repos-command-consolidation.md)) has no preset concept. -Two readers — the dispatch Route job's `yq` gates and `agent -add/update/remove` — read only the overlay, so ADR 0069's layering never -reaches them (#6422's class); those are fixed as prerequisites, not decided -here, and the `agent` one must land before any preset ships agents: today -`agent add` copies preset entries into `config.yaml`, where they shadow -the preset forever. The fullsend-ai org replaced a working Renovate custom manager -(fullsend-ai/.fullsend#174) with a workflow that `sed`-rewrites -`config.yaml` across a hard-coded repo list, pushes to `main` through a -ruleset-bypass App at agents `main` HEAD, and leaves stale hashes on every -non-harness path. +An organization wants one shared agent configuration for many repos, and +wants each repo to pick up updates automatically, the way Renovate already +bumps its packages and GitHub Actions pins. Per-repo installation +([ADR 0044](0044-deprecate-per-org-installation-mode.md)) removed the org +config repo that used to do this and left nothing in its place. -GitHub Agentic Workflows solves this with a central `agentic-workflows` -repo, a `source:` field in each installed workflow, and `gh aw update --org ---create-pull-request`. It needs a three-way merge because one file mixes -managed fields with user edits (it already carves `source` out of the -merge), a compile step because pins live in generated `.lock.yml`, and it -infers the tracking strategy from the ref's shape — which resolves every -`add` to a SHA and so tracks branch HEAD by default. +The building blocks exist but do not connect. A shared +`config.base.yaml` can be installed with `github setup --config` +([ADR 0069](0069-ready-made-configuration-presets.md)), but nothing records +where it came from, so nothing can refresh it. Agent pins carry a commit +SHA and a `#sha256=` hash, but `agent update` does not re-pin `base:` in +local harness files and does not regenerate `lock.yaml` (#5433, #5802). +No command checks pins in CI (#6191). `repos install` +([ADR 0074](0074-repos-command-consolidation.md)) does not know presets. + +Today the fullsend-ai org fills the gap with a workflow that `sed`-rewrites +`config.yaml` in a hard-coded list of repos and pushes to `main` through a +ruleset-bypass App. It always tracks `main`, offers no review, and leaves +stale hashes on any path outside `harness/`. It replaced a Renovate custom +manager that did the bump correctly but ran in only one repo +(fullsend-ai/.fullsend#174). Two readers also ignore the base layer and +are fixed as prerequisites, not decided here: the dispatch Route job reads +only `config.yaml` (#6422's class), and `agent add/update/remove` copy +preset entries into `config.yaml`, where they shadow the preset. ## Decision -Shared fullsend configuration is a **versioned preset**, recorded in each -consuming repository by provenance, and **bumped by Renovate** — the same -job that bumps the repo's packages and its workflow pins — with one -first-party verb doing the fullsend-specific part. +Shared configuration is a **preset**: a versioned `config.base.yaml` in +any repo, recorded in each consuming repo by provenance, and bumped by that +repo's own Renovate job. -1. **A preset is a git-hosted `config.base.yaml`** plus the harness bases, - skills and policies it references by URL, schema-validated on fetch. It - lives in whichever repo already hosts the org's shared Renovate config; - no dedicated org repo, no org-level workflow and no enrollment list are - needed — the `/.fullsend` repo of - [ADR 0003](0003-org-config-repo-convention.md) is one possible host, - not a requirement. -2. **Every pin is a Renovate-bumpable string, and the verb never writes - human-owned files.** `config.yaml` and local harness files are the human - layer. `config.base.yaml` (byte-identical to the fetched preset, so - `--config-hash` keeps working), `lock.yaml`, the shim, and a new - `.fullsend/preset.lock.yaml` whose `source` URL carries the tracked - ref, with the resolved SHA and `sha256` derived from it, are the bot - layer, rewritten whole. - `github setup --config` creates that record from a blob URL on a branch - or tag, resolved the way `agent add` resolves one. Carving every managed - field into its own file is what removes the three-way merge; run-time - resolution removes the compile step. -3. **fullsend ships a Renovate preset** (`custom.regex` managers for the - preset record, `agents[].source` URLs and harness `base:` URLs — never - `config.base.yaml` — with `git-refs` / `github-tags` datasources) that a - repo's `renovate.json` extends. Renovate rewrites the pin strings; - **one idempotent verb, `fullsend update`**, run as its `postUpgradeTasks` - command, does the derived work: re-fetch the preset into - `config.base.yaml`, recompute every `#sha256=`, regenerate `lock.yaml` - without re-resolving unchanged dependencies. Run by hand it also bumps - the shim ref; `--check` writes nothing and exits non-zero on drift from - a tag or SHA. `repos install` applies the preset on a fresh install. -4. **Tracking strategy and cadence are Renovate's**, not new fullsend - fields: `packageRules` per dependency choose branch, major tag, exact tag - or SHA, `minimumReleaseAge` gates adoption, `schedule` sets cadence, and - the org's shared Renovate preset carries those choices to every repo. - The Route job reads both config layers so a preset can carry policy. +1. **Preset.** The preset is a `config.base.yaml` plus the harness bases, + skills and policies it references by URL. It lives in whichever repo + hosts the org's shared Renovate config. There is no dedicated org repo, + no org-level workflow and no enrollment list. +2. **Two kinds of files.** Humans own `config.yaml` and local harness + files. Tooling owns `config.base.yaml` (a byte-identical copy of the + preset), `lock.yaml`, the shim, and a new `.fullsend/preset.lock.yaml` + whose `source` URL names the preset and its tracked ref; the resolved + SHA and `sha256` are derived from it. Tooling never writes the human + files. That split is why no three-way merge and no compile step are + needed. +3. **Renovate bumps the pins.** fullsend ships a Renovate preset with + `custom.regex` managers for the preset record, `agents[].source` URLs + and harness `base:` URLs (never `config.base.yaml`). A repo's + `renovate.json` extends it. Renovate's built-in `github-actions` manager + already bumps the shim. +4. **`fullsend update` does the rest.** Renovate runs it as the + `postUpgradeTasks` command. It re-fetches the preset into + `config.base.yaml`, recomputes every `#sha256=`, and regenerates + `lock.yaml` without re-resolving unchanged dependencies. Run by hand it + also bumps the shim ref. `--check` writes nothing and exits non-zero on + drift from a tag or SHA. `repos install` applies the preset on a fresh + install. +5. **Policy is Renovate's.** Which ref to track, how long to wait + (`minimumReleaseAge`) and how often to run (`schedule`) are ordinary + Renovate `packageRules`, shared through the org's Renovate preset. -Repos with no explicit `agents:` entry already follow the fullsend build tag -for first-party agents; for them the shim bump is the agent bump. Agents -generated by `fullsend agent new` (#6966) are local files with no -provenance and are left alone except for their `image:` pin. +Repos with no explicit `agents:` entry follow the fullsend build tag for +first-party agents, so the shim bump is their agent bump. Agents generated +by `fullsend agent new` (#6966) are local files and are left alone. ## Consequences -- Agent and preset bumps arrive as ordinary Renovate pull requests next to - package and action bumps, replacing `sed` scripts, direct pushes and - ruleset-bypass Apps; #5433, #5802 and #6191 close on the verb, #6597 and - #6607 become Renovate policy. -- `postUpgradeTasks` runs only on self-hosted Renovate with an anchored - `allowedCommands` entry and the shell executor off, so each repo's - Renovate job (not the Mend-hosted App) runs the verb; a repo without - Renovate runs `fullsend update` by hand. The fullsend-ai sync workflows - are retired. -- Orgs get gh-aw's configurable sharing model without a compile phase; the - org tier ADR 0044 removed returns as a preset URL plus a Renovate preset, - not a config repo. -- The preset URL and its refs are a supply-chain trust surface: the - `allowed_remote_resources` union-with-deny-all floor and `--config-hash` - apply; signing and a `redirect:` for relocated presets are follow-ons. -- Enforcing a policy floor (refusing to run when a repo drifts) is a separate - decision; this ADR only makes drift visible. The model maps onto Tekton - remote resolution (git resolver `revision`, bundle digests) if stages - later run as Tekton tasks. +- Agent and preset bumps arrive as ordinary Renovate pull requests, and the + fullsend-ai sync workflows are retired. +- #5433, #5802 and #6191 close on the verb; #6597 and #6607 become + Renovate policy. +- `postUpgradeTasks` needs self-hosted Renovate with an anchored + `allowedCommands` entry, so a repo without Renovate runs `fullsend + update` by hand. +- The preset URL is a supply-chain trust surface covered by + `allowed_remote_resources` and `--config-hash`; signing is a follow-on. +- Refusing to run when a repo drifts from its preset is a separate + decision; this ADR only makes drift visible. diff --git a/docs/architecture.md b/docs/architecture.md index ce61b2feb3..19af2441a5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -57,7 +57,8 @@ the dedicated org-level `/.fullsend` config repo is deprecated - Event-driven stage dispatch: eliminate `workflow_dispatch` + `gh workflow run` fan-out from `dispatch.yml` in favor of synchronous `workflow_call` so the dispatched run stays linked to the caller ([ADR 0041](ADRs/0041-synchronous-workflow-call-event-dispatch.md)). - Multi-repo management: a `fullsend repos` subcommand group with a declarative `repos.yaml` manifest for managing per-repo installations at scale — install, convergence (provision, sync, upgrade), status, and uninstall across repos and orgs ([ADR 0057](ADRs/0057-repos-management.md), [ADR 0074](ADRs/0074-repos-command-consolidation.md)). - Dispatch version-skew resolution: per-repo `reusable-dispatch.yml` inlines stage workflow jobs directly, eliminating `@v0` references to `reusable-{stage}.yml` ([ADR 0062](ADRs/0062-dispatch-version-skew.md)). -- Ready-made configuration presets: `fullsend github setup --config ` installs a vendor preset as `.fullsend/config.base.yaml` and a stub `.fullsend/config.yaml` overlay in the target repository; a bot-owned `.fullsend/preset.lock.yaml` records the preset's source, tracking ref and hash; every pin is a Renovate-bumpable string, fullsend ships a Renovate preset with the custom managers, and Renovate runs `fullsend update` as its post-upgrade task to re-fetch the preset, recompute `#sha256=`, re-pin `base:` in local harness files and regenerate `lock.yaml` (`--check` reports drift) ([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)); mint URL, inference backend, and related settings live in configuration files resolved through accessor methods, not CLI flags. Shared-infrastructure presets will reduce per-adopter enrollment (target state): mint via `job_workflow_ref` trust per [ADR 0059](ADRs/0059-public-mint-mode-with-wildcard-allowlists.md); inference authorization model undecided ([ADR 0069](ADRs/0069-ready-made-configuration-presets.md)); enrollment remains required until follow-on ADRs land. +- Ready-made configuration presets: `fullsend github setup --config ` installs a vendor preset as `.fullsend/config.base.yaml` and a stub `.fullsend/config.yaml` overlay in the target repository; mint URL, inference backend, and related settings live in configuration files resolved through accessor methods, not CLI flags. Shared-infrastructure presets will reduce per-adopter enrollment (target state): mint via `job_workflow_ref` trust per [ADR 0059](ADRs/0059-public-mint-mode-with-wildcard-allowlists.md); inference authorization model undecided ([ADR 0069](ADRs/0069-ready-made-configuration-presets.md)); enrollment remains required until follow-on ADRs land. +- Shared presets bumped by Renovate: a bot-owned `.fullsend/preset.lock.yaml` records the preset's source and tracked ref; Renovate custom managers bump the preset record, `agents[].source` and harness `base:` pins, and run `fullsend update` as a post-upgrade task to refresh `config.base.yaml`, recompute hashes and regenerate `lock.yaml` ([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)). - GitLab event dispatch: two-path model — native CI triggers (`merge_request_event`) for MR events, cron-based polling for issues/comments/labels. No external infrastructure (no webhook bridge). Bot PAT stored as a protected CI/CD variable. Per-repo only ([ADR 0067](ADRs/0067-gitlab-cron-polling-event-dispatch.md)). **Open questions:** @@ -533,12 +534,10 @@ Fullsend uses a three-tier configuration inheritance model for all configuration In per-repo installation the middle tier is replaced by files inside the target repo: `.fullsend/config.base.yaml` (vendor preset or baseline) and `.fullsend/config.yaml` (repo overlay), with code defaults below both. The -preset is hosted in any repo, typically the one holding the org's shared -Renovate config — no dedicated org repo or org-level workflow; -`config.base.yaml`, `preset.lock.yaml`, `lock.yaml` and the shim are -bot-owned and rewritten whole by Renovate running `fullsend update`; -`config.yaml` and local harness files are human-owned, and the dispatch -Route job reads both config layers ([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)). The +preset may live in any repo; Renovate bumps its pins and `fullsend update` +refreshes the tooling-owned files (`config.base.yaml`, `preset.lock.yaml`, +`lock.yaml`), never `config.yaml` or local harness files +([ADR 0103](ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)). The org-tier box above describes the historical per-org model, now deprecated ([ADR 0044](ADRs/0044-deprecate-per-org-installation-mode.md), [ADR 0069](ADRs/0069-ready-made-configuration-presets.md)). diff --git a/docs/problems/agent-infrastructure.md b/docs/problems/agent-infrastructure.md index c3e65edca9..8b9b28e590 100644 --- a/docs/problems/agent-infrastructure.md +++ b/docs/problems/agent-infrastructure.md @@ -113,7 +113,7 @@ Many fullsend scenarios skew toward **ephemeral, task-scoped** execution (triage - **Agent architecture** — Instance topology (per-repo vs shared) and “local vs remote” for pre-PR review depend on what infrastructure we have. Infrastructure enables or constrains those choices. - **Security threat model** — Isolation and “separate execution environments” are implemented by this layer. Supply chain (what base images and dependencies the runtime uses) also lives here. -- **Governance** — Policy may be applied at runtime by agents reading from a policy repo; infrastructure determines where that runtime runs and how it accesses policy. Shared configuration reaches per-repo installs as a recorded preset converged by `fullsend update` ([ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)); the same pinned-remote-reference model maps onto Tekton remote resolution if stages later run there. +- **Governance** — Policy may be applied at runtime by agents reading from a policy repo; infrastructure determines where that runtime runs and how it accesses policy. Shared configuration reaches per-repo installs as a recorded preset bumped by Renovate ([ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md)); the same pinned-remote-reference model maps onto Tekton remote resolution if stages later run there. - **Repo readiness** — Repos need reliable CI and signals; agent infrastructure may consume or depend on the same CI (e.g. for “run tests” or “run linters”) and should not conflict with it. Headless runtimes amplify **feedback latency** and **workspace handoff** costs when CI is the only execution path. ## Open questions diff --git a/docs/problems/governance.md b/docs/problems/governance.md index 85f5ced262..77a690da08 100644 --- a/docs/problems/governance.md +++ b/docs/problems/governance.md @@ -29,7 +29,7 @@ Agent configuration is itself a security-critical attack surface. If someone can **Open design questions:** - Where does agent policy live? In the repos it governs (as CLAUDE.md, agent config files)? In a separate policy repo? In a central configuration system? -- If policy lives in a separate repo, how does it get applied to target repos? Push-based (policy repo pushes to targets) or pull-based (agents read from policy repo at runtime)? (Push-based, as pull requests: a preset published from an org-owned repo is recorded per repo and converged by `fullsend update` — decided in [ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md); enforcing a policy floor remains open.) +- If policy lives in a separate repo, how does it get applied to target repos? Push-based (policy repo pushes to targets) or pull-based (agents read from policy repo at runtime)? (Push-based as Renovate pull requests, from a preset hosted in any repo — decided in [ADR 0103](../ADRs/0103-shared-config-presets-converged-by-fullsend-update.md); agents still read the merged layers at runtime, and enforcing a policy floor remains open.) - How do we audit changes to agent configuration? Git history helps if policy is in git, but we also need to detect unauthorized runtime changes. - How do we handle the bootstrap problem — who sets up the initial agent configuration for a new repo, and how is that initial setup secured? (Preset-based install and `config.base.yaml` / `config.yaml` layering decided in [ADR 0069](../ADRs/0069-ready-made-configuration-presets.md); workflow pinning and backend policy remain open.)