From 9d6952a77244e4d7453df1190069d42c234f2934 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sun, 1 Nov 2026 17:44:47 +0000 Subject: [PATCH] Plan credential preflight for forge delivery --- docs/roadmap/forge-boundary.md | 58 +++++++-- docs/roadmap/implementation.md | 221 +++++++++++++++++++++++++++------ 2 files changed, 231 insertions(+), 48 deletions(-) diff --git a/docs/roadmap/forge-boundary.md b/docs/roadmap/forge-boundary.md index 473c267..104f709 100644 --- a/docs/roadmap/forge-boundary.md +++ b/docs/roadmap/forge-boundary.md @@ -1,4 +1,4 @@ -# Roadmap: separate shared delivery from forge adapters +# Roadmap: forge delivery boundary and credential preflight Status: accepted; implementation has not started. @@ -10,6 +10,8 @@ choices, task sequencing, and verification. Introduce an internal boundary between shared hosted-release delivery and the Gitea implementation. The result should support adding another forge through an adapter while reusing ReleaseKit's release and asset-delivery policy. +Add operation-specific credential preflight through that boundary so delivery +stops on credential problems before expensive preparation or builds. ReleaseKit's `Publish` operation validates and publishes source through Git. Its `Deliver` operation verifies tagged source and delivers release notes and @@ -42,6 +44,10 @@ and the subsequent token-file and forbidden-file changes. - CLI credential resolution supplies an explicit token from the configured environment variable or token file. The profile describes a Gitea repository; it has no forge selector. +- CLI loading checks token availability, but forge API authentication first occurs + through delivery requests after application dependency preparation and managed + builds. Library delivery also checks a blank token only at the later Gitea + request boundary. There is no dedicated authenticated credential preflight. The [Gitea integration guide](../integrations/gitea.md) and public Go declarations document the baseline contracts. Current behavioral tests provide regression @@ -68,6 +74,7 @@ and protocol requirements are implemented. Shared internal code owns: +- Automatic credential preflight coordination before expensive delivery work. - Captured source identity, local tag/commit checks, committed release notes, managed or prebuilt artifact coordination, and local artifact verification. - The existing logical rules for accepting matching hosted content, reporting @@ -81,16 +88,46 @@ generic delivery workflow has no dependency on `internal/gitea`. The facade or private composition code constructs the concrete backend and supplies it to shared delivery. +### Credential preflight + +Delivery automatically checks the credentials required for its selected backend. +Local checks establish availability and valid destination settings; a read-only +backend probe establishes authentication and access to the configured repository. +Reliable evidence of insufficient required permissions stops the operation. +Unexposed permissions remain unverified; successful reads establish no blanket +guarantee that later writes will succeed. Preflight performs no test writes and +preserves ordinary delivery authorization and recovery handling. + +Library and ordinary CLI delivery perform preflight before managed builds and +full artifact verification. CI loads the verified tagged profile first, then +performs preflight before application dependency preparation. A callable facade +operation lets consumers check a destination before their own preparation, +without requiring a release tag or note to exist. + +Failures distinguish missing or rejected credentials, insufficient permissions, +an inaccessible destination, and unavailable or unverifiable service responses. +Delivery reports `not_attempted` for these pre-write failures. Diagnostics retain +useful causes while excluding tokens. Each check describes access at that time; +later operations retain their normal checks. + +Required credentials follow operation scope. Candidate checking and standalone +builds remain credential-free. Source publication continues using Git's native +SSH/HTTPS authentication and existing remote guards, independently of hosted +delivery credentials. This feature adds no Gitea-token requirement to `Publish`. + ### Backend responsibilities The Gitea adapter owns destination validation specific to Gitea, API routes, authentication headers, request and response formats, pagination, multipart uploads, attachment downloads, and HTTP-specific error interpretation. +It also owns authenticated credential probes and interpretation of available +permission evidence, using repository-scoped checks without requiring unrelated +account or administration scopes. -The internal contract supplies the logical operations needed to inspect a -hosted tag, find or create a release, enumerate assets, read asset content, and -upload an asset. Release and asset references carry backend-owned handles; -shared orchestration works with logical identity and content rather than +The internal contract supplies the logical operations needed to preflight +credentials, inspect a hosted tag, find or create a release, enumerate assets, +read asset content, and upload an asset. Release and asset references carry +backend-owned handles; shared orchestration works with logical identity and content rather than interpreting Gitea's positive integer IDs. The contract also carries the evidence needed for existing recovery behavior: @@ -116,13 +153,16 @@ and follow-up lookup behavior. Local verification reuse, streaming, cancellation, stream closure, temporary build ownership, and upload cleanup remain part of the affected workflow. +Read-only credential preflight precedes expensive local artifact work; local +content verification still completes before hosted release or asset writes. Credential environment/file selection and forbidden-file validation retain their existing responsibilities. Backend-specific address validation stays with that backend. -The public destination replacement and required forge selection are deliberate -API and configuration changes for the next minor release. Migration guidance -describes the consumer edits under the project's direct-cutover approach. +The public destination replacement, required forge selection, and earlier +credential failures are deliberate changes for the next minor release. +Migration guidance describes consumer edits under the project's direct-cutover +approach. Current contract documents and examples change with implementation; historical release notes retain their original contents. @@ -130,6 +170,8 @@ Completion means shared delivery and asset policy have independent internal ownership, Gitea implements the backend boundary, existing Gitea contracts work through it, and the neutral public destination and explicit selection are consistent across the API, configuration, examples, and documentation. +Credential preflight operates through the backend boundary in both library and +CLI delivery, with accurate failure distinctions and documented permission limits. Additional forge implementations and compatibility claims belong to later work. Release preparation and publication continue under the diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 2c4c152..27e3d3a 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,4 +1,4 @@ -# Implementation plan: neutral forge delivery boundary +# Implementation plan: neutral forge delivery and credential preflight Status: ready for implementation; all stages are pending. @@ -6,14 +6,15 @@ Status: ready for implementation; all stages are pending. Implement the target in the [feature roadmap](forge-boundary.md). That document owns scope and user choices; this plan owns concrete design and ordered work. -The baseline is `71746f4`: Gitea delivery works, token files and -`forbidden_files` are implemented, and the neutral destination is planned. +The planning baseline is `44823ce`; source implementation remains unchanged +from `ac78e7d`. Gitea delivery, token files, and `forbidden_files` work today. +The neutral destination and credential preflight are planned. -Execute Stage 1 through Stage 9 in order as one end-to-end sprint. Each stage +Execute Stage 1 through Stage 10 in order as one end-to-end sprint. Each stage is a focused Luna implementation task. Intermediate stages may leave callers or tests uncompilable while responsibilities move. Continue without an intermediate release, pilot, external action, or human review. Run focused -checks when the affected packages compile; Stage 9 owns final assembled +checks when the affected packages compile; Stage 10 owns final assembled verification. Record completion here and follow the [agent policy](../policy/agents.md) for commits and validation reporting. @@ -74,7 +75,8 @@ its profile as today, then checks the forge before token loading and application dependency preparation. Ordinary `deliver` checks it before loading receipts, building, or making hosted requests. Library selection failure returns `DeliveryNotAttempted` with the requested version and an error before invoking -the generic workflow. No new public error type is needed. +the generic workflow. Credential preflight failures use the classification +described below. After explicit Gitea selection, retain `RELEASEKIT_GITEA_TOKEN` as the CLI's existing default token environment name. Preserve configured names, environment @@ -86,15 +88,83 @@ and adds `"forge": "gitea"` to each delivery profile. Use a direct API cutover without a legacy `GiteaDestination` alias or implicit-selection compatibility path. +### Credential preflight contract and ordering + +Add `PreflightDelivery(ctx context.Context, destination DeliveryDestination) error` +to the root facade. It checks the selected destination without requiring source, +version, tag, note, or artifact inputs. `Deliver` automatically uses the same +internal preflight implementation; callers do not need to invoke the standalone +operation for ordinary delivery. + +Use a facade-owned `DeliveryPreflightError` with `Kind` and `Err` fields, +`Error`, and `Unwrap`. Its named string kind, `DeliveryPreflightFailureKind`, +has these constants: + +| Constant | Value | +| --- | --- | +| `DeliveryPreflightMissingCredential` | `missing_credential` | +| `DeliveryPreflightInvalidCredential` | `invalid_credential` | +| `DeliveryPreflightInsufficientPermissions` | `insufficient_permissions` | +| `DeliveryPreflightInaccessibleDestination` | `inaccessible_destination` | +| `DeliveryPreflightUnavailable` | `unavailable` | + +Convert internal classifications explicitly and retain causes and cancellation +identity without exposing internal types in +exported signatures. The unavailable category includes network/service failures +and malformed responses that prevent verification. A 404 can mean hidden or +missing repository access; diagnostics must preserve that ambiguity. A 403 +establishes denied access, not necessarily an expired or invalid token. + +Preflight checks local destination rules and nonblank credentials, then invokes +the backend's read-only authenticated repository probe. It fails on rejected +credentials, inaccessible target access, and reliably established deficiencies +in permissions needed for delivery. Scope checks to release/asset delivery; +code-push permission alone is not an equivalent release permission. Unexposed +token scopes or write permissions remain unverified and do not independently +fail preflight. A nil error establishes authentication and repository access +plus no known delivery-permission deficiency; it does not certify every future +write. Preserve ordinary authorization and write recovery. Do not create test +releases, upload test assets, or request extra user/admin token scopes. + +Library delivery performs cheap request/source/note checks, then credential +preflight before managed builds or full artifact size/digest verification. +Ordinary CLI delivery resolves credentials as today and relies on this automatic +library preflight. Reading a prebuilt receipt can remain a cheap earlier step. +After preflight succeeds, full local content verification still precedes every +hosted release/asset write. This deliberately permits the earlier read-only +credential probe before local artifact verification. + +For binary `ci-deliver`, load the verified tagged profile and resolve credentials, +invoke the standalone facade preflight, then prepare application dependencies. +Subsequent `Deliver` performs its normal preflight before building; that later +check is fresh after network-capable dependency preparation. Page-only CI has +no application dependency preparation and can rely on `Deliver` alone. No +long-lived validation cache or public preflight-bypass setting is needed. + +Automatic preflight failure leaves delivery `not_attempted`, including service +unavailability; unknown-write outcomes remain reserved for unresolved writes. +CLI missing/invalid credentials, established insufficient permissions, and +inaccessible destinations return status 2. Unavailable verification and +cancellation return status 1 with their actual causes. Existing local profile +errors keep status 2. Document these earlier failures and classifications as +part of the next minor release's operational compatibility impact. + +`check` and standalone `build` acquire no credentials or online preflight. +`publish` retains Git's native SSH/HTTPS authentication and remote guards; it +does not invoke hosted delivery preflight or require a forge token. Consumers +can explicitly call `PreflightDelivery` before their own preparation or source +publication without making that a new mandatory publication gate. Application +dependency credentials stay with the existing Go/Git preparation mechanisms. + ### Internal ownership and dependencies | Owner | Responsibility after the sprint | | --- | --- | -| Root facade | Public destination, explicit selection, concrete backend construction, request/result conversion. | -| `internal/forge` (new) | Backend interface, observed release/asset values, opaque handles, neutral failure classifications, and write evidence. | +| Root facade | Public destination and callable preflight, explicit selection, concrete backend construction, request/result/error conversion. | +| `internal/forge` (new) | Backend interface, preflight classifications, observed release/asset values, opaque handles, neutral failures, and write evidence. | | `internal/artifact` | Existing build identity helpers plus expected local assets, manifests, declaration validation, and local size/digest verification. | -| `internal/delivery` | Local source/build coordination, hosted tag comparison, page matching/creation recovery, remote content comparison, upload sequencing/recovery, observations, and final outcomes. | -| `internal/gitea` | Destination rules, HTTP operations, codecs, protocol acknowledgments, authentication and redirects, multipart streaming, and protocol-to-neutral failure conversion. | +| `internal/delivery` | Automatic preflight coordination, local source/build coordination, hosted tag comparison, page matching/creation recovery, remote content comparison, upload sequencing/recovery, observations, and final outcomes. | +| `internal/gitea` | Destination rules, authenticated preflight probes and permission evidence, HTTP operations, codecs, protocol acknowledgments, authentication and redirects, multipart streaming, and protocol-to-neutral failure conversion. | | CLI | Profile adaptation, token loading, receipts, preparation, presentation, and exit statuses. | Both delivery and Gitea depend on `internal/forge`; that contract imports neither @@ -109,6 +179,7 @@ implementation choices. | Operation | Inputs and outputs | | --- | --- | +| `Preflight` | Context; checks the backend's configured credentials/repository through read-only requests and returns a classified preflight error or nil. | | `TagCommit` | Context and full tag; returns observed commit or a classified error. | | `FindRelease` | Context and full tag; returns logical release data or a classified not-found/error. | | `CreateRelease` | Context and requested tag, commit, title, body, draft/prerelease values; returns observed release, write evidence, and error. | @@ -151,8 +222,9 @@ buffering of complete binaries. Status: pending. 1. Add `internal/forge` with the interface and logical values specified above. - Define neutral failure and write-evidence types with internal comments about - not-found, accepted acknowledgments, uncertain writes, opaque handles, and + Define preflight classifications, neutral failures, and write-evidence types + with comments about not-found, accepted acknowledgments, uncertain writes, + opaque handles, and stream ownership. Use ordinary Go values and error wrapping. 2. Keep this package concerned with the delivery seam; production backend selection belongs to the facade. Avoid a registry, capability negotiation, @@ -186,7 +258,7 @@ Status: pending. Preserve rejection of same-size local tampering. Result: local assets are declared and verified independently of Gitea. Retain -one full local size/digest preflight before hosted requests and reuse it for +one full local size/digest preflight before hosted writes and reuse it for uploads; subsequent transport reads stream content without a second full preflight. This preserves existing efficiency/integrity choices rather than adding a guarantee against concurrent file replacement. @@ -210,12 +282,49 @@ Status: pending. introduce pagination or a new response-completeness guarantee. 4. Retain local HTTP tests for routes, authentication, escaped tags, metadata decoding, malformed lists, redirects, and unreadable assets. Adapt tests to - the read operations; shared comparison tests move in Stage 6. + the read operations; shared comparison tests move in Stage 7. Result: backend reads expose observations and streams. Content equality, tag comparison, page acceptance, and delivery outcomes belong to shared policy. -## Stage 4: Extract Gitea write operations and evidence +## Stage 4: Implement read-only Gitea credential preflight + +Status: pending. + +1. Implement backend `Preflight` with local destination/token validation and + authenticated repository-scoped reads. Perform no source/tag checks here so + the same probe works before a release tag exists. +2. Resolve the configured repository with `GET /repos/{owner}/{repo}`, then + use its ID with the authenticated `GET /repositories/{id}` route. Validate + the required repository identity and successful response shape. The second + route requires authentication and repository scope, so public anonymous + repository access alone cannot satisfy preflight. Keep headers, IDs, and + route details within Gitea. See the upstream + [route definitions](https://github.com/go-gitea/gitea/blob/release/v1.26/routers/api/v1/api.go). +3. Interpret reliable release/asset permission evidence when available. Gitea's + basic `permissions.push` describes code pushes, and a successful repository + GET does not establish `write:repository` token scope. Treat unexposed + release-write/token-scope evidence as unverified. Do not equate absent + metadata with denial or add `/user` probes requiring unrelated user scope. + The upstream [repository model](https://github.com/go-gitea/gitea/blob/release/v1.26/modules/structs/repo.go) + and [scope documentation](https://docs.gitea.com/development/api-usage/) + describe those distinctions. Keep later write authorization authoritative. +4. Classify missing/blank credentials locally, server-rejected authentication, + permission denial, inaccessible/hidden destinations, and unavailable or + malformed verification responses. Preserve cancellation and useful HTTP + causes without including the token. Unsupported/malformed destination + settings retain actionable local configuration errors. +5. Use a local HTTP fixture to cover accepted repository-scoped credentials, + invalid tokens, denied/missing repositories, service/decoding failure, and + cancellation. Demonstrate that anonymous public access cannot pass the + authenticated step and that unexposed write scopes are not claimed as + verified or treated as a new failure. Assert read-only behavior at this seam. + +Result: the backend checks authentication and repository access before costly +delivery work and accurately represents the limits of available permission +evidence. It uses no extra account/admin credentials or destructive probe. + +## Stage 5: Extract Gitea write operations and evidence Status: pending. @@ -231,7 +340,7 @@ Status: pending. are not attempted. Shared policy decides how to recover. 3. Validate Gitea acknowledgments here, including positive IDs and the uploaded asset's selected filename. Leave requested release tag/title/body/stability - comparison to Stage 5. A decoded acknowledgment that fails shared comparison + comparison to Stage 6. A decoded acknowledgment that fails shared comparison can still require reconciliation. 4. Accept a borrowed content reader for upload. Move file/inline-reader selection to shared code and preserve multipart streaming, pipe closure, writer @@ -239,19 +348,20 @@ Status: pending. content pass. 5. Keep HTTP tests for request bodies, uploads, malformed acknowledgments, known rejections, ambiguous writes, and cancellation at this boundary. Recovery - orchestration tests move with policy in Stages 5 and 6. + orchestration tests move with policy in Stages 6 and 7. Result: individual writes return evidence. Follow-up lookups, content comparison, and continuation decisions belong to shared delivery. Remove the adapter's old orchestration as its callers move rather than retaining a parallel policy path. -## Stage 5: Move hosted page policy and recovery +## Stage 6: Move hosted page policy and recovery Status: pending. 1. Add shared page coordination in `internal/delivery` using `forge.Backend`. - After existing local source/build preflight, obtain the hosted full tag's - commit and compare it with the captured commit before page writes. Find the + After credential preflight and local source/build verification, obtain the + hosted full tag's commit and compare it with the captured commit before + page writes. Find the release, accepting the same full tag/body/stable non-draft content and retaining an existing title. Only a missing release triggers create. 2. Supply the full note and H1 title to `CreateRelease`. Apply shared comparison @@ -281,7 +391,7 @@ Result: page behavior runs through the seam. Current Gitea recovery remains unchanged; this does not promise a fixed HTTP request count for future logical backend methods. -## Stage 6: Move remote asset comparison and upload recovery +## Stage 7: Move remote asset comparison and upload recovery Status: pending. @@ -321,7 +431,7 @@ Result: shared code owns asset policy/recovery. Remove the former Gitea their callers/tests move; keep transport helpers needed by the backend. These internal entry points have no public compatibility obligation. -## Stage 7: Connect the neutral facade and CLI +## Stage 8: Connect the neutral facade and CLI Status: pending. @@ -329,28 +439,39 @@ Status: pending. current `GiteaDestination` uses. The private facade factory switches on the explicit forge and constructs Gitea; invalid selection returns the described early result. -2. Change the internal delivery request to receive the backend. Replace Gitea +2. Implement `PreflightDelivery` and `DeliveryPreflightError` conversions using + that same backend factory and preflight implementation. Support destination + checking without a repository checkout or published tag. Preserve causes + with `Unwrap` and keep all exported named types facade-owned. +3. Change the internal delivery request to receive the backend. Replace Gitea asset/error references in request/result conversion and build validation. Pass delivery's single captured module/source identity through its existing workflow. Retain local source rechecks, temporary output cleanup, and - prebuilt output ownership. -3. Add `Forge` to CLI delivery settings with JSON key `forge`. Validate selection + prebuilt output ownership. Invoke backend preflight after cheap local checks + and before managed builds/full artifact hashing. Convert its failures to + the facade error and retain `not_attempted` delivery state. +4. Add `Forge` to CLI delivery settings with JSON key `forge`. Validate selection during delivery adaptation before credentials or subsequent delivery work, returning status 2 for invalid selection. Map the facade's supported value; preserve strict decoding and non-delivery behavior. -4. Update `deliveryDestination`, `deliverWithDestination`, ordinary delivery, +5. Update `deliveryDestination`, `deliverWithDestination`, ordinary delivery, and CI delivery. Retain tagged profile loading, module/tag checks, dependency preparation, receipt binding, and output/exit classification. Keep token resolution in the CLI with current precedence and Gitea environment default. -5. Add `"forge": "gitea"` to ReleaseKit's profile, maintained delivery profiles, + For binary CI, invoke `PreflightDelivery` after tagged profile/credential + loading and before dependency preparation. Ordinary and page-only delivery + rely on automatic library preflight; subsequent binary CI delivery rechecks + after dependency preparation. Apply the preflight exit classifications above. +6. Add `"forge": "gitea"` to ReleaseKit's profile, maintained delivery profiles, examples, and active test fixtures. Check/build-only profiles can keep omitting delivery. Keep token names, file paths, and Woodpecker secret mappings. Result: both interfaces select Gitea explicitly through a neutral destination -and share generic orchestration. CI preparation retains its earlier position -because it establishes which tagged profile is authoritative. +and share generic orchestration and automatic credential preflight. CI checkout +preparation retains its earlier position because it establishes which tagged +profile is authoritative; credential verification precedes application downloads. -## Stage 8: Complete regression coverage and dependency checks +## Stage 9: Complete regression coverage and dependency checks Status: pending. @@ -363,44 +484,63 @@ Status: pending. assert status/semantic diagnostics and that token access, dependency preparation, and hosted work were not reached. Reuse existing non-delivery tests to show those commands still need neither selection nor credentials. -3. Use controlled backend tests to demonstrate shared page/asset policy without +3. Cover automatic and standalone preflight with controlled failures: library + and ordinary CLI failures precede builds/full artifact hashing and writes; + binary CI failures precede dependency downloads; page-only CI still checks + credentials; standalone checks need no checkout or tag. Assert typed error + kinds, causes, CLI status, and `not_attempted` even on unavailable service. + Verify unavailable permission metadata alone is not failure and no preflight + path performs test writes. Update affected "before hosted requests" fixtures + to permit credential reads while retaining assertions that local verification + precedes writes. Keep `check`, `build`, and source `publish` independent of + hosted credential preflight and preserve the existing native Git guards. +4. Use controlled backend tests to demonstrate shared page/asset policy without Gitea imports. Consolidate obsolete client-policy tests rather than retaining wrappers. Keep representative real-Gitea HTTP assembly tests for page creation and binary delivery/retry, exercising conversions and opaque handles. -4. Extend `internal/apicheck` with a focused check that shared delivery, forge +5. Extend `internal/apicheck` with a focused check that shared delivery, forge contracts, and artifact mechanics have no production dependency path to `internal/gitea`. Preserve export ownership/runtime checks, keeping new delivery-related packages out of the runtime dependency graph through the existing checking mechanism. Add a small violation fixture following current apicheck tests. -5. Review stream/error paths for cancellation identity, reader closure, +6. Review stream/error paths for cancellation identity, reader closure, multipart worker completion, and causes. Reuse cancellation tests; add a narrow regression for a gap introduced by moved ownership. Keep the default suite offline and credential-free. Result: selected requirements have meaningful coverage and clear test owners. -Run relevant package tests once assembled; Stage 9 owns the final full checks. +Run relevant package tests once assembled; Stage 10 owns the final full checks. -## Stage 9: Document the cutover and verify the sprint +## Stage 10: Document the cutover and verify the sprint Status: pending. 1. Add the next sequential ADR (currently `0006`) using the documentation policy's lightweight Nygard format. Record the shared-policy/backend boundary, - neutral facade-owned destination, explicit selection, alternatives, and - consequences. Keep progress in these roadmaps. + neutral facade-owned destination, explicit selection, credential preflight + ownership and evidence limits, alternatives, and consequences. Keep progress + in these roadmaps. 2. Update GoDoc, `docs/config.md`, `docs/cli.md`, and - `docs/consumers/delivery.md` for the neutral destination and selection. + `docs/consumers/delivery.md` for the neutral destination, selection, automatic + preflight, callable preflight, typed failures, and CLI exit classifications. Update current README/runbook/example snippets where affected. Keep Gitea wire/authentication details in `docs/integrations/gitea.md`; describe shared coordination in internal documentation linked from `docs/internal/overview.md`. Align architecture policy wording with the implemented boundary while preserving its broader constraints. + Document the authenticated repository probe and unverified write scopes in + the Gitea guide. Describe standalone preflight as a caller option; preserve + existing source-publication completion and credential requirements. 3. Write `docs/releases/v0.6.0.md`, the next minor release note, explaining the features, compatibility impact, and public type/profile migration. Follow the [release policy](../policy/release.md) and [runbook](../release.md) for note/footer requirements. Explain the unchanged Gitea token default and profile updates before tagging: CI reads the tagged profile. + Include fail-fast credential checks, additional read-only requests, failure + classifications, and the limited meaning of successful preflight. Explain + that local artifact verification now follows credential reads and still + precedes hosted writes. Preserve historical notes. Prepare documentation here; tagging, pushing, and publication remain separate release work. 4. Check maintained JSON/Go examples, profile-generating fixtures, and Woodpecker @@ -416,8 +556,9 @@ Status: pending. stage. Verify generic policy has no Gitea dependency; only the adapter interprets its handles/statuses; the assembled workflow retains recovery outcomes; current delivery profiles explicitly select Gitea; migration - instructions match the API/configuration; local preflight is reused; and - content comparison and transport remain streamed. + instructions match the API/configuration; credential failures stop before + costly delivery work; source publication remains independent; local content + verification is reused before writes; and comparison/transport remain streamed. 7. Mark stages complete as verified. Commit completed work under the agent policy, report validation and the final commit, and leave a clean workspace. Retain accurate completed plans for later release-preparation cleanup.