From d7879e68afdcd707b291cb5d15dc5d1bf431fd1f Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:02:39 +0200 Subject: [PATCH 01/20] =?UTF-8?q?plan(deploy-plugin):=20stage=20A=20bootst?= =?UTF-8?q?rap=20=E2=80=94=20supervisor=20identity,=20drift,=20worklog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .llm/runs/plan-deploy-plugin--seed/drift.md | 8 ++ .llm/runs/plan-deploy-plugin--seed/kickoff.md | 83 +++++++++++++++++++ .../plan-deploy-plugin--seed/supervisor.md | 57 +++++++++++++ .llm/runs/plan-deploy-plugin--seed/worklog.md | 12 +++ 4 files changed, 160 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/drift.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/kickoff.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/supervisor.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/worklog.md diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md new file mode 100644 index 000000000..917e445e2 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -0,0 +1,8 @@ +# Drift Log — plan-deploy-plugin--seed + +Append-only. Severity: `note` | `significant` | `architectural`. + +| ID | Date | Severity | Drift | Disposition | +| --- | --- | --- | --- | --- | +| D-1 | 2026-07-18 | note | `workflow/seed-run.md` stage A requires a draft PR as commit trail; kickoff stop-line forbids creating any PR. | Kickoff wins (owner-authored, verbatim, in force). Commit trail = branch `plan/deploy-plugin` pushed with explicit refspec. | +| D-2 | 2026-07-18 | note | Seed-run stages F (adversarial) and G (PLAN-EVAL) are replaced by a kickoff-defined pipeline: supervisor-dispatched Sol-xhigh constructive adversarial + Kimi-K3 doc-story, then generator resume. Formal PLAN-EVAL timing is a supervisor decision downstream. | Recorded; this session generates and stops at `STAGE-COMPLETE: generator`. | diff --git a/.llm/runs/plan-deploy-plugin--seed/kickoff.md b/.llm/runs/plan-deploy-plugin--seed/kickoff.md new file mode 100644 index 000000000..b7c5a29b2 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/kickoff.md @@ -0,0 +1,83 @@ +# Kickoff — plan-deploy-plugin--seed (Fable 5 · xhigh orchestrator) + +use harness. You are a **planning/seed orchestrator** (Fable 5, effort xhigh) producing a clean new +harness run for the NetScript **deploy plugin** concept. You are a generator of drafts and design +documents ONLY. + +## Owner ratification context (#824 redirect) + +The prior seed run `.llm/runs/plan-unified-runtime--seed/` (branch `plan/unified-runtime`, worktree +`/home/codex/repos/wt-g8-seed`) reached Stage-H with an adversarial nitro-vs-own analysis. The owner +read `adversarial-nitro-vs-own.md` and ratified a NEW direction. His conclusion, VERBATIM — this is +the concept your run is built on: + +> - it is true that we cannot achieve full Deno native no matter we choose nitro, existing Deno +> adapter (most are insufficient anyway) of imolemnt ourselves : the goal should be reframed as +> "Deni native first then Node compat where needed" +> - it is true that creating a giant new deploy package is not the appropriate solution, we've +> learnt from other topic like auth that the best way is to use Netscript at what's it's best : +> composability --> should we consider a deploy PLUGIN with a plugin core and then adapter the +> EXACT same way auth works ? would it make sense ? would it also allow us to sprinkle some nitro +> integration where it make sense, aspire native where it matters and implement ourselves the rest +> the absolute best possible way : entreprise grade like we did for other package (unless we find +> specifc well written and standard package that could be valuable to wrap as per our doctrine) +> - if we go the plugin route it refine COMPLETELY the deploy story including what we've already +> shipped but solves many problems : the plugin allow us to contribute to every layers (even +> frontend soon) meaning we could imagine SCAFFOLDING a cloudflare optimized project that would +> ships seams that already are cloudflare first (e.g. workers, durable object, kV, ...) same story +> for AWS, ... it defeats in some sense the cloud agnostic but at the same time make it actually +> credible (it's almost Impossible to be TRULY cloud agnostic since each cloud adopt their own +> standards and adapters have their limit as described in the harness run analysis!) +> - if we go this route we would have plugin deploy, deploy-core, deploy-aws, deploy-cloudflare, +> deploy-vercel, ... which in some sense start really to look like what other major framework +> proceed ! with the advantage that in our case leveraging plugins (contribution, scaffolding, +> CLI, services, ...) makes it much more powerful + +(Read "Deni/imolemnt" as "Deno native first, then Node compat where needed" / "implement".) + +## Mandate + +Take the prior run as evidence base (read its canonical designs, research, adversarial findings, +nitro-vs-own synthesis) but produce a **clean run from scratch** in +`.llm/runs/plan-deploy-plugin--seed/` on branch `plan/deploy-plugin` (this worktree). Cover, at +minimum: + +1. **Architecture**: `plugin-deploy` with `deploy-core` + per-cloud adapters + (`deploy-aws`, `deploy-cloudflare`, `deploy-vercel`, thin adapters for koyeb/sevalla/coolify/ + dokploy/fly.io) modeled the EXACT way the auth plugin composes (core + provider adapters). + Doctrine archetypes, ports, public surface, gates for each. +2. **Migration**: what happens to the existing shipped deploy layer (current packages, CLI deploy + commands, Aspire helpers) — concrete migration map, debt entries, deprecation story. +3. **Plugin contributions**: what the deploy plugin contributes to EVERY layer — CLI commands it + ships, services, scaffolding: what a cloudflare-optimized scaffold looks like (workers, durable + objects, KV seams cloudflare-first), same for AWS product suites (queues, cache, KV), Vercel; + how contribution keeps the core cloud-agnostic story credible. +4. **Selective wrapping**: where nitro integration makes sense, where Aspire-native matters, where + we implement ourselves enterprise-grade, and which well-written standard packages are worth + wrapping per doctrine (wrap-don't-reinvent). +5. **Deno native first, Node compat where needed** as the stated goal frame throughout. + +Consult: `docs/architecture/doctrine/`, `.agents/skills/netscript-doctrine`, +`.agents/skills/netscript-harness`, the auth plugin implementation (`plugins/auth`) as the +composition precedent, and the new enterprise-auth board (issues #871–#887) for pattern parity. +Use `deno doc` before broad source reads. + +## Pipeline (supervisor-dispatched — do NOT self-dispatch downstream stages) + +1. YOU produce the full seed run (plan.md, design/canonical/*, migration map, contribution matrix, + scaffold stories, worklog, drift). +2. When complete, write `STAGE-COMPLETE: generator` at the end of `worklog.md` and STOP. The + supervisor then dispatches a **GPT Sol xhigh adversarial (constructive/collaborative)** pass — + its role is to ENHANCE your output, not ruin it — then a **Kimi K3 doc-driven story** pass that + forecasts the public-facing documentation to make the API/DX concrete. +3. You will be resumed to integrate their findings. + +## Stop-lines (verbatim, in force) + +- Drafts only: no GitHub issues, PRs, labels, or milestones may be created or changed by this run. +- No product code changes: no `packages/` or `plugins/` source edits. +- No merges without CI green + eval PASS + standing authorization; HARD STOP before any release + publish (release:cut, JSR publish, tag push, canary or stable) — owner sign-off in-turn only; + HARD STOP before closing milestone 13 — owner only. +- Do not self-arrange evals; the supervisor triggers every downstream stage. +- Commit to this branch only, push with explicit refspec: `git push origin HEAD:plan/deploy-plugin`. diff --git a/.llm/runs/plan-deploy-plugin--seed/supervisor.md b/.llm/runs/plan-deploy-plugin--seed/supervisor.md new file mode 100644 index 000000000..460b5a60d --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/supervisor.md @@ -0,0 +1,57 @@ +# Supervisor Identity — plan-deploy-plugin--seed + +Written at run start per `workflow/lane-policy.md` § Supervisor identity. A run dir without this +file is not activated. Other supervisors cross-peek a run by reading this file — it is how a run's +operating identity is discoverable without chat memory. + +| Field | Value | +| --- | --- | +| Model | Claude Fable 5 (`claude-fable-5`), effort **xhigh** (kickoff-directed) | +| Session | background job `948c0e39` — https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR | +| Host | WSL2 (Linux 6.18.33.2-microsoft-standard-WSL2), user `codex` | +| Checkout | /home/codex/repos/netscript-547-lffix | +| Worktree | /home/codex/repos/wt-deploy-plugin-seed | +| Branch | `plan/deploy-plugin` | +| Baseline | `290c68ef` (origin/main, 2026-07-18) | +| Run ID | `plan-deploy-plugin--seed` | + +## Run shape + +Modified **seed run** (planning-only, drafts-only). This session is the **generator stage only** of +a supervisor-defined three-stage pipeline (see `kickoff.md`): + +1. **Generator (this session)** — full seed corpus: `research.md`, `plan.md`, + `design/canonical/*` (architecture, migration map, contribution matrix, scaffold stories), + worklog, drift. Ends with `STAGE-COMPLETE: generator` in `worklog.md`, then stops. +2. **Adversarial (supervisor-dispatched)** — GPT-5.6 Sol · xhigh, constructive/collaborative + enhancement pass. +3. **Doc-driven story (supervisor-dispatched)** — Kimi K3 pass forecasting public documentation to + make API/DX concrete. + +Generator is resumed afterward to integrate findings. Downstream stages are **never** dispatched +from this session (kickoff stop-line: no self-arranged evals/stages). + +## Routes in force + +| Task lane | Provider / model / effort | Role in this run | +| --- | --- | --- | +| `planning_decisions` (orchestrator) | Claude · Anthropic · Fable 5 · **xhigh** | Seed generator: synthesis + all canonical design authoring | +| `chore_code` / research fan-out | Claude · Anthropic · Opus 4.8 (sub-agents) | Read-only evidence gathering (prior-run corpus, auth anatomy, deploy inventory, doctrine, board parity, provider APIs) | +| Stage-2 adversarial | Codex · OpenAI · GPT-5.6 Sol · xhigh | Supervisor-dispatched, not this session | +| Stage-3 doc story | Kimi K3 | Supervisor-dispatched, not this session | +| `formal_evaluation` (PLAN-EVAL) | open-model lane per `lane-policy.md` | Deferred; supervisor triggers after stages 2–3 integrate | + +Reference `.llm/harness/workflow/lane-policy.md`; the complete route table is not copied here. + +## Recorded lane/eval overrides + +- **Orchestrator effort xhigh** (default is Fable · low): owner-directed via `kickoff.md` for this + planning-heavy run. +- **No draft PR at stage A**: kickoff stop-line "no GitHub issues, PRs, labels, or milestones may + be created or changed by this run" overrides `workflow/seed-run.md` stage A. The commit trail is + the pushed branch `plan/deploy-plugin` (explicit refspec pushes). Mirrored in `drift.md` D-1. +- **Fable not used for sub-agent swarms** (owner directive 2026-07-11): research fan-out runs on + Opus 4.8. +- **Stage F/G (adversarial, PLAN-EVAL) replaced** by the kickoff's supervisor-dispatched Sol-xhigh + + Kimi-K3 pipeline; the formal PLAN-EVAL remains a later supervisor decision. Mirrored in + `drift.md` D-2. diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md new file mode 100644 index 000000000..4d3d41fc6 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -0,0 +1,12 @@ +# Worklog — plan-deploy-plugin--seed + +## 2026-07-18 — Stage A: bootstrap + +- Harness activated (`netscript-harness` skill, `activation.md`, `run-loop` context, `seed-run.md`, + `lane-policy.md` read). Kickoff read verbatim. +- Worktree confirmed: `/home/codex/repos/wt-deploy-plugin-seed`, branch `plan/deploy-plugin`, + baseline `290c68ef` = origin/main (2026-07-18). +- `supervisor.md` written (identity, pipeline shape, overrides). `drift.md` D-1 (no draft PR, + kickoff stop-line) and D-2 (custom downstream pipeline) recorded. +- Stop-lines in force: drafts only; no GitHub mutations; no product code; no self-dispatched + downstream stages; push only `HEAD:plan/deploy-plugin`. From fed305727fccb1f87a1926c192662eda8386fef2 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:17:20 +0200 Subject: [PATCH 02/20] =?UTF-8?q?plan(deploy-plugin):=20stage=20B=20discov?= =?UTF-8?q?ery=20corpus=20=E2=80=94=20six-surface=20research=20+=20synthes?= =?UTF-8?q?is?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior-run distillation, auth composition anatomy, deploy-layer inventory, doctrine constraints, board parity (#871-#887), live provider deploy surfaces; research.md synthesis with drift-candidate ledger. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../runs/plan-deploy-plugin--seed/research.md | 86 +++++ .../research/auth-composition-anatomy.md | 198 ++++++++++++ .../research/board-parity-871-887.md | 110 +++++++ .../research/deploy-layer-inventory.md | 199 ++++++++++++ .../research/doctrine-constraints.md | 280 ++++++++++++++++ .../research/prior-run-distillation.md | 302 ++++++++++++++++++ .../research/provider-deploy-surfaces.md | 179 +++++++++++ 7 files changed, 1354 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/research.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/auth-composition-anatomy.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/board-parity-871-887.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/deploy-layer-inventory.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/doctrine-constraints.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/prior-run-distillation.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/research/provider-deploy-surfaces.md diff --git a/.llm/runs/plan-deploy-plugin--seed/research.md b/.llm/runs/plan-deploy-plugin--seed/research.md new file mode 100644 index 000000000..7ec99a0d2 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research.md @@ -0,0 +1,86 @@ +# Research — plan-deploy-plugin--seed + +Synthesis over the committed stage-B corpus (`research/`). Every load-bearing claim below is cited +in the corpus files; this document indexes and concludes, it does not re-derive. + +## Corpus index + +| File | Surface | Method | +| --- | --- | --- | +| `research/prior-run-distillation.md` | Full prior run `plan-unified-runtime--seed` (UR-0…12, nitro/deno-deploy/market/sagas research, both adversarial rounds, evidence extracts) | Direct read of the wt-g8-seed worktree; supervisor additionally read `adversarial-nitro-vs-own.md` + `nitro-vs-own-synthesis.md` first-hand | +| `research/auth-composition-anatomy.md` | `plugins/auth`, `plugin-auth-core`, `auth-{kv-oauth,workos,better-auth}`, `packages/plugin`, CLI install path | Source + `deno doc` | +| `research/deploy-layer-inventory.md` | Shipped deploy layer: CLI verbs/adapters, aspire substrate, config schema, scaffold artifacts, docs promises, gaps | Direct file reads | +| `research/doctrine-constraints.md` | Doctrine 01–11, archetypes (esp. A2/A5/A6/A7), gate matrix, plan-gate, arch-debt, plugin host mechanics | Direct reads + `deno doc` | +| `research/board-parity-871-887.md` | Enterprise-auth board #871–#887 | Read-only GitHub API | +| `research/provider-deploy-surfaces.md` | Live provider surfaces: Cloudflare, Vercel, AWS, Fly, Deno Deploy (new), koyeb/sevalla/coolify/dokploy, Nitro v3, cross-cutting IaC | Primary-source web research, URLs inline | + +Re-baseline: this run starts clean at `origin/main = 290c68ef` (2026-07-18). The prior run's board +was drafts-only and never filed; nothing on the live board encodes the UR shape. The owner's +ratification (kickoff, verbatim) supersedes #824's unified-runtime framing. + +## Conclusions that drive the design + +1. **The plugin decomposition is the structural answer to the prior run's fatal finding.** + Adversarial F5 killed `@netscript/deploy` as "both a neutral core and a cross-domain + composition god-object" with a dependency-cycle risk. The auth composition pattern is the + proven anti-god-object shape in this codebase: a provider-agnostic `-core` owning ports + + registry + conventions, thin per-provider adapter packages, and a thin A5 plugin that only + wires and re-exports. The deploy family adopts it 1:1 (`auth-composition-anatomy.md` §1–§5, + "what generalizes"). +2. **Doctrine already reserved the seat.** Archetype 7 models deploy as A2-core + A6-router; + F-DEPLOY-1/2 gates are seeded `reviewed` awaiting the packages; debt + `DEPLOY-ARCHETYPE-7-CORE-SEED` states the core extraction verbatim — the 7-op + `DeployTargetPort`, the closed-on-key registry, and the activation/secrets/otel/rollback/ + health conventions already exist as target-agnostic modules inside the CLI kernel + (`doctrine-constraints.md` §2/§5, `deploy-layer-inventory.md` §7). The new family is an + **extraction + pluginization**, not a green-field invention. +3. **What survives from the prior run as doctrine:** the owner conformance rule (provider-native + wrapper beats a Nitro preset iff same conformance suite + full native surface + cheaper to + maintain; Nitro never in the composition contract or leaf ports); leaf ports stay + authoritative with provider primitives as capability-scoped backings; capability manifest + + build-time rejection compiler (UR-5); the sagas `supported | externalized | rejected` + tri-state; single-composition-root / no-loopback invariants (UR-1/UR-4); hostable lifecycle + via `ServiceShutdownCoordinator` (UR-0); Docker-image long tail as thin adapters + (`prior-run-distillation.md` §7–§8). +4. **What is dead:** Nitro-as-host (UR-2), Nitro-preset cell columns (UR-6), and any un-probed + "owned AWS/CF family" claim (adversarial F1–F4). AWS Lambda Web Adapter is an HTTP sidecar + only; Cloudflare tooling is provider-owned Node tooling callable from Deno; Miniflare is not a + production oracle. These become **probe-gated adapters**, not first-wave commitments. +5. **The wrap map is now concrete** (`provider-deploy-surfaces.md`): wrangler + Build Output API + + `deno deploy` CLI are the three bespoke framework-defined-infrastructure seams; Fly + the + four thin PaaS (koyeb/sevalla/coolify/dokploy) + Cloudflare Containers collapse onto **one + shared container-deploy path**; Pulumi Automation API is the only cross-cutting IaC dependency + worth taking (AWS lane); Serverless Framework v4 is disqualified on licensing; Nitro presets + are not programmatically consumable without adopting Nitro's build — study, don't import. +6. **Deno Deploy (new) is the Deno-native-first flagship but is capability-scoped:** no queues, + KV without queues/replication controls, `Deno.cron()` supported, monorepo git-integration gap — + the capability manifest must say so honestly (backend-truthful discovery, the auth board's S1 + lesson). +7. **The plugin host needs named, small extensions** for deploy to contribute at every layer: + `cli.doctorChecks` is a hardcoded `'auth-backend'` union; there is **no CLI-command + contribution axis** and **no frontend axis** (frontend is a separate parallel seed run). The + design must name these host changes explicitly rather than smuggle them + (`doctrine-constraints.md` §3, `auth-composition-anatomy.md` §9). +8. **Auth-specific patterns that invert for deploy:** single-active backend selection becomes a + **multi-target registry** (a project deploys to several targets/environments concurrently); + the no-userland-leaf rule inverts (deploy wants user-visible target config); the HTTP-service + shape is not required for v1 (deploy is CLI/CI-shaped) + (`auth-composition-anatomy.md` §9). +9. **Board mechanics:** mirror the enterprise-auth board (one umbrella epic, `Part of #N` + children, `- [ ] gate:` acceptance predicates, `Dependencies:`/`Delivery shape:` metadata, + core-contract-first DAG, one shared conformance kit) and fix its defects (single ID scheme, + milestone the p0, consistent waves, GitHub-native sub-issues) + (`research/board-parity-871-887.md` §4). + +## Drift-candidate ledger + +- D-C1: docs claim "deliberately minimal about deployment" while the CLI ships 15+ deploy verbs — + the plugin story must replace the "alpha-minimal" framing (docs refresh scoped in the + contribution matrix). +- D-C2: `deploy` group description stale ("Windows Service") and legacy flat verbs coexist with + the target-op router (S12/#348) — resolved by the verb-vocabulary lock in `plan.md`. +- D-C3: `deno_deploy`-related evidence predates Classic sunset (2026-07-20, two days after this + run) — all new-platform claims cite the new console/CLI docs only. +- D-C4: prior-run evidence extracts live in the wt-g8-seed worktree, not this branch — cited + transitively via `research/prior-run-distillation.md` §9; copy-forward is deliberate non-goal + (clean run). diff --git a/.llm/runs/plan-deploy-plugin--seed/research/auth-composition-anatomy.md b/.llm/runs/plan-deploy-plugin--seed/research/auth-composition-anatomy.md new file mode 100644 index 000000000..3598f86b0 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/auth-composition-anatomy.md @@ -0,0 +1,198 @@ +# Auth plugin composition pattern — definitive anatomy + +Source: Opus 4.8 research sub-agent of this run, 2026-07-18, over `plugins/auth`, +`packages/plugin-auth-core`, `packages/auth-{kv-oauth,workos,better-auth}`, `packages/plugin`, +`packages/cli` (install path). Paths relative to repo root at baseline `290c68ef`. + +## 1. Package topology + +| JSR name | Dir | Role | +|---|---|---| +| `@netscript/plugin-auth` | `plugins/auth/` | The plugin (manifest + service + streams + DB + contracts re-export + adapter CLI/scaffold) | +| `@netscript/plugin-auth-core` | `packages/plugin-auth-core/` | Provider-agnostic core: domain, ports, contracts/v1, config, presets, streams schemas, telemetry, testing | +| `@netscript/auth-kv-oauth` | `packages/auth-kv-oauth/` | Adapter — KV-backed OAuth2/OIDC relying party (wraps `@panva/oauth4webapi`) | +| `@netscript/auth-workos` | `packages/auth-workos/` | Adapter — WorkOS AuthKit (wraps `@workos-inc/node` + `jose`) | +| `@netscript/auth-better-auth` | `packages/auth-better-auth/` | Adapter — better-auth over Prisma (wraps `better-auth`) | + +Dependency direction (strictly inward): plugin → core + all adapters + `@netscript/plugin`, +`plugin-streams-core`, `service`, `kv`, `telemetry` (`plugins/auth/deno.json:17-28`). Adapters → +core (port types) + their one upstream lib; never the plugin, never each other. Core → `@orpc/*`, +`zod`, `@std/assert` (`packages/plugin-auth-core/deno.json:17-22`) **plus `@netscript/service/auth`** +(re-exports `AuthenticatorPort`, `AuthnRequest`, `AuthnResult`, `Principal` — +`src/domain/mod.ts:15-22`) — core sits on the service auth kernel, not fully standalone. +`plugins/auth/contracts/v1/mod.ts:10-11` is a pure re-export of core — the plugin owns no contract. + +## 2. Manifest & contribution points — three declarations, three consumers + +**(a) Runtime `PluginManifest`** via `definePlugin` fluent builder +(`plugins/auth/src/public/mod.ts:23-68`): + +``` +definePlugin('@netscript/plugin-auth', AUTH_PLUGIN_VERSION) + .withDisplayName('Auth').withType('api') + .withPermissions(AUTH_SERVICE_PERMISSIONS) // --unstable-kv,--allow-net,-env,-read,-write + .withService({ name:'auth-api', entrypoint:'./services/src/main.ts', port:8094 }) + .withContractVersions([{ version:'v1', loader:'./contracts/v1/mod.ts' }]) + .withRuntimeConfigTopics([{ name:'auth' }]) + .withMetadata({...}).build(); +// then frozen with an extra CLI contribution: +contributions.cli = { doctorChecks: ['auth-backend'] } +``` + +Auth uses only `services`, `contractVersions`, `runtimeConfigTopics`, `cli.doctorChecks` — NOT +`databaseSchemas`/`streamTopics`/`migrations` (those wire through other channels, §6/§8). + +**(b) Installer manifest `scaffold.plugin.json`** — static JSON read before any plugin code runs +(`PluginInstallerManifest`, `packages/plugin/src/protocol/manifest.ts:112-136`): `capabilities` +(`hasDatabaseMigrations:true, hasRoutes:true, hasBackgroundWorkers:false`), `scaffolder` +(`export:"./scaffold"`, requiredPermissions), `provider` (archetype defaults: `kind:"auth"`, +`portRangeKey:"PLUGIN_API"`, `pluginType:"utility"`, `infrastructureRequires:["db","kv"]`), +`officialSource` (first-party copy metadata: `canonicalName:"auth"`, `pluginDir:"auth"`, +`serviceEntrypoint`, `serviceConfigKey`, `servicePort:8094`, `requiresDb/Kv:true`, permissions). + +**(c) Adapter connector `NetScriptPlugin`** (`plugins/auth/src/adapter/plugin.ts:15-47`) — the +seam consumed by `createPluginAdapter`: `install.configParams:['NETSCRIPT_AUTH_BACKEND']`, +`install.prismaContract:'database/auth.prisma'`, `install.wiringEntry:'@netscript/plugin-auth/services'`, +`doctor.requiredConfigKeys:['NETSCRIPT_AUTH_BACKEND']`. + +Self-verification: `verify-plugin.ts:17-33` asserts expected axes via `verifyPlugin`. + +## 3. The core package + +Nine subpaths (`packages/plugin-auth-core/deno.json:6-16`): `.`, `./domain`, `./ports`, +`./contracts/v1`, `./telemetry`, `./streams`, `./config`, `./presets`, `./testing`. Root = curated +barrel. Owns: domain (`AuthUser`, `AuthSession`, `Account`, `Principal`, session states, Zod +schemas); ports (§4); oRPC contract v1; config (`AuthConfigSchema` `{backend, session, providers}`, +backend default `'default'`); presets (`createAuthPresetRegistry`, duplicate-guarded — +adapter packages contribute provider/backend presets); telemetry (span names, redaction, subject +hashing); streams schema (`authStreamSchema`, event types); testing fixtures. Provider-agnostic: +no provider SDK imported anywhere; defines interfaces + schemas + registry + provider-neutral +helpers (`createHmacSessionTokenCrypto` `ports/mod.ts:182`; `createAuthBackendRegistry`/ +`resolveBackend` `:329,341`). + +## 4. The adapter contract (provider SPI) + +`AuthBackendPort` (`packages/plugin-auth-core/src/ports/mod.ts:212-241`): + +```ts +export interface AuthBackendPort extends AuthenticatorPort { + readonly name: string; // stable key for backend selection + readonly providers: AuthProviderRegistryPort; // listProviders/getProvider + readonly sessions: AuthSessionStorePort; // get/create/refresh/revokeSession + readonly crypto: AuthSessionCryptoPort; // seal/openSessionToken + readonly principalMapper: AuthPrincipalMapperPort; // mapSessionToPrincipal + readonly interactive?: InteractiveFlowPort; // OPTIONAL redirect flow + authenticate(request: AuthnRequest): Promise | AuthnResult; +} +``` + +Capability boundaries: adapters **throw `AuthBackendOperationUnsupportedError`** (`:147-163`) +rather than faking operations (WorkOS refuses create/refresh/revoke — +`packages/auth-workos/src/workos-backend.ts:91-108`). + +Selection: `createAuthBackendRegistry(Map, defaultName)` → `resolveBackend(name?)` +(`ports/mod.ts:317-355`); missing key → `AuthBackendNotFoundError`. Runtime selection env-first: +`NETSCRIPT_AUTH_BACKEND ?? appsettings.auth.backend ?? 'kv-oauth'` +(`plugins/auth/services/src/backend-registry.ts:113-126`); **single-active** — registry built with +exactly ONE entry keyed by the active name (`:106-109`); `createActiveBackend` constructs only the +selected provider, reading provider-specific env (`:128-190`). + +## 5. What each adapter wraps (thinness) + +Each = `createXBackend(options) → AuthBackendPort`, re-exports port types from core. `auth-workos` +wraps `@workos-inc/node` + `jose`; delegates session crypto to core's HMAC helper; README doctrine: +"NetScript's auth plugin adapts the official `@workos-inc/node` SDK to it for you" +(`packages/auth-workos/README.md:96`). `auth-better-auth` wraps `better-auth` over Prisma +(`README.md:11-13,64`). `auth-kv-oauth` wraps `@panva/oauth4webapi` — the only full interactive RP +(8 subpaths; implements `InteractiveFlowPort`). Thinness marker: adapter `deno.json` imports only +`@std/assert`; upstream via package.json `catalog:`; no cross-adapter deps. + +## 6. Layer contributions (file-level map) + +- **CLI**: `plugins/auth/cli.ts:16` → `createPluginAdapter(authAdapterPlugin).toCli()` + (install/doctor/info/update/remove); `scaffold.ts:21` → `.toScaffold()`. Host doctor check + `'auth-backend'` contributed via manifest. +- **Service (HTTP)**: `services/src/main.ts:54-84` `createAuthService(ctx)` via + `createPluginService(router, {...}).serve()` (mandated builder chain). Router via + `assemblePluginContractRouter(implementer, {version:'v1', namespace:'auth', handlers:authV1})` + (`services/src/router.ts:11-18`). 5 procedures: signin/callback/signout/session/me + (`contracts/v1/auth.contract.ts:436-459`) + mandatory base `describe` spread from + `BASE_PLUGIN_CONTRACT_ROUTES` (`:434`). KV adapter side-effect import ordering: + `import '@netscript/kv/redis'` precedes backend composition (`services/src/main.ts:7-8`). +- **Scaffold assets**: ONE userland file — `auth/mod.ts` typed barrel re-export, emitted by + `authBarrelScaffolder` (`src/adapter/resources/barrel/barrel.ts:21-33`) from a type-checked stub + with `%%AUTH_CORE_CONTRACTS%%` token; never rewritten after first scaffold (`barrel.stub.ts:13-24`). +- **Database**: ships `database/auth.prisma` (better-auth-shaped models, `auth_users` table maps) + wired via connector `install.prismaContract` + `capabilities.hasDatabaseMigrations:true`. +- **Streams**: `streams/schema.ts:42-91` re-exports core schema + defines `authSession` entity + 5 + event types; `producer.ts` server-side emit via `plugin-streams-core` `createDurableStream` with + W3C trace-context; `factory.ts` browser-safe TanStack-DB client factory. Exports `./streams`, + `./streams/server`. +- **Contracts**: `./contracts` re-exports core v1 verbatim. + +## 7. Naming + versioning conventions + +- Plugin `@netscript/plugin-`; core `@netscript/plugin--core`; adapters + `@netscript/-` (drop the `plugin-` prefix). Plugin dir = bare canonical name + (`plugins/auth/`). +- Plugin subpaths role-named (`./public`, `./plugin`, `./contracts`, `./scaffold`, + `./adapter-cli`, `./services`, `./streams`, `./streams/server`); core subpaths layer-named. +- Contracts versioned by directory `contracts/v1/`; contract composes the base seam by object + spread (`...BASE_PLUGIN_CONTRACT_ROUTES`) + interface `extends BasePluginContract` + (`auth.contract.ts:410-417,434`); error map merges `BASE_PLUGIN_ERRORS` + auth-specific + (UNAUTHORIZED 401, AUTH_PROVIDER_ERROR 502, VALIDATION_ERROR 422) (`:137-183`). +- Version single-sourced: workspace `0.0.1-beta.10`; plugin version from generated + `src/package-metadata.generated.ts`; pinned `jsr:@netscript/x@` specifiers enforced by + `deps:check`; npm upstream deps via `catalog:` refs. + +## 8. Scaffold-time story + +Install modes: `netscript plugin install auth --source auto|starter|local` +(`packages/cli/src/maintainer/adapters/official-plugin-source.ts:37`); `--stub` = thin +local-import stub. **Official copy mode** = maintainer `copyOfficialPlugin` +(`packages/cli/src/maintainer/features/sync/plugin/copy-official-plugin.ts:54-104`): copies +`plugins/auth/` only (auth declares no `officialSource.dependencies`) — core + adapter packages +resolve from JSR; `rewritePluginDenoJsons` rewrites `@netscript/*` imports to versioned `jsr:` +specifiers; `regenerateCopiedRuntimeRegistries` refreshes registries. + +A scaffolded project gets: the plugin tree (copy) or stub; the `auth/mod.ts` barrel; a +registry/appsettings entry generated from `scaffold.plugin.json` by +`normalizeScaffoldPluginMetadata` (`packages/cli/src/kernel/adapters/config/plugin-registry.ts:207-256`); +`NETSCRIPT_AUTH_BACKEND` config key wired; Prisma contract registered; workspace `plugins/deno.json` +regenerated via `PluginRegistryScaffolder` → `generatePluginsDenoJson` +(`kernel/adapters/plugin/registry-scaffolder.ts:13,22-38`). Install/scaffold algorithms are +host-owned in `@netscript/plugin/adapter` (`packages/plugin/src/adapter/mod.ts:78-83`); the plugin +supplies only the `InstallSpec` seam. + +## 9. Auth-specific bits that will NOT generalize to deploy + +1. `cli.doctorChecks: readonly 'auth-backend'[]` — hardcoded literal union in the host + (`plugin-contributions.ts:14-16`); a deploy doctor check needs the union widened, not imitated. +2. `AuthBackendPort extends AuthenticatorPort` — the per-request authn seam from + `@netscript/service/auth` is meaningless for deploy; deploy ports shape around operations + (build/emit/up/rollback), not request authentication. +3. **Single-active-backend selection** via one env var fits "pick one auth provider"; deploy + plausibly needs **multiple concurrent targets** (aws + cloudflare in one project) → the + single-active registry pattern inverts to a multi-registered map keyed per environment/target. +4. better-auth-shaped Prisma schema is a provider artifact leaking into the plugin; deploy has no + analogous universal schema — don't assume a plugin must ship a `.prisma`. +5. `interactive?: InteractiveFlowPort` (browser redirect flow) has no deploy analog. +6. HMAC session-token crypto + cookie/session policy have no deploy counterpart. +7. Auth deliberately emits NO userland leaf sample (env-driven config, single never-rewritten + barrel); deploy likely DOES want a user-authored leaf (target definitions) — the "single + barrel" doctrine may not transfer. +8. `portRangeKey:'PLUGIN_API'`, `servicePort:8094`, `hasRoutes:true`, `requiresDb/Kv:true` encode + "long-running HTTP API needing db+kv"; deploy is more CLI-tool-shaped (`hasRoutes:false`, no + persistent port, maybe `hasBackgroundWorkers` for async deploys) — re-derive the + provider/officialSource block, don't copy it. + +## What DOES generalize cleanly + +The 3-tier split and naming (`plugin-deploy` / `deploy-core` / `deploy-`); inward +dependency direction; core-owns-ports-and-registry + adapters-implement-port + +`OperationUnsupportedError` capability boundaries; the `definePlugin` builder + +`scaffold.plugin.json` + `NetScriptPlugin` connector triad; +`createPluginAdapter(connector).toCli()/.toScaffold()`; contract composition via +`...BASE_PLUGIN_CONTRACT_ROUTES` + `satisfies BasePluginContract`; `contracts/v1/` versioning; +catalog/JSR-specifier centralization; the official-source copy-mode + registry-generation install +path (host-owned, driven by `officialSource`/`provider` metadata). diff --git a/.llm/runs/plan-deploy-plugin--seed/research/board-parity-871-887.md b/.llm/runs/plan-deploy-plugin--seed/research/board-parity-871-887.md new file mode 100644 index 000000000..3d08f89b2 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/board-parity-871-887.md @@ -0,0 +1,110 @@ +# Enterprise-auth board (#871–#887) — pattern-parity digest + +Source: read-only GitHub API sweep of `rickylabs/netscript` issues #871–#887, 2026-07-18 (Opus 4.8 +research sub-agent, this run). All 17 are open issues (no PRs in range), authored by the owner, +created 2026-07-18 in one filing burst. Citations are issue numbers; GitHub is the source of truth. + +## 1. Per-issue table + +| # | Role | Title | Labels (namespaced) | Milestone | One-line scope | +|---|------|-------|---------------------|-----------|----------------| +| **871** | **EPIC** | Epic: Enterprise auth | `type:umbrella` `area:auth` `priority:p1` `status:triage` `epic:enterprise-auth` | Backlog / Triage | Umbrella: vendor-neutral identity/session/policy kernel leveraging KV OAuth + WorkOS + Better Auth; 16 children. | +| 872 | S1 / EA-00 | Make auth capability discovery backend-truthful | `type:fix` `area:auth` `area:service` `wave:v1` `priority:p0` `status:triage` | **none** | Derive advertised capabilities from backend ports, not a static list; kill false unsupported-op paths. Only beta-critical. | +| 873 | S2 / EA-01 | First-class Microsoft Entra ID OAuth/OIDC profile | `type:feat` `area:auth` `area:cli` `priority:p1` `status:triage` | Backlog / Triage | Typed Entra preset: tenant authority, tid/claims/app-roles, consent, B2B guests; explicit "not MSAL parity". | +| 874 | S3 / EA-02 | Compose multiple auth backends with tenant-aware routing | `type:feat` `area:auth` `area:service` `priority:p1` `status:triage` | Backlog / Triage | Deterministic org/domain/connection routing; account linking, provenance. | +| 875 | S4 / EA-03 | Complete WorkOS AuthKit + enterprise SSO interactive adapter | `type:feat` `area:auth` `priority:p1` `status:triage` | Backlog / Triage | WorkOS sign-in/callback/refresh/logout + SAML/OIDC connections. | +| 876 | S5 / EA-04 | Normalize per-tenant enterprise connection setup | `type:feat` `area:auth` `area:cli` `priority:p1` `status:triage` | Backlog / Triage | One normalized connection-setup record across providers. | +| 877 | S6 / EA-05 | Integrate WorkOS Directory Sync and SCIM lifecycle | `type:feat` `area:auth` `area:database` `priority:p1` `status:triage` | Backlog / Triage | Directory/SCIM projection via shared event-sync consumer; "automatic" not "real-time". | +| 878 | S7 / EA-06 | Enterprise audit events + bridge WorkOS Audit Logs | `type:feat` `area:auth` `area:telemetry` `priority:p1` `status:triage` | Backlog / Triage | Stable audit envelope + optional provider bridge. | +| 879 | S8 / EA-07 | Add WorkOS RBAC/FGA authorization providers | `type:feat` `area:auth` `area:service` `priority:p1` `status:triage` | Backlog / Triage | RBAC from authoritative claims + FGA relationship provider over the shared policy port. | +| 880 | S9 / EA-08 | Enterprise auth secret-reference and rotation lifecycle | `type:feat` `area:auth` `area:cli` `priority:p1` `status:triage` | Backlog / Triage | Vendor-neutral secret reference/rotation contract (overlap window, redaction). | +| 881 | S10 / EA-09 | Generate Better Auth plugin schema and migrations | `type:feat` `area:auth` `area:cli` `area:database` `priority:p1` `status:triage` | Backlog / Triage | Deterministic per-plugin schema/migration generation; drift detection. | +| 882 | S11 / EA-10 | Mount Better Auth interactive handlers + client integration | `type:feat` `area:auth` `area:cli` `area:service` `priority:p1` `status:triage` | Backlog / Triage | Mount upstream handler at a deliberate path; reconcile cookies/sessions. | +| 883 | S12 / EA-11 | Ship curated Better Auth capability profiles | `type:feat` `area:auth` `area:cli` `priority:p1` `status:triage` | Backlog / Triage | 2FA/passkey/magic-link/org/admin profiles as config manifests. | +| 884 | S13 / EA-12 | Organization-aware identity + authorization policy contracts | `type:feat` `area:auth` `area:service` `priority:p1` `status:triage` | Backlog / Triage | Canonical org/membership/connection/role + authz request contracts. Foundational. | +| 885 | S14 / EA-13 | Auth conformance, mocking, and scaffold test kit | `type:feat` `area:auth` `area:cli` `area:tooling` `priority:p1` `status:triage` | Backlog / Triage | Shared security test kit: fake OIDC/JWKS, fixtures, multi-backend, scaffold E2E smokes. | +| 886 | S15 / EA-14 | Authenticate machines, agents, CLIs, and MCP clients | `type:feat` `area:auth` `area:service` `area:cli` `area:ai-core` `priority:p1` `status:triage` | Backlog / Triage | Non-browser identity lane: client-creds, M2M, device flow, API keys, MCP. | +| 887 | S16 / EA-15 | Define outbound NetScript-as-IdP support | `type:feat` `area:auth` `area:service` `priority:p2` `status:triage` | Backlog / Triage | Deferred, RFC-first; no custom auth server. | + +`epic:` = `epic:enterprise-auth` on every row. + +## 2. Board shape + +- Exactly **one epic** (#871, `type:umbrella`), flat one level deep — 16 sub-issues, no nested + epics. Children tracked as a Markdown checklist under `## Children` (not GitHub-native + sub-issues); each child links back via prose `Part of #871`. +- **Two identifier schemes run in parallel**: titles use S1–S16; body `## Metadata` uses EA-00–EA-15 + (zero-indexed, off by one). Consistent but a readability hazard. +- **Phasing is a dependency DAG, not waves.** Encoded in the `Dependencies:` metadata line, rooted + at ~four zero-dependency foundations: EA-00 capability truth, EA-12 identity+authz contracts, + EA-09 Better Auth schema, EA-08 secrets. EA-02 composition sits one layer up, feeding vendor + adapters. +- **Issue archetypes**: vendor-neutral core/contract (S1, S3, S9, S13) — the "kernel"; per-provider + adapters (S2, S4, S6, S7, S8, S10–S12); scaffold/CLI story (`area:cli` recurring; S5 dedicated); + a single shared test kit (S14) that "expands alongside every adapter"; cross-cutting new lane + (S15); deliberately deferred RFC (S16, the only p2). +- Exactly one **p0** (S1, also the only `wave:v1` — and anomalously the only issue with **no + milestone**); everything else p1 except S16 (p2). All in `status:triage`, milestone + Backlog / Triage. +- **Area labels double as package routing** (`area:service`, `area:database`, `area:telemetry`, + `area:tooling`, `area:cli`, `area:ai-core`). + +## 3. Body conventions (recurring template) + +1. `Part of #871` as the literal first line — reference, never a closing keyword. +2. One scoping paragraph (3–8 lines): what exists, what to leverage upstream, and an explicit + **anti-scope boundary** ("do not reproduce SAML parsing", "not MSAL parity", "automatic, never + real-time", "normalize configuration and policy, not implement another authorization server"). +3. `## Acceptance` — every item phrased `- [ ] gate: `. Gates are testable + invariants, not tasks. Recurring themes: fail closed; tenant-safe; redaction of + secrets/tokens/PII; negative tests (replay, bad issuer/audience, malformed input); unsupported + ops not advertised; single lifecycle owner per overlapping provider surface. No shell commands — + predicates the conformance kit (S14) mechanizes. +4. `## Metadata` with exactly two lines: `Dependencies:` (EA-NN topological ordering + + co-ownership notes) and `Delivery shape:` (the core-vs-adapter split stated per issue, e.g. + "core/service contract correction, not vendor adapter work"; "new WorkOS interactive adapter + code using upstream AuthKit/SSO APIs"). + +Epic body adds `## Epic acceptance` (nine cross-cutting `- [ ] gate:` invariants, including "all +implementation PRs satisfy CI and the required opposite-family evaluation before merge") and +`## Children`. + +## 4. Decomposition lessons for the deploy board + +Mirror: + +1. One umbrella epic (`type:umbrella` + new `epic:deploy` + an `area:` route), body = epic + acceptance gates + children checklist; never a closing keyword on it. +2. Uniform child template: `Part of #` → scoping paragraph with explicit anti-scope boundary + ("do not reimplement the provider's CLI/API") → `## Acceptance` as `- [ ] gate:` predicates → + `## Metadata` with `Dependencies:` + `Delivery shape:`. +3. Lead with a vendor-neutral core-contract issue (S1+S13 analogue): deploy target/descriptor/ + capability-discovery contract, backend-truthful, fail-closed. Make the correctness-critical + slice p0 and (unlike auth) milestone it. +4. One issue per deploy target/adapter, each `Delivery shape: adapter over the shared deploy port + using upstream tooling; provider remains the implementation source`. +5. A dedicated scaffold-story issue (`area:cli`): select, configure, mock, smoke-test a deploy + profile. +6. A migration/rollout-lifecycle issue (S10 analogue): deterministic, diffable, destructive-change + detection; overlap-window + rollback gates. +7. A secrets/config-reference issue (S9 clone): credentials referenced not serialized; redaction + across logs, generated files, diffs, telemetry. +8. An audit/telemetry issue (S7 clone): versioned deploy events through a shared event seam; core + success must not silently depend on the audit provider. +9. A single shared conformance + mock + scaffold-E2E test-kit issue (S14 clone), ending in a CI + failure matrix by target × capability × invariant. +10. Encode phasing in `Dependencies:` DAG, rooted at the core contract + capability-truth issues. +11. State the core-vs-adapter lane in every `Delivery shape:` line. +12. Carve deferred scope into a `priority:p2` RFC-first backlog issue. + +Do differently: + +13. **Single identifier scheme** (auth's S-vs-EA off-by-one is a standing decode tax) — use `DP-N` + in both titles and cross-references. +14. **Milestone every child at creation, especially the p0** (auth's sole p0 is the only + unmilestoned issue). +15. Apply `wave:` labels consistently or not at all. +16. Move scoped children out of `status:triage` (exactly one `status:` each; ready → ready). +17. Prefer GitHub-native sub-issues for automatic rollup. +18. Right-size the provider matrix — collapse adapters where one suffices rather than mechanically + reproducing 16 slices. diff --git a/.llm/runs/plan-deploy-plugin--seed/research/deploy-layer-inventory.md b/.llm/runs/plan-deploy-plugin--seed/research/deploy-layer-inventory.md new file mode 100644 index 000000000..dd13f4601 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/deploy-layer-inventory.md @@ -0,0 +1,199 @@ +# Current-state inventory — the shipped deploy layer + +Source: Opus 4.8 research sub-agent of this run, 2026-07-18, direct file reads over +`packages/cli`, `packages/aspire`, `packages/config`, `packages/runtime-config`, scaffold +templates, and `docs/site`. Baseline `290c68ef` (origin/main). The shipped deploy story lives +almost entirely in **`packages/cli`** (verbs + target adapters), with **`packages/aspire`** as the +SDK-neutral AppHost-composition substrate and **`packages/config`** owning the `deploy.*` schema. +There is no `plugin-deploy`/`deploy-core` package — deploy is monolithic inside the CLI kernel. + +## 1. CLI surface today + +Public root registration: `deploy` group in +`packages/cli/src/public/features/root/public-command-tree.ts:59-62`; factory +`createDeployCommand` in `packages/cli/src/public/features/deploy/deploy-group.ts:22-98`. Group +description is stale ("Build and manage NetScript **Windows Service** deployments", +`deploy-group.ts:27`). + +| Verb | Registered | Implementation entry | What it does | +|---|---|---|---| +| `list` | `deploy-group.ts:31` | `list/list-deploy-targets-command.ts` | Lists registered deploy targets. | +| `desktop` | `deploy-group.ts:33` | `target/desktop/desktop-group.ts` | Native desktop packaging subgroup. | +| `build` | `deploy-group.ts:41` | `build/build-deploy.ts` (`BuildDeployPipeline`) + `build/build-windows-strategy.ts` | Bare-metal build: `deno compile` service binaries into `.deploy/`. | +| `deno-deploy` | `deploy-group.ts:57` | `deno-deploy/deno-deploy-command.ts:107-156` | Deno Deploy target subgroup (`plan/up/down/status/logs`). | +| `package-cli` | `deploy-group.ts:64` | `package-cli/package-cli-deploy-command.ts` | Compiles the NetScript CLI into the deploy bin dir. | +| `copy` | `deploy-group.ts:65` | `copy/copy-deploy-command.ts` | Copies deploy build output. | +| `install` | `deploy-group.ts:67` | `install/install-service-deploy.ts` | Installs OS service (Servy/systemd) from resolved manifest. | +| `start`/`stop`/`status`/`logs` | `deploy-group.ts:75-78` | `start|stop|status|logs/*-deploy-command.ts` | OS-service lifecycle over `osServices` port. | +| `uninstall` | `deploy-group.ts:80` | `uninstall/uninstall-service-deploy.ts` | Removes the OS service. | +| `upgrade` | `deploy-group.ts:88` | `upgrade/upgrade-deploy-command.ts` + `kernel/adapters/deploy/upgrade-steps.ts` | In-place service upgrade. | +| `docker`/`compose` | `deploy-group.ts:91-92` | `target/target-deploy-command.ts` → `AspireComposeDeployTarget` | Thin router → Aspire compose/docker adapter. | +| `kubernetes`/`azure-aca`/`azure-app-service`/`azure-aks`/`cloud-run` | `deploy-group.ts:93-97` | `target/target-deploy-command.ts` → `AspireCloudDeployTarget` | Thin router → Aspire AppHost / Cloud Run adapter. | + +Generic target router (`target/target-deploy-command.ts:15-23`) derives verbs from each adapter's +advertised `operations`: `plan, up, down, status, logs, rollback, secrets` (+ `secrets` subcommand +`:60-61`). Dispatch: `target/run-target-operation.ts:22-54`; target config from +`deploy.targets.` merged with `aspire.appHost` (`:56-72`). `secrets`/`rollback` +declared-unsupported on all shipped adapters (deferred to #341). + +Adjacent groups: `netscript service` (`services-group.ts:18-49`) manages `appsettings.json` +deployable-unit declarations; `netscript generate aspire` regenerates AppHost `.helpers`; +`netscript db generate` / `generate plugins` are pre-bake steps (docs `deploy.md:169-173`). + +Maintainer surface (`netscript-dev`, `packages/cli/bin/netscript-dev.ts`): local tree merges the +entire public tree (`src/local/composition/local-contributor-command-tree.ts:11-32`) + maintainer +features (`init`, `probe`, `release`, `sync`, `test-scaffold`) — framework-repo tooling; no +maintainer-only app-deploy verbs. + +## 2. Aspire layer + +`packages/aspire` (`@netscript/aspire`) is **SDK-neutral** — "no Aspire SDK type appears in any +public signature" (`packages/aspire/README.md:23-24`). Composition + diagnostics, NOT a deploy +executor. Public surface: root `mod.ts:22-27` = `inspectAspire` + types; subpaths `./config` +(`parseAppSettings`, `NetScriptConfigSchema`), `./application` (`composeAppHost`, +`ContributionRegistry`, deterministic port allocation, env/reference/permission resolvers), +`./adapters` (`AspireTypeScriptBuilder` — emits real AppHost resources; `resolveEnvSource`), +`./ports` (`AspireBuilderPort`, `AspireRuntimePort`), `./testing` (`MemoryAspireBuilder`), +`./types`, `./constants`. + +Generated artifacts (emitted by the CLI template engine `packages/cli/src/kernel/templates/aspire/` +via `HelpersGeneratorPipeline`): `aspire/apphost.mts` (TypeScript/Node AppHost), +`aspire/.helpers/*.mts` (index, register-apps/services/plugins/infrastructure/background/tools, +db-cli-mode), `aspire/aspire.config.json`, `aspire/package.json` (vscode-jsonrpc, tsx, +typescript), `aspire/tsconfig.apphost.json`. Renderer +`kernel/application/scaffold/render-ts-apphost.ts:19-265`. SDK via CommunityToolkit +`CommunityToolkit.Aspire.Hosting.Deno@13.2.1-beta.532` + Aspire Hosting integrations 13.4.x +(Postgres/MySql/SqlServer/Redis/Garnet/Browsers/DenoKv) (`scaffold-aspire.ts:9-43`). + +Run model: `cd aspire && aspire restore && aspire start`; dashboard `:18888`, OTLP `:4318` +(docs `deploy.md:236,252`; `port-ranges.ts:13-14`). + +Cloud-deploy hooks (executor = CLI shelling the `aspire` binary): + +- `kernel/adapters/aspire/aspire-compose-deploy-target.ts` (`compose|docker`): `plan`/`emit` shell + `aspire publish --output-path ` (`:168-181`); `compose up` self-hosts via + `docker compose -f /docker-compose.yaml up -d` (`:105-121`); `docker up` delegates to + `aspire deploy` (`:106-111`); `down/status/logs` shell docker compose (`:124-166`). `operations` + omit rollback/secrets (`:64-71`). +- `kernel/adapters/aspire/aspire-cloud-deploy-target.ts` (`kubernetes|azure-aca|azure-app-service| + azure-aks|cloud-run`, TARGETS `:37-83`): AppHost targets validate platform markers in AppHost + source (`addKubernetesEnvironment`, `addAzureContainerAppEnvironment` — `:43,52-53,65,74`, + enforced `:258-282`) then shell `aspire publish|deploy|destroy --apphost --output-path + ` (`:183-239`). `cloud-run` is a Docker lane: `docker build/push` + `gcloud run deploy` + (`:167-181`), delete (`:197-208`); requires `registry`+`imageName` (`:284-298`). No `azd` + anywhere; Azure reached purely through `aspire publish/deploy`. + +## 3. Scaffold deploy artifacts + +Fresh `netscript init` project gets (generators `kernel/application/scaffold/`): + +- `appsettings.json` — single source of truth for deployable units (ports, entrypoints, + permissions, DB/KV requirements, references) (`templates/aspire/generate-appsettings.ts`; + `render-ts-apphost.ts:123-235`). +- `netscript.config.ts` — ships **`deploy: {}` empty** (`netscript-config-1.ts.template:25`) + + `aspire: { appHost: 'aspire/apphost.mts' }` (`:16-18`). +- Aspire bundle under `aspire/` (§2). +- `.github/workflows/` (`plan-init.ts:132-154`): `deploy-deno-deploy.yml` (checks → + `netscript deploy deno-deploy up`, token/org/app secrets); `deploy-bare-metal.yml` + (Linux+Windows matrix, `deno task check`, `netscript deploy build`, artifact upload); + `deploy-compose-ghcr.yml` (Aspire projects only: `aspire restore` → `netscript deploy compose + plan` → `docker compose build/push` → `netscript deploy docker up --clear-cache`). CLI + specifier templated via `{{netscriptCliSpecifier}}` (`plan-init.ts:143-146`). + +**NOT scaffolded**: no Dockerfile, no docker-compose.yml, no K8s manifest, no `.env` +(docs `deploy.md:38-41,371-381`) — container/K8s/Azure artifacts emitted at deploy time by +`aspire publish` from user-configured AppHost code. Scaffold E2E gates: +`packages/cli/e2e/src/application/gates/scaffold/`. + +## 4. Config / environment model + +`deploy.*` schema — `packages/config/src/domain/schemas/deploy-schema.ts`. `DeployConfigSchema` +(`:288-317`) = `{ targets?: { windows, docker, compose, linux, deno-deploy, kubernetes, +azure-aca, azure-app-service, azure-aks, cloud-run } }`; types +`config-section-types.ts:357-592`. Shared `deployTargetBaseShape` (`:21-150`): compile mode, +`compileTarget`, `concurrency`, `bundleExternal`, per-type `v8HeapMb`, `generateEnvFile`, +`logging` rotation, `health`, `docker` base images (`denoland/deno:2`, +`mcr.microsoft.com/dotnet/aspnet:9.0`), `activation` (retain/strategy symlink|dir-swap/ +healthGate), `secrets` (`envFile`, POSIX mode 0o600), `otel`. Per-target extras: Windows→Servy +(`:163-173`); Linux→systemd (`:204-220`); deno-deploy→org/app/entrypoint/prod/envFile +(`:227-241`); AppHost cloud→outputPath/appHost (`:253-260`); cloud-run→registry/imageName +(`:272-279`). + +Ports: `kernel/constants/port-ranges.ts:8-15` — SERVICE 3000-3099, APP 8000-8099, PLUGIN_API +8091-8099, INFRA_PLUGIN 4400-4499, ASPIRE_DASHBOARD 18888, OTEL 4318. Env contract +(bring-your-own in prod): `POSTGRES_URI`/`DATABASE_URL`, `REDIS_URI`/`GARNET_URI`, `PORT`, +`OTEL_EXPORTER_OTLP_ENDPOINT`, `NETSCRIPT_SAGA_STORE`, `NETSCRIPT_AUTH_BACKEND`, concurrency vars +(docs `deploy.md:195-205`). Secrets: no first-class manager; only the env-file convention + +unimplemented `secrets` ops. Aspire deploy state cached `~/.aspire/deployments/{sha}/{env}.json`; +CI told `--clear-cache` (docs `:331-338`). `packages/runtime-config`: hot-reloadable versioned +overrides "without a deploy" (`loadRuntimeConfig()`/`watchRuntimeConfig()`). Environments: no +matrix in config; `--environment` is an opaque pass-through to Aspire +(`aspire-compose-deploy-target.ts:184-186`); CI promotion ladder is convention only. Deploy +conventions (target-agnostic, injected ports): `kernel/domain/deploy/{activation,secrets, +observability,rollback}-convention.ts`, `health-gate.ts`, `runtime-overrides.ts`; Servy config +`servy-config.ts`. + +## 5. Docs promises + +Primary: `docs/site/orchestration-runtime/how-to/deploy.md`. "NetScript is in alpha… deliberately +minimal about deployment" (`:16-17`). Wired = appsettings graph + Deno entrypoints + apphost.mts; +Generated CI = 3 workflows; Delegated = docker/compose/k8s/azure → `aspire publish|deploy| +destroy`, cloud-run → docker+gcloud; Manual = no generated container image/compose/cloud stack; +`deploy: {}` ships empty (`:25-44`). Three-layer mental model (`:86-107`); "keep Aspire… or drop +it" (`:104`); "no opinionated build step that produces a single bundle — each Deno process runs +from source" (`:152-153`); cloud recipes (`:255-312`, k8s emits Helm chart); CI/promotion ladder +(`:314-338`); bare-metal primitive = one Deno process, one `deno run`, exact permissions +(`:340-369`); "Limits of the alpha scaffold" (`:371-384`) tagging arch-debt +`cli-deploy-artifacts-missing`, `streams-manifest-helpers-unsupported`, +`workers-scaffold-job-tools-noop`; health-check verification (`:386-410`). + +Supporting: `deploy-deno-deploy.md` (the one fully-runnable managed path), +`deploy-local-aspire.md`, `explanation/aspire.md`, `runtime-config.md` + +`roll-out-runtime-overrides.md`, `graceful-shutdown.md`, `cli-reference.md`, +`reference/cli/commands.md`. `packages/aspire/README.md:124-131` links them. + +## 6. Gaps (no deploy story today) + +- No hand-authored container artifacts ever generated (Dockerfile/compose/K8s) — all delegated to + `aspire publish` (debt `cli-deploy-artifacts-missing`). +- `rollback`/`secrets` unimplemented on every target (declared-unsupported; #341) + (`deploy-target-port.ts:8-11,118-121`). No secret-manager integration. +- No environment/target matrix in config; promotion ladder CI-convention only. +- Cloud-Run is the only non-Aspire container cloud lane. **Vercel, Fly.io, AWS (ECS/Lambda/App + Runner), Cloudflare/Wrangler, Netlify, Railway, Render — none exist.** No wrangler, flyctl, or + AWS SDK/CDK anywhere in the deploy layer (Cloudflare/Vercel/Fly appear only in + `docs/site/_plan/research/competitors/`). +- Deno Deploy target is preflight-thin: `plan` = unstable-API guard (best-effort, entrypoint + + deno.json unstable list only, not transitive — debt in `unstable-api-guard.ts`); other ops + shelled to native `deno deploy` CLI; prod push refused on violations + (`deno-deploy-target.ts:98-101`). +- `packages/aspire` cannot deploy — composition/diagnostics only; execution depends on external + `aspire`/`docker`/`gcloud`/Servy/`systemctl` binaries. +- Scaffold worker `createJobTools` helpers no-op; streams manifest helpers throw by design + (docs `:377-378`). +- Legacy vs canonical drift: group description says "Windows Service" (`deploy-group.ts:27`); + flat legacy verbs (`build/install/start/stop/upgrade`) coexist with the ` ` router + pending convergence (S12/#348, `deploy-group.ts:89-90`). + +## 7. Ownership map (migration-map input) + +| Item | Owner today | Future home | +|---|---|---| +| `deploy` command group + verbs | `packages/cli/src/public/features/deploy/**` | `plugin-deploy` CLI contributions | +| 7-op contract (`DeployTargetPort`, `DeployOperation`) | `packages/cli/src/kernel/domain/deploy/deploy-target-port.ts` | `deploy-core` port | +| Target registry + defaults | `kernel/application/registries/deploy-target-registry.ts` (`DEFAULT_DEPLOY_TARGETS` `:71-83`); port `domain/deploy/deploy-target-registry-port.ts` | `deploy-core` registry (named extension axis) | +| Windows/Linux service targets + Servy/systemd | `domain/deploy/{windows,linux}-service-deploy-target.ts`, `service-deploy-target.ts`, `servy-config.ts`; adapters `kernel/adapters/deploy/commands/*`; OS port `public/adapters/os-service-factory.ts` | bare-metal adapter package | +| Aspire compose/docker + k8s/azure/cloud-run | `kernel/adapters/aspire/aspire-{compose,cloud}-deploy-target.ts` | per-cloud adapter packages | +| Deno Deploy target | `domain/deploy/deno-deploy-target.ts`, `deno-deploy-cli-port.ts`; `kernel/adapters/deno-deploy/*`; `unstable-api-guard.ts` | deno-deploy adapter package | +| Bare-metal compile/build | `public/features/deploy/build/*`; engine `kernel/adapters/deploy/compile/*`; domain `compile-target.ts` | `deploy-core` build primitive | +| Desktop packaging | `public/features/deploy/target/desktop/**` (dmg/appimage/deb/msi + release sign/serve/store) | separate concern (own adapter/plugin; epic #830 graph) | +| Conventions (activation/secrets/otel/rollback/health) | `kernel/domain/deploy/*-convention.ts`, `health-gate.ts`, `runtime-overrides.ts` | `deploy-core` (R-DEPLOY-3) | +| `deploy.*` config schema | `packages/config/.../deploy-schema.ts`; types `config-section-types.ts:357-592` | base stays; per-target members move with adapters (config-plugin-specific-schema-debt) | +| Aspire substrate | `packages/aspire` | stays (foundation for Aspire adapters) | +| Scaffold deploy emission | `kernel/application/scaffold/*`, `kernel/templates/aspire/**`, workflow templates | plugin scaffold contributions | +| Runtime overrides | `packages/runtime-config` | adjacent, unchanged | +| Docs | `docs/site/orchestration-runtime/how-to/deploy*.md`, `explanation/aspire.md` | refreshed with plugin story | +| Maintainer deploy QA | `maintainer/features/test-scaffold/**`; e2e `gates/scaffold/**` | extended for plugin scaffolds | + +Root `deno.json`: no app-deploy tasks (repo CI/release/agentic only); the only app-facing deploy +automation is the three scaffolded workflow templates. diff --git a/.llm/runs/plan-deploy-plugin--seed/research/doctrine-constraints.md b/.llm/runs/plan-deploy-plugin--seed/research/doctrine-constraints.md new file mode 100644 index 000000000..52a6fabd6 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/doctrine-constraints.md @@ -0,0 +1,280 @@ +# Doctrine + harness constraints for the deploy plugin family + +Source: Opus 4.8 research sub-agent of this run, 2026-07-18, over `docs/architecture/doctrine/` +(01–11 + ref-migration-map), `.llm/harness/archetypes/`, `.llm/harness/gates/`, +`.llm/harness/debt/arch-debt.md`, and `packages/plugin` (via `deno doc` + source). Citations are +repo paths (line numbers per baseline `290e68ef`… = `290c68ef` origin/main). + +**Scope note / naming mapping.** Doctrine does not model deploy as a *plugin* family — it models +it as **Archetype 7 (Deployment Target Adapter)**, a composite folding **Archetype 2** (port/ +adapter core) + **Archetype 6** (thin CLI router) (`06-archetypes.md:257-283`, +`ARCHETYPE-7-deploy-target-adapter.md:1-8`). The owner's shape maps onto the existing reference +split (`auth-core` + thin adapters + thin `plugins/auth`, `11-plugin-thinness-and-base-seams.md:49-64`): + +- `plugins/deploy` (delivery shell) → **Archetype 5** (thin plugin). +- `deploy-core` → **Archetype 2** integration core owning the port, registry, and every + convention-bearing primitive. +- `deploy-aws` / `deploy-cloudflare` / `deploy-vercel` → **Archetype 2** thin adapter packages, + one file-group per technology behind the core's port (mirrors auth-better-auth/auth-kv-oauth/ + auth-workos). +- The 7-op uniform adapter contract and `DeployTargetBaseSchema`-spread config rule bind adapters + regardless of delivery (`ARCHETYPE-7-…:51-73`). AWS/Cloudflare/Vercel are **new target-axis + variants** — absent from the current config target map (`packages/config/.../deploy-schema.ts:288-316` + lists windows/docker/compose/linux/deno-deploy/kubernetes/azure-*/cloud-run only). + +Central tension for the plan: doctrine's deploy pattern is A7 (A2 core + A6 router); a plugin +delivery adds A5 on top. Both the A5 thinness law and the A7 core-centralization law converge: +convention lives in `-core`, the delivery shell stays thin. + +## 1. Binding axioms (citation → implication for deploy) + +1. **A1 public types first; `mod.ts` before classes** (`01-thesis-and-axioms.md:20-24`, + `02-public-surface.md:31-42`) → author deploy-core's port, 7-op contract type, and base-schema + config types first, in a README quick-start, before any adapter. +2. **A2/A3 simple over easy; 80% in one chained call** (`01:26-36`) → one entry verb per package + (`createDeployRuntime`/`registerDeployTarget`); advanced opts via `withX`. +3. **A4 base classes stub-only; no cross-package implementation inheritance** (`01:38-43`, + `03-base-and-derived-classes.md:28-73`) → adapters never `extends` a core base; flow/port bases + abstract-only (`03:52-79,209-227`). +4. **A5 composition over inheritance** (`01:46-49`) → **R-DEPLOY-4**: each target config member + *spreads* `DeployTargetBaseSchema`; no per-target config base-class hierarchy + (`ARCHETYPE-7:71`, `06:311-314`; shipped pattern `deploy-schema.ts:163-173,185-197,227-241,253-260,272-279`). +5. **A7 wrap upstream, don't reinvent** (`01:59-63`) → **R-DEPLOY-1**: Aspire adapters delegate to + `aspire publish`/`aspire deploy`; deno-deploy wraps `deno deploy`; AWS/CF/Vercel adapters wrap + vendor CLI/SDK (`ARCHETYPE-7:68`, `06:305-309`). +6. **A10 composition root over container; constructor injection** (`01:79-82`, `07:14-42`) → + deploy-core wires via one `createX()` factory with `?? default` collaborators; the plugin's + service is a composition root handing a declarative description to `createPluginService` + (`11:246-249`). +7. **A11 name the extension axis; no premature port** (`01:84-88`, `07:83-113`) → **R-DEPLOY-5**: + port justified because ≥2 adapters foreseeable; "deploy target" is the named axis (`07:96`). +8. **A13 explicit crash boundaries** (`01:96-100`, `08`) → bare-metal activation health-gated, + paired with `rollback`; cloud `rollback` maps to platform-native mechanism, never a silent + no-op (`06:319-321`). +9. **A9 one structure per archetype; fold smaller into larger** (`01:73-76`, `06:348-366`). +10. **A14 tests are fitness functions; publish gate = doctrine gate** (`01:102-106`) → + `deno publish --dry-run`, `deno doc`, `deno task arch:check` per package. +11. **A6/A7 helpers justified; `@std/*` first** (`01:52-63`). +12. **Public-surface budget** (`02:136-138`, F-5): ≤20 exports per `mod.ts` or split subpaths; + JSDoc + `@example` per export (`02:141-171`). oRPC-bound `-core`/service packages may use + `--allow-slow-types` (sanctioned exception `02:217-242`). + +## 2. Archetype fit + +### (a) `plugins/deploy` → ARCHETYPE-5 (plugin) + +- Thin userland glue that wires/composes core-owned primitives and re-exports sibling contracts; + owning a contribution axis is a smell (`ARCHETYPE-5-plugin.md:1-10`, `06:157-214`). +- Folder shape: top-level `mod.ts`, `README.md`, `deno.json`, `verify-plugin.ts`, contribution + folders (`contracts/`, `services/`, `database/`, `jobs/|sagas/|triggers/`, `streams/`) as + siblings of `src/` (`06:181-198`; reconciled per `ARCHETYPE-5:46-50`, debt + `doctrine-06-archetype-5-folder-shape` `arch-debt.md:2118-2135`). +- Design Checkpoint (`ARCHETYPE-5:121-137`): per wired axis, name which core/sibling primitive is + composed and re-exported (never redefined); slices = reuse-core-contracts → service/runtime + wiring → verification → host integration. +- Concept of Done (`ARCHETYPE-5:138-147` + parity checklist `11:126-133`): `verify-plugin.ts` + passes; runtime declarations validated against host loader; DB schema folder correct; contracts + imported-not-redefined; golden test per scaffold emitter; `plugin doctor` coverage; registered + `scaffold.runtime` e2e case; contract exercised in-repo with a soundness test. "Thin ≠ thin + quality budget" (R-PLUGIN-PARITY `11:97-138`). +- Gates (matrix `archetype-gate-matrix.md:20-40`, Arch 5): F-1, F-3, F-5–F-12, F-13-subtype, + F-14–F-19. Runtime/Aspire validation required; consumer-import validation required (`:63-65`). + +### (b) `deploy-core` → ARCHETYPE-2 (integration) + +- Wraps external systems behind a package-owned port with ≥1 adapter; port belongs to the package; + `createX(options)` factory; tech adapters get their own subpath; `./testing` exposes in-memory + adapters (`ARCHETYPE-2-integration.md:17-22`, `06:41-77`). +- Design Checkpoint (`ARCHETYPE-2:80-93`): name external system, port shape, adapter set, + composition root, permissions, consumer import impact; slices = port+contract → adapters → + consumer wiring. Concept of Done (`:94-101`). +- Gates: full universal F-1…F-19 (F-2, F-4 apply, unlike A5). Runtime/Aspire optional (required + when exercised against a real backend); consumer-import required. +- Home of the 7-op uniform contract, closed-on-key `deploy-target-registry`, centralized + health-gate/OTEL/secrets/rollback conventions (`ARCHETYPE-7:38-44,70-72`, `06:288-321`); + R-DEPLOY-3: shared across all targets, never re-implemented per target. + +### (c) Per-cloud adapters → ARCHETYPE-2 thin adapters + +- "Thin backend adapters — one file group per technology behind the core's backend port" + (`11:56-58`). Implement the uniform op set; a target implements the subset it supports + (`ARCHETYPE-7:51-62`). R-DEPLOY-1 + R-DEPLOY-4 bind. AP-11 watch: no premature target-specific + base class (`ARCHETYPE-7:106-108`). + +### Composite ARCHETYPE-7 conformance + +- Arch-7 gate column = union of A2 (core) + A6 (router) gates, plus **F-DEPLOY-1** (each + registered adapter implements the uniform 7-op contract or a declared subset — AST + registry + scan) and **F-DEPLOY-2** (no target-specific business logic in the command surface; conventions + in core — import graph + AST) (`archetype-gate-matrix.md:13-16,51-57`, `ARCHETYPE-7:75-86`). +- **F-DEPLOY-* are seeded `reviewed`, not `gated`, until the deployment packages exist** + (`ARCHETYPE-7:9-16`, `arch-debt.md:2058-2063`). + +### CLI router (ARCHETYPE-6) rules if a router surface is added + +- R-A6-N5/F-CLI-27 declarative `class XCli extends CliRoot` + single factory; R-A6-N7 `console.*` + only under `kernel/presentation/output/**`; R-A6-N10 `kernel/extension-points.ts` aggregates + every Registry; R-A6-N13/F-CLI-1 per-layer LOC caps (`ARCHETYPE-6-cli-tooling.md:136-155`). + R-DEPLOY-2/F-DEPLOY-2: the deploy router only parses and routes. + +## 3. Plugin host contribution kinds (`@netscript/plugin`) + +**Closed contribution-axis vocabulary today** (`packages/plugin/src/domain/constants.ts:16-40`, +`CONTRIBUTION_AXES`): `service` · `background-processor` · `stream-topic` · `database-schema` · +`runtime-config-topic` · `contract-version` · `e2e` · `telemetry` · `migration` · `aspire`. + +`PluginContributions` manifest groups (`src/config/domain/plugin-contributions.ts`): +`cli.doctorChecks` (**hard-coded literal union — only `'auth-backend'`**, `:14-18`; a deploy +doctor check requires widening this union in core), `services` (`{name, entrypoint, port?}`), +`backgroundProcessors`, `streamTopics` (`{name, subject}`), `databaseSchemas` (plain `*.prisma`), +`runtimeConfigTopics`, `contractVersions`, `e2e`, `telemetry`, `migrations`, `aspire` (string +module ref). + +Builder verbs (`src/config/builders/plugin-builder.ts:74-282`): `withName/withVersion/…` + one +`with*` per axis → `build(): PluginManifest`. Contribution abstracts: stub-only +`PluginContribution` base + per-axis subclasses (`src/abstracts/`). + +Plugin categories (`PluginType`, `constants.ts:5-13`): `'background-processor' | 'api' | +'frontend' | 'utility'`. Deploy plugin likely `'utility'` (or `'api'` if it exposes a service). + +Installer/protocol manifest (`scaffold.plugin.json`, `src/protocol/manifest.ts`): +`PluginInstallerManifest` (schemaVersion 1, name, version, peerDependencies, capabilities, +scaffolder, optional postScripts/provider/officialSource). `PluginManifestCapabilities` = +`{ hasDatabaseMigrations, hasRoutes, hasBackgroundWorkers, supportsMcpScaffold? }` (`:16-26`) — +**fixed set, no deploy capability flag**. `PluginManifestScaffolder` = +`{ export, requiredPermissions: {net,read,write} }` (`:28-34`). `parsePluginManifest` validates +statically without executing plugin code (`:240-263`). + +Host lifecycle surface (subpaths, `packages/plugin/deno.json:5-19`): `./config`, `./abstracts`, +`./contract-base`, `./service`, `./protocol`, `./sdk` (walker/AST/manifest-resolver/ +registry-emitter/bootstrap), `./cli` (mount/doctor/registry-emit, `applyScaffoldPlan`, +`runDoctorReport`), `./scaffold`, `./adapter`, `./loader`, `./templates`, `./testing`. +`LIFECYCLE_HOOK_NAMES` = `setup | beforeGenerate | afterGenerate | teardown` (`constants.ts:42-51`). + +**Frontend contribution seam — MISSING/PLANNED.** `'frontend'` exists only as a plugin *category*, +not a contribution axis; no frontend abstract or manifest group. Net-new core work would need: +new `ContributionAxis` literal, `FrontendContribution` type, `PluginFrontendContribution` +abstract, `withFrontend` builder verb, a capability flag. (`07:101` names "frontend framework: +fresh (today), future expansion" as an anticipated axis. A parallel seed run `plan/frontend-contrib` +exists on this topic.) + +## 4. Plugin thinness doctrine (11) — seam rules + +**Law 1 — R-PLUGIN-THIN / core-centralization** (`11:14-64`): every convention-bearing or +by-design-repeating primitive lives in a core `@netscript/*` package; the plugin carries only its +own specifics and re-exports contract types from its `-core` sibling. Promotion test +(`11:255-271`): a primitive stays local only while exactly one plugin consumes it; second consumer +→ promote to core; prefer a subpath/injected port on an existing core over a new top-level +package (`11:265-271`). + +What belongs where for deploy: + +- `@netscript/plugin` (base): base contract (`/contract-base`) + base service (`/service`) seams; + a new mandatory route/error/builder step is a `@netscript/plugin` change (`11:273-277`). +- `deploy-core`: the port(s), closed-on-key target registry, 7-op contract, health gating, OTEL, + secrets, rollback (R-DEPLOY-3). Contract authored here so dependencies run + `@netscript/plugin` → `deploy-core` → adapters → thin plugin, never cycling (`11:164-169,251-252`). +- Adapters: one file-group per technology; target-specific fields only. +- `plugins/deploy`: declares contributions, wires composition root, ships schema/starter + resources, selects the active adapter/target, re-exports contract types; `mod.ts` a few lines. + +**Law 2 — R-PLUGIN-SEAM** (`11:153-232`): (1) contract spreads `BASE_PLUGIN_CONTRACT_ROUTES` +(mandatory `describe` route wired to `BASE_PLUGIN_ERRORS` + `PluginCapabilitiesSchema`), declared +`satisfies BasePluginContract` (`contract-base/mod.ts:24-35`, `11:193-202`) — a genuine compile +guard; (2) connector service produced through `createPluginService(...)` (fixed builder chain +cors→logger→openapi→docs→database→middleware→context→withRPC→withHealth→withServiceInfo) handed a +data-only description (`service/mod.ts:18-33`, `11:204-211`). Realized structurally +(satisfies/extends + spread), not by a runtime class lattice; no cross-package implementation +inheritance (`11:176-179`). + +Anti-patterns (`ARCHETYPE-5:81-102`, `11:66-79`): fat plugin owning conventions (push down); +re-implementing core conventions; engine-coupling counter-example (binding a store to concrete +`Deno.Kv` instead of the shared port — deploy analog: welding an adapter to a vendor SDK instead +of routing shared secrets/state/health through the core port); AP-11 load side effects; AP-14 +redefining sibling contracts; AP-22 sub-barrels; AP-23 inline bodies in wiring; AP-24 +switch-over-kind; AP-25 `Deno.*`/`console.*`/`fetch` in non-edge plugin files. + +Parity checklist = plugin acceptance gate (`11:126-151`); the only accepted casts are the +`as unknown as` contract bridge + the top-router `any`. Closing an issue with unchecked gate +items is a process violation (`11:148-151`). + +## 5. Relevant existing debt (`arch-debt.md`) — absorb/retire, don't re-open + +- **`DEPLOY-ARCHETYPE-7-CORE-SEED`** (`:2011-2063`) — the anchor. A7 names the law but the core + package does not exist; deploy lives in `packages/cli/src/public/features/deploy/`, conventions + in `packages/cli/src/kernel/domain/deploy/` (pure target-agnostic modules with injected ports: + `secrets-convention.ts`, `rollback-convention.ts`, `health-gate.ts`, `activation-convention.ts`, + `observability-convention.ts`). Closes when a standalone deploy core owns those + the registry, + every adapter is 7-op, and F-DEPLOY-1/2 promote `reviewed`→`gated`. **The new `deploy-core` IS + this extraction.** +- **`DEPLOY-SECRETS-ROLLBACK-CORE`** (`:1804-1837`) — shared secrets/rollback primitives landed in + the CLI kernel; residual = adapters delegating to them. New adapters delegate, not fork. +- **`DEPLOY-BAREMETAL-PUBLIC-WIRING`** (`:2065-2094`) — 7-op adapter proven with injected fakes + but not composed onto the public deploy path (registry descriptors constructed with no ports → + only 6-op subset advertised at runtime). Missing production composition root. +- **`cli-deploy-artifacts-missing`** (`:1344-1374`) — `netscript deploy` first-class for + bare-metal + deno-deploy; Dockerfile/Compose/K8s generation tracked by the Aspire-compose + adapter (#343). +- **`DEPLOY-S7-APPHOST-COMPOSE-GEN`** (`:1763-1802`) — Aspire compose-publishing generation + deferred as a shared cross-slice primitive (Deno resources register as `addExecutable`, not + containers). +- **`cli-deploy-linux-integration-untested`** (`:1403-1413`, open) — Linux systemd lane not + integration-exercised against live `systemctl`. +- **`packages/cli` AP-1 / verdict Restructure** (`:756`, Wave 6 "command registry/deploy target + seams") — A7 core extraction is its downstream continuation; do not duplicate + (`ARCHETYPE-7:100-102`, `arch-debt.md:2027-2030`). +- **`config-plugin-specific-schema-debt`** (`:915-934`) — `@netscript/config` still owns + plugin-specific schemas (incl. deploy/runtime surfaces); target = move to + `@netscript/plugin-*-core/config` subpaths. Bears directly on where deploy config lives. +- Plugin-host debt: `ISSUE-167-STANDALONE-PLUGIN-PROTOCOL` (`:235`), `PLUGIN-USERLAND-SOURCE-COPY` + (`:29`), `PLUGIN-LIST-MANIFEST-REGISTRATION-BLOCKER` (`:1929`), + `PLUGIN-RUNTIME-DEPENDENCY-ENTRYPOINT-EXPORTS` (`:1953`), `PLUGIN-RUNTIME-ADAPTER-RELOCATION` + (`:1868`). + +**Shipped config contract** (`deploy-schema.ts`): `DeployTargetBaseSchema` is a plain Zod raw +shape spread by every member (`:21-156`), already carrying R-DEPLOY-3 conventions as sub-blocks — +`activation` (retain + symlink/dir-swap + healthGate), `secrets` (`envFile`, `mode`), `otel`, +`health`, `docker`. AWS/Cloudflare/Vercel absent from the target map (`:288-316`) — new members +spread `deployTargetBaseShape` + vendor fields (follow `DenoDeployTargetSchema:227-241`, +`CloudRunDeployTargetSchema:272-279`). + +## 6. Plan-gate essence (PLAN-EVAL pass bar) + +Separate session; hard stop; every box checked or `FAIL_PLAN` (`plan-gate.md:1-40`): +research present + re-baselined; decisions locked with rationale; open-decision sweep (any +deferred decision that would force rework → FAIL_PLAN — e.g. the **verb-vocabulary lock** +`build/install/uninstall` vs `up/down` deferred at `06:340-346` must be resolved or proven +rework-safe); commit slices enumerated, ordered, <30, each naming proof + gate + files; risk +register; gate set selected from the matrix; deferred scope explicit; **jsr-audit applied to the +planned public surface** (oRPC-bound contract → sanctioned `--allow-slow-types`, name it). +Plus archetype-selection statement (`archetypes/README.md:39-46`): archetype + justification, +overlays, doctrine verdict, gates, known debt, in-scope AP codes. Two FAIL_PLAN cycles → escalate. + +## 7. Fitness functions / anti-patterns most in play + +- **AP-24** switch-over-tagged-union instead of registry (`09:166-183`) → typed closed-on-key + `deploy-target-registry` populated at composition (`07:114-142`, `06:296-298`). +- **AP-4** cross-package implementation inheritance (`09:54-57`). +- **AP-9** premature abstraction / one configurable helper hiding aws/cf/vercel differences — + prefer clearly-named sibling adapters (`09:79-83`, `ARCHETYPE-2:103-105`). +- **AP-3** god interface — the uniform contract is a closed 7-op set with declared subsets, not an + accreting optional-method bag (`09:46-52`, `ARCHETYPE-7:51-62`). +- **AP-11/AP-25** module-load side effects / `Deno.*`/`fetch`/`Deno.Command` outside adapter + edges (`09:90-93,186-195`). +- **AP-13/F-14** `console.log` in published code — status/logs via telemetry port or + `OutputEvent`, except CLI presentation (`09:99-103,291-293`). +- **AP-1/F-1** monolithic files (CLI `pipeline.ts` at 1,869 LOC is the cautionary example). +- **AP-16/F-11** forbidden generic folders; allowed vocabulary: `application/ adapters/ domain/ + ports/ runtime/ state/ middleware/ presets/ registry/ diagnostics/ presentation/ testing/ + internal/` (`09:113-116,271-274`). +- **AP-14/F-15** no re-export of vendor SDKs (`09:105-107,294-298`). +- **AP-19/F-9** permissions declared per README, matching actual `Deno.*` calls (`09:130-134`). +- **F-DEPLOY-1/2** seeded `reviewed` → flip to `gated` on extraction (`ARCHETYPE-7:75-86`). +- **F-4** inheritance audit; **F-5** surface budget; **F-6/F-7** JSR publishability + doc-score + (slow types sanctioned only for oRPC-bound `-core`/service); **F-16** folder cardinality ≤12 / + depth ≤4; **F-3** layering `domain→ports→application→adapters`; **F-17** abstract-derived + co-location; **F-19** scoped gate runners. +- **R-COMP-DECL** (`07:220-252`) + **R-COMP-EXT-MANIFEST** (`07:254-289`): ≥2 extension axes → + documented `extension-points.ts` aggregating every Registry. +- Debt discipline (`09:378-395`): unfixable violations need named, owned, time-bounded + `arch-debt.md` entries. diff --git a/.llm/runs/plan-deploy-plugin--seed/research/prior-run-distillation.md b/.llm/runs/plan-deploy-plugin--seed/research/prior-run-distillation.md new file mode 100644 index 000000000..1f5f9be8d --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/prior-run-distillation.md @@ -0,0 +1,302 @@ +# Prior-run distillation — plan-unified-runtime--seed as evidence base + +Source: full read of `/home/codex/repos/wt-g8-seed/.llm/runs/plan-unified-runtime--seed/` +(branch `plan/unified-runtime`, worktree wt-g8-seed) by an Opus 4.8 research sub-agent of this run, +2026-07-18. All paths below are relative to that run dir unless repo-rooted. The supervisor +(Fable 5) additionally read `adversarial-nitro-vs-own.md` and `nitro-vs-own-synthesis.md` directly. + +**Orientation.** The prior run (#824) designed a Nitro-v3-hosted "unified runtime" board +(UR-0…UR-12 + DD-RESEARCH), passed Stage-G PLAN-EVAL (`plan-eval.md:63` `PASS`), but was +drafts-only, never filed (`supervisor.md:28-34`, `plan-eval.md:47`). A later adversarial +nitro-vs-own pair proposed pivoting away from Nitro-as-host toward NetScript-owned provider +adapters behind a new `@netscript/deploy` package — the seed of the owner's ratified deploy-plugin +direction. The canonical UR bodies were never re-branched for that pivot +(`adversarial-nitro-vs-own.md:43-49`; `nitro-vs-own-synthesis.md:30-32`). + +## 1. What UR proposed (per canonical doc + slot-map) + +13 UR slots (UR-0…UR-12) + non-slot successor DD-RESEARCH; all at milestone `0.0.1-beta.13` +(`slot-map.md:11-28,68-73`). Owning packs: D1 composition-host, D2 capability-matrix, D3 +board-mechanics. + +- **UR-0 — Hostable-service lifecycle contract.** NEW prerequisite (Stage-F F5). `build`/`start`/ + `stop` surface (or `[Symbol.asyncDispose]` handle) a host drives without owning a listener; + reuses shipped `ServiceShutdownCoordinator` policy (idempotency, `AbortController`, + `DEFAULT_DRAIN_TIMEOUT_MS = 30_000`, LIFO teardown, `ShutdownReport`) (`UR-0.md:31-44`; cites + `packages/service/src/builder/service-builder-impl.ts:423-432,501-521`, `service-shutdown.ts`). + **REUSABLE** — host-neutral lifecycle; only the "Nitro `close` hook drives stop" framing is + Nitro-tied (`UR-0.md:42-44,61`). +- **UR-1 — Single logical composition root / no application-created loopback** (`UR-1.md:3,17-24`). + Universal invariant = "logical graph identity — one composition root"; physical single-process is + a per-preset capability; cross-preset rule = "no application-created loopback" (in-process Fetch + delegation, never a socket back to the host listener). **REUSABLE verbatim** + (`research/nitro-vs-own-rev2.md:168`). +- **UR-2 — Nitro owns listener/lifecycle; single-shot `close` disposal** (`UR-2.md:3,18-26`). + "Nitro owns process startup, the single listener, top-level `error` observation, and shutdown." + **DEAD-SHAPE / must be REPLACED** — the load-bearing "Nitro is the host" card; rev1/rev2 replace + it with a NetScript host-runtime port + optional `NitroHostAdapter` + (`research/nitro-vs-own.md:104`; `research/nitro-vs-own-rev2.md:169`). +- **UR-3 — Mount Fresh via `app.handler()`** (`UR-3.md:19-27`). Fresh as an opaque Fetch delegate, + never `app.listen()`; disjoint route spaces (RPC / health / Fresh-UI / static); F-5 static-asset + ownership fork. **REUSABLE if generalized** to a host-neutral Fetch/static contract. +- **UR-4 — In-process oRPC host bridge over `ServiceApp.fetch`** (`UR-4.md:19-40`). Web `Request` + into `ServiceApp.fetch` / oRPC `RPCHandler` (`packages/service/src/primitives/handlers.ts:115-143`); + "invocation placement over a stable Fetch/RPC contract — 'no socket loopback' is the requirement; + a second codec is not." Host-side bridge ONLY — subset of #451's SDK transport surface, no + `Closes #451`. Forks F-7 (SDK↔service dependency direction), F-4 (WebSocket scope). **REUSABLE**. +- **UR-5 — Capability manifest + build-time rejection compiler + per-preset sagas** + (`UR-5.md:19-38`). Machine-readable manifest per cell: per-capability + `lossless | partial | unsupported` + saga declaration + writer/offline flags; a composition + compiler cross-checks the app's logical-graph requirements and **fails the build** (never + runtime, never silent downgrade) on `unsupported`, warns on `partial`. Sagas rule: + `sagas: supported | externalized | rejected` per cell. **REUSABLE but rename** preset→target + capability manifest (`research/nitro-vs-own.md:102`; `research/nitro-vs-own-rev2.md:170`). +- **UR-6 — Runtime-cell columns: three v1 cells + conformance suite** (`UR-6.md:22-54`). v1 = C1 + `deno_server` (Node-built, `deno run --unstable`), C3 `node_server`, C4 `cloudflare_module`. + `deno_deploy` (C2) WITHDRAWN from v1 (Classic/deployctl sunset 2026-07-20; new platform lacks + Deno queues) → DD-RESEARCH. Pins exact Nitro version + compatibility-date + upgrade-drift gate. + **DEAD-SHAPE for a plugin** — cells are Nitro presets; rev2 replaces preset columns with + emitter/provider cells (`research/nitro-vs-own-rev2.md:171`). +- **UR-7 — Writer-ownership & exclusive-lock database capability** (`UR-7.md:19-32`). Long-lived + cells may hold an exclusive lock; bounded-window cells mark it `unsupported in-cell`. "Default + embedded" (single-writer Turso) must not silently override topology. KEEP #453. **REUSABLE**. +- **UR-8 — Offline-sync as a database-target capability/profile** (`UR-8.md:19-30`). Turso Sync as + `profile`/`n/a` per cell, not a runtime invariant. KEEP #455. **REUSABLE**. +- **UR-9 — KV/queue/cache ownership + durability behind NetScript ports** (`UR-9.md:19-34`). + `KvStore`, `MessageQueue`, `DatabaseAdapter` stay authoritative; Nitro unstorage/db0/task/ocache + are host bindings behind the ports, never exposed to app code. "volatility is removed from the + definition of 'in-process'." **REUSABLE** — core leaf-port ownership + (`research/nitro-vs-own-rev2.md:109-119`). +- **UR-10 — Single-process realization capability** (`UR-10.md:19-33`). One-OS-process / + zero-loopback guarantee only for cells declaring `process: in-process`; cloud/serverless + guarantee logical composition only. KEEP #454. **REUSABLE**. +- **UR-11 — Architecture contracts** (`UR-11.md:18-45`). NEW prerequisite (Stage-F F14): package/ + file ownership + JSR export map for the host package; archetype selection + per-package + fitness/JSR/E2E gate matrix; composition-compiler requirement/manifest types + build/CLI seam; + naming normalize to `@netscript/database` (retire `@netscript/data`); migration/JSR text-import + policy. Blocks UR-1/UR-4/UR-5. **HIGHLY REUSABLE** — where a plugin package boundary would be + named; rev2 expands it to decide/extract `@netscript/deploy` + dependency direction + + emitter/provisioner separation (`research/nitro-vs-own-rev2.md:172`). +- **UR-12 — Epic acceptance + supersession reconciliation** (`UR-12.md:19-30`). Three-cell matrix + green; #451/#453/#454/#455 KEEP-open. Acceptance wording Nitro-tied; rev2 rewrites to "prove the + three cells and that Nitro is absent from the public composition/leaf contracts; optional + emitters must be droppable" (`research/nitro-vs-own-rev2.md:173`). +- **DD-RESEARCH — new-platform Deno Deploy probe** (`DD-RESEARCH.md:19-41`). `0.0.1-stable`; + depends on the conformance harness; must prove a real build+deploy+conformance on `deno deploy` + (not deployctl) with external queue/DB bindings before `deno_deploy` re-enters. **REUSABLE as a + probe card** regardless of host shape. + +**Reusable-vs-dead summary:** REUSABLE = UR-0, UR-1, UR-4, UR-5, UR-7, UR-8, UR-9, UR-10, UR-11, +DD-RESEARCH (rev2:168 lists UR-0/1/3/4/7/8/9/10 as "survive in intent"). DEAD "giant unified +package + Nitro-host" shape = UR-2 (Nitro-owns-listener), UR-6 (Nitro-preset cells), and the +Nitro-specific acceptance wording in UR-3/UR-12. + +## 2. Nitro v3 facts (`research/nitro-v3.md`, `evidence/nitro-v3-live-2026-07-18.md`) + +- **Maturity:** public beta with intentional breaking changes; migration guide a "living beta + document" (`research/nitro-v3.md:20`). Built on Web Standards, Rolldown, Vite 8, Node.js 20 + minimum toolchain (`evidence/nitro-v3-live-2026-07-18.md:9-11`). Verdict: "a credible + host/output substrate, but not yet a production-grade replacement for NetScript's durable + adapters" (`research/nitro-v3.md:7-8`). No explicit license text captured in corpus. +- **Build:** Rolldown/Vite backend build — route compilation, splitting, tree shaking, HMR, + normalized `.output` directory. Nitro's real value = "backend build plus changing provider + artifact/event conventions" (`research/nitro-vs-own.md:36-38`). +- **Presets:** default production output is a Node server; dev is always `nitro-dev` (Node/ESM + worker) (`research/nitro-v3.md:23`). Preset via `NITRO_PRESET`/config; compatibility dates pin + provider behavior. Auto-detected: AWS Amplify, Azure, Cloudflare, Firebase, Netlify, StormKit, + Vercel, Zeabur (`research/nitro-v3.md:40-41`). +- **Deno support:** `deno_server` preset is **built using Node.js**, then launched + `deno run --unstable ... .output/server/index.ts` — "'Deno preset' does not mean a Deno-only + build pipeline or stable-API-only artifact" (`research/nitro-v3.md:21`). `deno_deploy` preset + targets Classic (sunset). +- **Runtime surface maturity** (`research/nitro-v3.md:27-34`): db0 SQL layer experimental; KV + default mount in-memory (loses data on restart); ocache GET/HEAD-only cache "Nitro-native win" + but distinct from durable KV; tasks experimental — "not, by itself, a durable queue, retry + ledger, workflow engine, or saga store"; static files run before middleware/routes; plugin + registration synchronous. +- **Integration conclusion:** rev1 HYBRID: "NetScript owns the composition root, runtime + contracts, and deploy-target port; Nitro is an optional, replaceable output-emitter/host + adapter" (`research/nitro-vs-own.md:9-11`). Decision rule: "own the semantics; rent the volatile + provider translation" (`research/nitro-vs-own.md:122`). Nitro never enters the composition + contract or leaf ports (`nitro-vs-own-synthesis.md:11-13`). + +## 3. Adapter-limit findings — why true cloud-agnosticism fails + +- **General principle:** provider primitives are capability-scoped backings at most; each is "a + FEASIBILITY question, not a mapping exercise" (`nitro-vs-own-synthesis.md:15-17`). Master + Nitro↔NetScript-port L/P/U grid: `research/adapter-mapping.md:17-33`; adapter boundary at + `:37-42`. +- **Cloudflare Workers KV:** eventually consistent, "explicitly unsuitable for atomic + operations/transactions"; cannot back CAS-dependent saga/trigger/idempotency state → rejected by + capability (`adversarial-nitro-vs-own.md:23`; `research/nitro-vs-own-rev2.md:78`). +- **Cloudflare Queues:** guaranteed delivery, batching, retries, delays, DLQ, pull consumers + (`evidence/nitro-vs-own-rev2-live-2026-07-18.md:45-46`) — close to `MessageQueue`, BUT push + consumer is an invocation-time `queue(batch)` handler vs `MessageQueue.listen()`'s caller-owned + long-running loop with drain/stop/`AbortSignal`; batch retry redelivers successful records + unless individually acked (`adversarial-nitro-vs-own.md:23`). +- **Cloudflare Durable Objects:** strongly-consistent attached storage + alarms, but storage is + private to one object instance while `SagaStorePort` needs optimistic saves, transition history, + cross-instance correlation index (`packages/plugin-sagas-core/src/ports/saga-store-port.ts:22-48`) + → dedicated adapter + saga-store proof, not a generic KvStore substitution. +- **AWS Lambda:** [BLOCKER] Lambda Web Adapter proves only an HTTP sidecar + raw-event tunnel + ("merely POSTs the raw Lambda event JSON to a local `/events` HTTP endpoint", "no runtime + hooks"); no SQS polling, `ReportBatchItemFailures`, per-record ack/nack, visibility-timeout, or + any `MessageQueue`/saga port (`adversarial-nitro-vs-own.md:7`). SQS is at-least-once with + duplicates; partial-batch reporting required (`evidence/nitro-vs-own-rev2-live-2026-07-18.md:56-59`). +- **Nitro tasks:** no attempt ledgers, backoff, ack/nack, DLQ; "Nitro is execution plumbing; + NetScript remains the durability/control plane" (`research/adapter-mapping.md:25`). +- **Net:** per-provider standards (KV consistency, queue consumer model, saga storage) cannot be + flattened into one agnostic adapter → capability negotiation + build-time rejection (UR-5 + mechanism) rather than a universal backing. + +## 4. Deno Deploy (new) facts (`research/deno-deploy-new.md`, `evidence/deno-deploy-new-live-2026-07-18.md`) + +- Classic (`dash.deno.com`) + subhosting v1 shut down **2026-07-20**; projects not + auto-transferred; new platform at `console.deno.com`, Deno 2.0 environment + new + application/build model (`evidence/deno-deploy-new-live-2026-07-18.md:10-12,24`). +- `deployctl` sunset; supported path is `deno deploy` / `deno deploy create` + (`evidence:13,43`). +- Runtime requires built-in `Deno.serve()`; legacy std `serve()` fails warmup (`evidence:14`). +- **Queues UNSUPPORTED**: `Deno.Kv.enqueue()`/`listenQueue()` unavailable; official alternative = + external MQ or DB-backed job queue (`evidence:15`) — cannot satisfy `MessageQueue` natively + (`research/deno-deploy-new.md:50-58`, cites `packages/queue/ports/message-queue.ts:39-133`). +- DB/KV supported: assigned Deno KV (`Deno.openKv()`), managed Prisma Postgres, or linked external + DB; `Deno.cron()` remains supported (`evidence:18,53-58`). +- Build contract: one app = one web service + one build config; install/build → artifact → warmup + request → route revision (`evidence:37-44`). **GitHub monorepos not yet supported** — this repo + is a monorepo (`research/deno-deploy-new.md:43-47`). +- Verdict: "C2 is not provable for v1 from current evidence" (`research/deno-deploy-new.md:94-96`). + +## 5. Market landscape (`research/market.md`, `evidence/market-frameworks-live-2026-07-18.md`) + +Cross-market pattern: "one source/application model, multiple target adapters, and a generated +artifact whose topology may differ by target" — target translation lives at an adapter/preset +boundary, not in app routes (`research/market.md:19-27`). + +- **Nuxt/Nitro:** named presets; boring default `.output/server/index.mjs`; escape hatch + `node_cluster` (`research/market.md:10`). +- **Next.js standalone:** `output: "standalone"` → minimal `server.js` + output-file-traced deps; + monorepos need `outputFileTracingRoot` (`research/market.md:11`). Lesson: self-describing + minimal artifact via dependency tracing. +- **React Router 7:** adapter contract = host request → Web Fetch `Request` → framework handler → + `Response`; official adapters Node, Express, Cloudflare (`research/market.md:12`). Lesson: thin, + independently-testable Web Request/Response adapters. +- **SvelteKit:** small build adapters per target (`adapter-node`, adapter-auto); + env/proxy-trust/graceful-shutdown explicit operator inputs (`research/market.md:13`). Lesson: + provider selection at the build boundary + generic-server fallback. +- **Redwood:** static web vs API separated; Docker path runs web and API as distinct units. +- **Wasp:** production = Node server + static client + PostgreSQL, separately deployable; + generates a server Dockerfile. + +Four steal-able rules (`research/market.md:29-49`): (1) stable Fetch handler boundary (shipped +`FetchHandler`, `packages/service/src/types.ts:206-212`); (2) target presets = pure declarations +of capabilities/artifact-shape/ops requirements; (3) emit a manifest (entrypoints, assets, traced +deps, migrations, durable resources, schedules, sidecars, shutdown/health); (4) "single" and +"multi" = two realizations of one logical graph. NetScript differentiator = a "capability-checked +composition graph" (`research/market.md:52-68`). + +## 6. Sagas constraint (`research/sagas-constraint.md`) + +- `SagaDefinition` is not "a background function": durability tier, initial state, handled message + types, correlation rules, handlers, compensations, signal/query handlers, retry, concurrency, + optional schedule (`research/sagas-constraint.md:31-33`). Ports: `SagaStorePort`, + `SagaTransportPort`, `SagaOutboxPort`, `SagaIdempotencyPort` (`:37-45`). +- Verdict: "STALE as a categorical exclusion; VALID as a 'Nitro does not implement saga semantics' + warning" (`:53`). Long-running targets can mount the shipped saga runtime in-process; naive + substitution by tasks discards correlation/transitions/compensation/retry/idempotency/outbox + (`:60-64`). +- Deploy constraint: preset-conditional — serverless/isolate outputs must prove activation, + execution window, storage/transport connectors, and shutdown satisfy the chosen + `SagaDurabilityTier`, else **reject at build time** or route to a macro-service (`:66-71`). + Board rule (`:74-87`): in-process only via the NetScript saga runtime; each target declares + `sagas: supported | externalized | rejected` and proves it; "externalized" = macro-service split + of the same app model, never a silent downgrade. + +## 7. Adversarial verdicts + +**Round A — Stage-F review of the UR board** (`adversarial-findings.md`: 17 findings, 9 BLOCKER / +6 MAJOR / 2 MINOR; all ACCEPTED). Load-bearing: F1 `deno_deploy` cell validates a shutting-down +platform → C2 withdrawn; F2 folding #451 into UR-4 would falsely close #451 → host-bridge-only; +F3 preserve #453/#454 desktop realization contracts; F4 #455 offline-sync KEEP; F5 "the proposed +Nitro host path bypasses shipped service startup/shutdown semantics" → UR-0 created reusing +`ServiceShutdownCoordinator`. Recheck: 13/17 RESOLVED; PLAN-EVAL `PASS` (`plan-eval.md:51-63`). + +**Round B — nitro-vs-own pair** (Sol·max: 3 BLOCKER / 3 MAJOR / 1 MINOR — ALL ACCEPTED): + +- F1 [BLOCKER] AWS citation proves an HTTP sidecar and raw-event tunnel, not a native AWS provider + family. +- F2 [MAJOR] "Official Cloudflare tooling is Deno-compatible" ≠ cheap Deno-native build path — + it is provider-owned Node tooling callable from Deno; Miniflare fidelity limits omitted. +- F3 [BLOCKER] Provider primitives do not satisfy queue/saga contracts at claimed depth. +- F4 [MAJOR] Estimates are arithmetic assertions without work breakdown; exclude live-target + conformance their own acceptance rule requires. +- F5 [MAJOR] `@netscript/deploy` specified as both neutral core and cross-domain god-object; + `ResourceBindingResolverPort` / `ActivationRouterPort` absorb leaf concerns; dependency-cycle + risk; conflicts with doctrine AP-3/AP-9 (`adversarial-nitro-vs-own.md:35-41`). +- F6 [BLOCKER] Rev2's UR delta table is not a filable branch — canonical bodies Nitro-specific + throughout; provider-native HYBRID needs a full canonical re-branch + new adversarial/PLAN-EVAL. +- F7 [MINOR] vercel-deno is research-only (20 commits / 11 stars / no releases at review). + +**Stable ground surviving all rounds (`nitro-vs-own-synthesis.md:8-19`):** (1) owner decision RULE +ratified: "per target, the provider-native wrapper wins over a Nitro preset iff it passes the same +conformance suite, exposes every required native binding/event/config surface, and costs less to +maintain"; Nitro never enters the composition contract or leaf ports; whatever emits, emits behind +the NetScript-owned deploy/emitter port. (2) NetScript ports authoritative; provider primitives +capability-scoped backings. (3) Docker-image long tail (Koyeb/Sevalla/Coolify/Dokploy/Fly.io) = +thin adapters on the existing Aspire/Docker lane (#346 lineage). + +**Unresolved owner fork N1** (`nitro-vs-own-synthesis.md:34-51`): N1-A file locked board + probe +cards (CF-PROBE, AWS-PROBE, DEPLOY-ARCH); N1-B full provider-native re-branch. The owner instead +ratified the deploy-plugin direction — effectively N1-B formalized as a plugin (this run). + +## 8. Already-plugin-shaped material + +`research/nitro-vs-own-rev2.md §3` "Proposed `@netscript/deploy`" (`:100-146`) anticipates the +plugin decomposition: + +- Move deploy domain out of CLI into a callable Archetype-2/7 core; CLI stays the Archetype-6 + presentation router — "one stable port, one adapter per target, and a thin CLI router" + (`:20-23,124-127`, citing `docs/architecture/doctrine/06-archetypes.md:257-299`). +- Suggested surface (`:130-141`): `DeploymentPlan`/`DeploymentCell` (declarative physical + topology); `CapabilityRequirement`/`CapabilityManifest` (reuses UR-5); `DeployTargetPort` + (`plan/emit/up/down/status/logs/rollback/secrets` — extracts shipped + `packages/cli/src/kernel/domain/deploy/deploy-target-port.ts:1-128`); `ArtifactEmitterPort`; + `ResourceBindingResolverPort`; `ProvisionerPort`; `ActivationRouterPort`; + `DeployTargetRegistry` (extracts `packages/cli/src/kernel/extension-points.ts:23-37`). +- Dependency direction (`:143-146`): "leaf packages define semantic ports; provider adapter + packages depend on those ports; `@netscript/deploy` composes adapters and resource declarations; + CLI depends on deploy. `@netscript/deploy` must not absorb KV/queue/saga behavior or become a + cloud god-object." ⚠ Adversarial F5 flagged this exact surface as a potential god-object with a + cycle risk — the plugin redesign must supply the explicit package/import graph the rev2 sketch + lacked. +- Leaf ownership (`:109-119`): `@netscript/kv`, `@netscript/queue`, `@netscript/database`, + workers/sagas/triggers/streams cores, service/Fresh/oRPC one Fetch host contract. "The provider + name is an extension axis, not a replacement domain" (`:98`). +- Per-cloud capability sketches: Cloudflare table (`:76-81`), AWS narrative (`:84-93`); per-target + v1 recommendation table (`:150-159`). N1-A feasibility cards (CF-PROBE / AWS-PROBE / + DEPLOY-ARCH) are pre-drafted plugin-work cards (`nitro-vs-own-synthesis.md:40-44`). +- Doctrine anchors: `06-archetypes.md:119-145,257-299`; `07-composition-and-extension.md:15-37,70-76`; + `08-runtime-state-failure.md:17-25`; `02-public-surface.md` + `09-anti-patterns...md` AP-3/AP-9. + +## 9. Evidence freshness + +All corpus research/evidence dated **2026-07-18** (same day as this run). Deno Deploy Classic +sunset 2026-07-20. Prior-run baseline `origin/main @ 56cf84b5`; PR #822 frame at head +`49bd014f...`. Committed evidence extracts under `evidence/` with `SHA256SUMS` (verified in the +prior run's PLAN-EVAL): nitro-v3, deno-deploy-new, market-frameworks, orpc-fresh (oRPC v2 beta +banner; repo pins `^1.14.6`), netscript-deno-doc (11 `deno doc --filter` runs), pr822-frame, +nitro-vs-own live + rev2 live (Cloudflare Workers/Wrangler/Vite/Miniflare/Queues/DO/KV docs, AWS +Lambda Web Adapter, SQS docs, lowlighter/vercel-deno, Denoflare, Vercel Build Output API). +Freshness caveats: Nitro v3 / oRPC / Fresh are mutable beta URLs. + +## Key load-bearing quotes for reuse + +- "logical graph identity — one composition root" + "no application-created loopback" + (`UR-1.md:20-24`). +- "invocation placement over a stable Fetch/RPC contract" (`UR-4.md:23`). +- Owner rule: "the provider-native wrapper wins over a Nitro preset iff it passes the same + conformance suite, exposes every required native binding/event/config surface, and costs less to + maintain" (`nitro-vs-own-synthesis.md:9-13`). +- "own the semantics; rent the volatile provider translation" (`research/nitro-vs-own.md:122`). +- "`@netscript/deploy` must not absorb KV/queue/saga behavior or become a cloud god-object" + (`research/nitro-vs-own-rev2.md:145-146`). diff --git a/.llm/runs/plan-deploy-plugin--seed/research/provider-deploy-surfaces.md b/.llm/runs/plan-deploy-plugin--seed/research/provider-deploy-surfaces.md new file mode 100644 index 000000000..5aca64dc1 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/research/provider-deploy-surfaces.md @@ -0,0 +1,179 @@ +# Provider deploy-surface research (live, 2026-07-18) + +Source: Opus 4.8 web-research sub-agent of this run, 2026-07-18, primary sources only (official +docs/repos), URLs inline. Frame: "Deno-native first, then Node compat where needed"; wrap +well-maintained standard packages/APIs (doctrine A7), implement the rest. + +## 1. Cloudflare + +- **Mechanism:** Workers on **workerd** (V8 isolates — not a Node or Deno process). Deploy via + `wrangler deploy` driven by `wrangler.jsonc`/`wrangler.toml` (entry `main`, + `compatibility_date`, resource bindings) — https://developers.cloudflare.com/workers/wrangler/configuration/. + Static assets via Workers Static Assets (`assets` binding) or Pages. OS-level workloads via + **Cloudflare Containers** (Docker image alongside a Worker). +- **Wrap surface:** npm `wrangler` CLI (`deploy`, `versions`, `containers`, KV/R2/D1/Queues/ + Secrets management) — https://developers.cloudflare.com/workers/wrangler/commands/general/#deploy; + Cloudflare REST API (`api.cloudflare.com`) for direct script upload; npm + `@cloudflare/containers` — https://developers.cloudflare.com/containers/; CI action + `cloudflare/wrangler-action` — https://github.com/cloudflare/wrangler-action. +- **Deno support:** none natively. "Deno-native first" on Workers = Web-platform-standard code + (fetch handler, Request/Response, Web Streams, crypto) running unmodified on both; `node:` + built-ins via the `nodejs_compat` flag (steadily improved through 2025) — + https://developers.cloudflare.com/workers/runtime-apis/nodejs/, + https://blog.cloudflare.com/nodejs-workers-2025/. True Deno runtime ⇒ Containers with a + `denoland/deno` image. +- **Primitives for an optimized scaffold:** Durable Objects (incl. SQLite-backed), KV, Queues, + R2, D1, Hyperdrive, Workers AI, Vectorize, Workflows; Workers for Platforms (dispatch + namespaces) for multi-tenant. Containers: `containers[]` + DO binding + migrations; each + instance backed by a DO sidecar — https://developers.cloudflare.com/durable-objects/api/container/. + Containers public beta since late June 2025, Workers Paid — + https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/, + https://developers.cloudflare.com/containers/pricing/. +- **CI auth:** API token + account ID as CI secrets — + https://developers.cloudflare.com/workers/ci-cd/external-cicd/github-actions/. **No native + OIDC/trusted publishing for wrangler yet** (open request: + https://github.com/cloudflare/workers-sdk/discussions/11434). + +## 2. Vercel + +- **Mechanism:** `vercel` CLI (`build`/`deploy`), Git integration, and — the framework seam — + **Build Output API v3**: emit `.vercel/output` (`config.json`, `functions/*.func` with + `.vc-config.json`, `static/`) fully describing a deployment ("framework-defined + infrastructure") — https://vercel.com/docs/build-output-api, + https://vercel.com/docs/build-output-api/configuration. NetScript can emit the dir and run + `vercel deploy --prebuilt`. +- **Wrap surface:** npm `vercel` CLI; Vercel REST API + typed `@vercel/sdk` — + https://vercel.com/docs/rest-api/sdk; reference examples + https://github.com/vercel/examples/tree/main/build-output-api. +- **Deno support:** not an official runtime. Official runtimes: Node.js, Bun (since 2025), + Python/Go/Ruby — https://vercel.com/docs/functions/runtimes, + https://vercel.com/blog/bun-runtime-on-vercel-functions. Community `vercel-deno` + (`vercel-community/deno`, MIT): per-function `"runtime": "vercel-deno@3.2.0"` in + `vercel.json`; latest tagged release 3.1.1 (Jul 2024), a 3.2.0 commit Jan 2026 — community- + owned, version-lag risk — https://github.com/vercel-community/deno. +- **Primitives:** Vercel Functions on fluid compute (2025 default; in-function concurrency, + active-CPU pricing) + Edge Functions. Storage is Marketplace-model: Vercel Blob + Edge Config + native; **KV = Upstash Redis**, **Postgres = Neon** (native Vercel Postgres sunset June 2025) — + https://vercel.com/docs/storage, https://vercel.com/marketplace/upstash. Queues via Upstash + QStash (Vercel Queues beta). +- **CI auth:** `VERCEL_TOKEN`; Vercel Functions can mint OIDC tokens toward AWS/GCP. + +## 3. AWS + +- **Mechanisms for Deno apps:** (1) Lambda container images / custom runtime + (`denoland/deno-lambda` — https://hub.docker.com/r/denoland/deno-lambda, + https://docs.deno.com/examples/aws_lambda_tutorial/); (2) **Lambda Web Adapter** (LWA, + `aws/aws-lambda-web-adapter`, AWS-official, ~2.7k stars, active through Feb 2026): any HTTP/1.1 + server (incl. `Deno.serve`) runs unchanged; same image runs on EC2/Fargate/local; Function + URLs/API GW/ALB; response streaming; readiness via `AWS_LWA_*`; ships as layer or copied + binary — https://github.com/aws/aws-lambda-web-adapter; (3) containers on ECS/Fargate or App + Runner for always-on — https://docs.deno.com/runtime/deploy/. + (Prior-run adversarial F1 stands: LWA is an HTTP sidecar; it does NOT provide SQS/event + semantics.) +- **IaC wrap candidates:** **Pulumi Automation API** (Apache-2.0; embed IaC programmatically, + inline programs, no CLI/no Pulumi Cloud account) — + https://www.pulumi.com/docs/iac/concepts/automation-api/; SST v3/ion (MIT, Pulumi+Terraform + under the hood, components tied to SST's runtime — reference not dependency; + https://sst.dev/docs/, https://github.com/pulumi/pulumi-cdk/issues/64); AWS CDK / + CloudFormation/SAM (first-party, Node-heavy); OpenTofu (MPL-2.0). +- **Deno support:** no managed Deno runtime; custom runtime or container image — a + container/adapter story. +- **Scaffold seams:** SQS, EventBridge, DynamoDB, ElastiCache (Redis/Valkey), S3 via + `npm:@aws-sdk/*` or IaC provisioning. +- **CI auth:** GitHub Actions OIDC → `sts:AssumeRoleWithWebIdentity` + (`aws-actions/configure-aws-credentials`); long-lived keys legacy. + +## 4. Fly.io + +- **Mechanism:** `fly deploy` (flyctl) builds a Docker image and boots **Fly Machines** + (Firecracker microVMs) per local `fly.toml` — https://fly.io/docs/launch/deploy/, + https://fly.io/docs/reference/configuration/. +- **Wrap surface:** flyctl; **Machines REST API** (OpenAPI 3.0, docs.machines.dev) — Machines, + Apps, Volumes, Certificates, Tokens (OIDC) — https://fly.io/docs/machines/api/. `fly.toml` + optional when driving the Machines API directly. Machines boot ~300ms. +- **Deno support:** container-based; official Deno guide — `fly launch` auto-generates a + Dockerfile — https://fly.io/docs/js/frameworks/deno/. +- **Primitives:** Fly Managed Postgres, Upstash Redis, Fly Volumes, Tigris object storage, + LiteFS. (Deno-KV/LiteFS blueprint retired Oct 2024 — + https://fly.io/docs/blueprints/deno-kv-litefs/.) +- **CI auth:** `FLY_API_TOKEN` (deploy tokens); Machines API Tokens resource can mint OIDC. + +## 5. Deno Deploy (new, console.deno.com) + +- Complete rework on a Deno 2.0 execution environment; new dashboard console.deno.com; Deno AND + Node apps (Next/Astro/SvelteKit first-class) — https://docs.deno.com/deploy/. **Classic + (dash.deno.com) + subhosting v1 shut down 2026-07-20.** +- **CLI:** `deno deploy` built into the runtime (`deploy create` interactive AND fully-flagged + for CI: `--source github|local`, `--framework-preset`, `--runtime-mode dynamic|static`, + `--region us|eu|global`); manages env vars, databases (`database provision --kind + denokv|prisma`, `database link`), logs, sandboxes, cloud connections — + https://docs.deno.com/runtime/reference/cli/deploy/. App config via a `deploy` section in + `deno.json` (Jan 2026) — https://docs.deno.com/deploy/changelog/. Terraform provider exists + for subhosting. +- **Capabilities:** Deno KV supported since Aug 2025 (`Deno.openKv()`), **but KV queues, + read-replication, backups, primary-region selection NOT available**; Postgres external-link or + provisioned Prisma Postgres (per-timeline isolated schemas); `Deno.cron()` auto-discovered + (Mar 2026); **Queues NOT supported** (explicit ❌ vs Classic); OpenTelemetry export; OIDC + tokens (`@deno/oidc` on JSR); Cloud Connections (AWS/GCP without static creds); Deno Sandbox; + static sites; instant rollback; 2 regions (us/eu + global); self-hostable regions — + https://docs.deno.com/deploy/, https://docs.deno.com/deploy/changelog/. +- **CI auth:** `DENO_DEPLOY_TOKEN` (user or org tokens, Dec 2025). + +## 6. Thin targets — Koyeb, Sevalla, Coolify, Dokploy + +All four are "Dockerfile/image (or Git repo) + env vars" platforms ⇒ **one shared +container-deploy path serves all four**, differing only in API client + resource mapping. +Managed: **Koyeb** (REST API https://www.koyeb.com/docs/reference/api + CLI; containers/git/ +functions as serverless containers), **Sevalla** (Kinsta PaaS on K8s + Cloudflare edge; REST API +v3 + deploy hooks — https://docs.sevalla.com/sevalla-api/overview, +https://api-docs.sevalla.com/). Self-hosted OSS (user supplies base URL + token): **Coolify** +(REST API, Bearer token — https://coolify.io/docs/api-reference/authorization), **Dokploy** +(REST API + CLI — https://docs.dokploy.com/docs/api). + +## 7. Nitro v3 (wrap/integration candidate only) + +- Preset system: provider-specific output from one codebase; presets via + `NITRO_PRESET`/config/`--preset`; zero-config CI auto-detect (cloudflare, vercel, netlify, + aws-amplify, azure, firebase, stormkit, zeabur); compatibility dates — + https://nitro.build/deploy. +- **Not cleanly consumable by a non-Nitro build:** presets are coupled to Nitro's rollup build + pipeline (a preset = output format/entry/rollup config), not a standalone library. The + reusable value is the **knowledge** encoded per preset (how to emit `.vercel/output`, a + `_worker.js`, etc.) — reimplement, don't import. +- Deno preset: `deno_server` "build[s] your Nitro server using Node.js to run within Deno + Runtime" — https://nitro.build/deploy/runtimes/deno — Node-compat-oriented, at odds with + Deno-native-first. License MIT (UnJS) — studying/vendoring preset logic is unencumbered. + +## 8. Cross-cutting + +No single library spans all targets. **Pulumi Automation API** is the strongest embeddable IaC +candidate (AWS lane). **Serverless Framework v4 is disqualified**: login + license key required, +paid for orgs >$2M revenue (v3 EOL 2024) — +https://forum.serverless.com/t/login-and-license-key-required-for-serverless-v4/20300, +https://github.com/brefphp/bref/issues/1749. OpenTofu (MPL-2.0) if a Terraform-shaped engine is +ever needed. SST ion = AWS reference, not dependency. + +## Wrap-vs-implement recommendation table + +| Target | Wrap candidate | Quality/risk | Recommendation | +|---|---|---|---| +| deploy-core | — | n/a | Implement ourselves (thin orchestration over adapters) | +| Cloudflare Workers | `wrangler` CLI + `wrangler.jsonc`; CF REST API | First-party, excellent; workerd ≠ Deno | Wrap wrangler; emit wrangler config + Web-standard handler; `nodejs_compat` for node: | +| Cloudflare (true Deno) | `@cloudflare/containers` + `denoland/deno` image | Public beta Jun 2025, Paid plan, DO-backed | Wrap Containers (shares container core) | +| Vercel | Build Output API v3 + `vercel` CLI / `@vercel/sdk` | First-party, stable, made for frameworks | Wrap Build Output API (`.vercel/output` → `vercel deploy --prebuilt`) | +| Vercel (Deno runtime) | `vercel-deno` (MIT, community) | Version-lag risk, no SLA | Optional opt-in; default Node runtime | +| AWS (server app) | AWS Lambda Web Adapter + container image | AWS-official, active; HTTP only (F1) | Wrap LWA as primary Deno-on-Lambda HTTP path | +| AWS (IaC/primitives) | Pulumi Automation API (Apache-2.0) | Strong, embeddable, no account | Wrap for IaC; SST ion as reference | +| AWS (serverless fw) | Serverless Framework v4 | License-gated, paid tier | Do NOT wrap | +| Fly.io | Machines REST API + flyctl | First-party, clean REST, OIDC | Wrap Machines API (flyctl fallback); shares container core | +| Deno Deploy (new) | `deno deploy` CLI + `deno.json` deploy config | First-party, native, best Deno fit; no queues, KV limited | Wrap — the Deno-native-first default | +| Koyeb / Sevalla / Coolify / Dokploy | Each platform's REST API | First-party APIs; self-hosted need URL+token | Wrap via shared container-deploy core | +| Nitro v3 presets | Nitro build (MIT) | Coupled to rollup build; Deno preset is Node-built | Implement ourselves; study presets as reference | + +**Net architectural read:** thin PaaS targets (koyeb/sevalla/coolify/dokploy) + Fly + +CF-Containers collapse onto one `deploy-container` path (build image → push → platform API → +env). The genuinely bespoke adapters are the three framework-defined-infrastructure platforms — +Cloudflare Workers (wrangler + bindings), Vercel (Build Output API), Deno Deploy (`deno deploy`) — +each rewarding a purpose-built wrapper. Deno Deploy is the Deno-native-first default; +AWS/CF-Workers/Vercel are the Node-compat/Web-standard tier. Only cross-cutting dependency worth +taking: Pulumi Automation API (AWS IaC). Serverless v4 disqualified on licensing. From bcec7e5346730062432c01171b667ed161c7d4e9 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:20:41 +0200 Subject: [PATCH 03/20] =?UTF-8?q?plan(deploy-plugin):=20canonical=20DP-0..?= =?UTF-8?q?DP-2=20=E2=80=94=20concept/goal=20frame,=20family=20graph,=20de?= =?UTF-8?q?ploy-core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DP-0 locks the carried-forward laws (owner conformance rule, leaf-port authority, capability manifest + build-time rejection, probes) and the three-tier Deno-native-first frame. DP-1 supplies the explicit package/import graph adversarial F5 demanded. DP-2 designs plugin-deploy-core as the DEPLOY-ARCHETYPE-7-CORE-SEED extraction with the 7-op port, emitter split, capability compiler, and binding-transport contract. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../canonical/DP-0-concept-and-goal-frame.md | 108 +++++++++++ .../canonical/DP-1-family-architecture.md | 132 ++++++++++++++ .../design/canonical/DP-2-deploy-core.md | 168 ++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md new file mode 100644 index 000000000..cb8b8146d --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md @@ -0,0 +1,108 @@ +# DP-0 — Concept & goal frame: deploy as a plugin family + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. Downstream +> passes (Sol adversarial, Kimi doc-story) amend in place; GitHub wins after any future filing. + +## 1. The ratified concept (owner, 2026-07-18, verbatim basis) + +The deploy story is rebuilt as a **plugin family** — `plugin-deploy` with a `deploy-core` and +per-cloud adapters — composed **the exact way the auth plugin composes** (core + provider +adapters). The owner's four ratified points (kickoff `plan-deploy-plugin--seed/kickoff.md:13-34`): + +1. **Goal reframed: "Deno native first, then Node compat where needed."** Full Deno-nativity is + unreachable whether we choose Nitro, existing Deno adapters, or our own implementation. +2. **No giant new deploy package.** The auth lesson: use NetScript at what it's best at — + composability. A deploy plugin with a plugin core and adapters, exactly like auth. +3. **The plugin route redefines the whole deploy story** — including what already shipped — and + solves the layer problem: the plugin contributes to every layer (CLI, services, scaffolding, + soon frontend). We can scaffold a *cloudflare-optimized* project shipping cloudflare-first + seams (workers, durable objects, KV), same for AWS, Vercel. This "defeats" cloud-agnosticism + in one sense while making it actually credible — true agnosticism is nearly impossible since + each cloud has its own standards and adapters have limits (proven in the prior run's + analysis). +4. **The family** — `plugin-deploy`, `deploy-core`, `deploy-aws`, `deploy-cloudflare`, + `deploy-vercel`, … — mirrors how major frameworks proceed, with the plugin system + (contribution, scaffolding, CLI, services) making it more powerful. + +Selective wrapping stays in force: sprinkle Nitro integration where it makes sense, Aspire-native +where it matters, implement ourselves enterprise-grade elsewhere — unless a well-written standard +package is worth wrapping per doctrine (A7 wrap-don't-reinvent). + +## 2. What this run supersedes and what it keeps + +The prior seed run (`plan-unified-runtime--seed`, issue #824 context) designed a Nitro-hosted +unified-runtime board (UR-0…UR-12). It was **never filed**; its adversarial rounds produced the +evidence this design is built on (`research/prior-run-distillation.md`). + +**Dead (not carried):** Nitro as host/listener (UR-2); Nitro-preset cell columns (UR-6); +unproven "owned AWS/CF family" claims; the `@netscript/deploy` god-object sketch (adversarial F5). + +**Carried forward as design law:** + +| # | Law | Origin | +| --- | --- | --- | +| L-1 | **Owner conformance rule**: per target, a provider-native wrapper wins over any generic emitter iff it passes the same conformance suite, exposes every required native binding/event/config surface, and costs less to maintain. Nothing third-party ever enters the composition contract or leaf ports; whatever emits, emits behind a NetScript-owned port. | `nitro-vs-own-synthesis.md:9-13` | +| L-2 | **Leaf ports stay authoritative.** `KvStore`, `MessageQueue`, `DatabaseAdapter`, saga/worker/trigger/stream ports are defined by their leaf packages; provider primitives are capability-scoped backings at most, each a feasibility question, not a mapping exercise. | `nitro-vs-own-synthesis.md:15-17` | +| L-3 | **Capability manifest + build-time rejection.** Every target ships a machine-readable manifest (`lossless | partial | unsupported` per capability; `sagas: supported | externalized | rejected`); a composition compiler fails the build on `unsupported` — never at runtime, never silently downgraded. | UR-5 (`prior-run-distillation.md` §1) | +| L-4 | **One logical composition root; no application-created loopback.** Physical single-process is a per-target capability; cross-target invariant is in-process Fetch delegation, never a socket back to the host listener. | UR-1/UR-4 | +| L-5 | **Hostable lifecycle.** Deploy artifacts drive services through the shipped `ServiceShutdownCoordinator` semantics (`build`/`start`/`stop`, drain, LIFO teardown) — no bypass of `.onStartup()`/`.onShutdown()`. | UR-0 (prior Stage-F F5) | +| L-6 | **Docker-image long tail = thin adapters** on one shared container path (fly/koyeb/sevalla/coolify/dokploy + CF Containers). | `nitro-vs-own-synthesis.md:18-19`, `provider-deploy-surfaces.md` §6 | +| L-7 | **Probe before ownership claims.** AWS event semantics and CF Workers fidelity are proven by probes (AWS-PROBE, CF-PROBE) before their adapters graduate past HTTP/emit scope. | adversarial F1–F4 | + +## 3. The goal frame, made operational + +**"Deno native first, then Node compat where needed"** decomposes into three tiers, applied per +target — not a single global stance: + +| Tier | Meaning | Targets | +| --- | --- | --- | +| **T1 — Deno-native process** | The artifact is a Deno process (`deno run`/`deno compile`/`Deno.serve`) or a `denoland/deno` container image. No Node toolchain in the app's runtime path. | Deno Deploy (new), bare-metal (Servy/systemd), Aspire lanes (`addExecutable` deno run), container path (Fly, koyeb, sevalla, coolify, dokploy, CF Containers), AWS via LWA container | +| **T2 — Web-standard portable** | App code is written against Web Platform APIs (Fetch handler, Request/Response, Web Streams, crypto) so the same code runs on Deno *and* V8-isolate runtimes. NetScript emits an isolate entry that adapts `ServiceApp.fetch`. | Cloudflare Workers (workerd) | +| **T3 — Node compat where needed** | The provider's first-class runtime is Node; NetScript emits Node-compatible output and declares the compat cost honestly. Deno realization stays available as opt-in (community runtime) or via containers. | Vercel functions (Node default, `vercel-deno` opt-in), `nodejs_compat` gaps on Workers | + +The invariant across tiers: **the application model never changes.** One logical composition +graph, NetScript-owned ports, Web-standard handler boundary. Tiers describe the *realization*, +selected and validated per target through the capability manifest (L-3). + +## 4. Why a plugin makes agnosticism credible (the positioning argument) + +True cloud-agnosticism fails empirically: KV consistency models, queue consumer models +(push-invocation vs owned `listen()` loop), and saga storage differ per provider in ways no +universal adapter can flatten (`research/prior-run-distillation.md` §3). Frameworks that claim +otherwise ship lowest-common-denominator adapters. + +The plugin family takes the opposite position: + +- **The core is honestly agnostic** — a small set of NetScript-owned ports, a target registry, + shared conventions, and a capability compiler that *refuses* to pretend (build-time rejection, + backend-truthful capability discovery — the auth board's S1 lesson applied to deploy). +- **The adapters are honestly provider-first** — each wraps the provider's own first-class + tooling (wrangler, Build Output API, `deno deploy`, Machines API) and declares exactly what it + can and cannot do. +- **The scaffold makes provider-first real** — a cloudflare-optimized scaffold ships seams that + are cloudflare-first from day one (DO/KV/Queues backings selected for the leaf ports, wrangler + config emitted, CI wired), instead of a generic project the user must bend. Same for AWS + product suites and Vercel's marketplace primitives (`scaffold-stories.md`). + +That combination — strict ports + honest capabilities + optimized scaffolds — is the +"capability-checked composition graph" differentiator the market research identified +(`research/prior-run-distillation.md` §5), and it is exactly what the plugin contribution system +makes possible: the deploy plugin contributes CLI, scaffolding, streams/telemetry, and (when the +frontend axis lands) UI, at every layer, per provider. + +## 5. Design principles (bind all DP docs) + +1. **Auth parity by default.** Package topology, naming, manifest triad, adapter thinness, + contract/versioning, install path — copy the auth pattern unless a deploy-specific inversion + is named in `research/auth-composition-anatomy.md` §9 (multi-target registry, user-visible + leaf config, no v1 HTTP service). +2. **Extraction before invention.** The shipped CLI kernel already contains the 7-op port, the + registry, and the conventions (debt `DEPLOY-ARCHETYPE-7-CORE-SEED`). Wave 1 moves them; new + capability lands in later waves. +3. **No board mutation from this run.** Draft texts only; the filing manifest is produced for a + future owner-ratified stage H. +4. **Doctrine gates are the acceptance language.** F-DEPLOY-1/2 flip `reviewed`→`gated` when the + core lands; every adapter card carries its archetype gate set; issue drafts phrase acceptance + as `- [ ] gate:` predicates (board-parity lesson). +5. **Honest tiers.** Every adapter card states its tier (T1/T2/T3), its wrap target, and its + capability manifest sketch — no "supports X" without the manifest row that proves it. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md new file mode 100644 index 000000000..ba9da0aec --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md @@ -0,0 +1,132 @@ +# DP-1 — Family architecture: packages, dependency graph, archetypes + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. + +This document is the explicit package/import graph the prior run's adversarial F5 demanded before +any deploy core could be accepted ("`@netscript/deploy` is specified as both a neutral core and a +cross-domain composition god-object … require an explicit package/import graph and +adapter-registration ownership", `adversarial-nitro-vs-own.md:35-41`). + +## 1. The family + +| Package | Dir | Archetype | Role | +| --- | --- | --- | --- | +| `@netscript/plugin-deploy` | `plugins/deploy/` | **A5** thin plugin | Delivery shell: manifest + contributions (CLI, scaffold, streams, telemetry, doctor), composition root, re-exports | +| `@netscript/plugin-deploy-core` | `packages/plugin-deploy-core/` | **A2** integration core | Ports (7-op target port, emitter port), closed-on-key target registry, capability manifest + compiler, shared conventions (activation/secrets/otel/rollback/health), compile/build primitive, deploy config schema base + schema registry, testing fakes | +| `@netscript/deploy-aspire` | `packages/deploy-aspire/` | **A2** adapter | Wraps the `aspire` CLI (`publish`/`deploy`/`destroy`): compose, docker, kubernetes, azure-aca, azure-app-service, azure-aks lanes (extraction of the shipped Aspire target adapters) | +| `@netscript/deploy-baremetal` | `packages/deploy-baremetal/` | **A2** adapter | OS-service lane: Windows (Servy) + Linux (systemd) install/lifecycle (extraction of the shipped service targets) | +| `@netscript/deploy-deno` | `packages/deploy-deno/` | **A2** adapter | Deno Deploy (new platform): wraps the built-in `deno deploy` CLI + `deno.json` deploy config. **T1 flagship.** | +| `@netscript/deploy-container` | `packages/deploy-container/` | **A2** adapter (+subpaths) | Shared OCI path: image build/push + generic container-platform port; thin platform clients as subpaths `./fly`, `./koyeb`, `./sevalla`, `./coolify`, `./dokploy` (+ backs CF Containers and cloud-run) | +| `@netscript/deploy-cloudflare` | `packages/deploy-cloudflare/` | **A2** adapter | Workers lane: wraps `wrangler` (config emission + deploy) with Web-standard worker entry over `ServiceApp.fetch`; Containers lane delegates to `deploy-container`. **Probe-gated (CF-PROBE).** | +| `@netscript/deploy-vercel` | `packages/deploy-vercel/` | **A2** adapter | Build Output API v3 emitter (`.vercel/output` → `vercel deploy --prebuilt`); Node runtime default, `vercel-deno` opt-in. **Probe-gated.** | +| `@netscript/deploy-aws` | `packages/deploy-aws/` | **A2** adapter | HTTP path: Lambda Web Adapter container (shares `deploy-container` build); Fargate/App Runner via container path; optional `./pulumi` subpath (Automation API) for IaC. **Probe-gated (AWS-PROBE); HTTP-scope only until the event-semantics probe passes.** | + +Naming decisions (owner fork OF-1 records the alternative): + +- Core keeps the `plugin-` prefix — `@netscript/plugin-deploy-core` — for exact auth parity + (`plugin-auth-core`). The kickoff's shorthand "deploy-core" names the same package. +- Adapters drop the prefix (`@netscript/deploy-`), exactly as `auth-workos` does + (`research/auth-composition-anatomy.md` §7). +- Plugin dir is the bare canonical name `plugins/deploy` (`officialSource.canonicalName: + "deploy"`). +- `@netscript/deploy-deno` (not `deploy-deno-deploy`); its registry/target key stays the shipped + `deno-deploy` for config back-compat. +- Thin PaaS platforms are **subpaths of `deploy-container`**, not packages (owner fork OF-2): + they are tiny REST clients with no heavy SDKs; A9 says pick the smallest structure and fold + smaller into larger; five extra JSR packages would multiply publish surface for ~100-line + clients. A platform graduates to its own package the moment it needs a real SDK dependency or + its own scaffold story. + +## 2. The dependency graph (the F5 answer) + +``` + @netscript/service (Fetch handler, shutdown coordinator) + @netscript/config (project-level loader seam) + leaf cores: kv, queue, database, plugin-{workers,sagas,triggers,streams}-core + ▲ + │ (types only: capability IDs, binding declarations) + ┌─────────────────┴───────────────┐ + │ @netscript/plugin-deploy-core │ ◄── @netscript/plugin (base seams) + └─────────────────▲───────────────┘ + │ implements ports, registers into registry + ┌──────────┬───────────┼────────────┬─────────────┬────────────┐ + │deploy- │deploy- │deploy- │deploy- │deploy- │deploy- + │aspire │baremetal │deno │container │cloudflare │vercel / aws + └──────────┴───────────┴────────────┴─────▲───────┴────────────┘ + (cloudflare containers-lane and aws http-lane depend on deploy-container) + ▲ + │ composes: selects adapters, builds registry, wires config + @netscript/plugin-deploy (plugins/deploy) + ▲ + │ mounts CLI contribution, install/doctor, scaffold + @netscript/cli (thin A6 router; presentation only) +``` + +Rules that keep it acyclic and non-god: + +- **R-GRAPH-1 — leafward types only.** `plugin-deploy-core` may import *types and IDs* from leaf + cores (capability identifiers, binding declaration types) — never leaf implementations, never + provider SDKs. Enforced by import-graph gate (F-DEPLOY-2 extension; see DP-2 §6). +- **R-GRAPH-2 — adapters depend on core + their one provider surface.** An adapter imports + `plugin-deploy-core` ports plus its wrapped upstream (wrangler, `@vercel/sdk`, Machines API + client, aspire CLI shell). Never another adapter, never the plugin — with the single declared + exception that `deploy-cloudflare` (Containers lane) and `deploy-aws` (image lane) depend on + `deploy-container`'s exported container-build port, which is itself a core-port implementation, + not a cross-adapter reach-around (it is the L-6 shared path). +- **R-GRAPH-3 — the plugin composes; nothing depends on the plugin.** `plugins/deploy` is the + only place all adapters meet. Adapter *registration* happens in the plugin's composition root + (closed-on-key registry, populated at composition — AP-24 avoidance), or in the scaffolded + project's generated registry for install-time target selection. +- **R-GRAPH-4 — leaf provider backings live with the leaves.** A Cloudflare KV backing for + `KvStore`, an SQS backing for `MessageQueue`, a DO-backed saga store — these are **leaf-domain + adapter packages** (e.g. future `@netscript/kv-cloudflare`, `@netscript/queue-sqs`), owned and + gated by the leaf, never by the deploy family. Deploy adapters only *declare and transport + bindings by name* (DP-2 §4) and *select* leaf backings at scaffold time (contribution matrix). + This is the precise line that makes deploy-core not a god-object: **deploy owns placement, + artifacts, and lifecycle; leaves own semantics.** +- **R-GRAPH-5 — CLI is presentation.** `packages/cli` keeps only the A6 thin router + (`deploy ` parse/route) delegating to core + registry; during wave 3 the group + itself becomes a plugin-contributed mount (DP-4). No business logic remains in the CLI + (F-DEPLOY-2). + +## 3. Archetype + gate mapping + +| Package | Archetype | Gate set (from `archetype-gate-matrix.md`) | Notes | +| --- | --- | --- | --- | +| plugin-deploy | A5 | F-1, F-3, F-5–F-12, F-13-subtype, F-14–F-19 + runtime/Aspire + consumer-import + R-PLUGIN-PARITY checklist | `verify-plugin.ts`, golden scaffold tests, `plugin doctor`, `scaffold.runtime` case | +| plugin-deploy-core | A2 | Full F-1…F-19 + F-DEPLOY-1/2 (flip `reviewed`→`gated`) | oRPC not used ⇒ no slow-type exception needed unless a contract lands (DP-4) | +| each adapter | A2 | Full F-1…F-19; runtime validation required when exercised against a real backend | Wrap-target permission READMEs (F-9/AP-19) | +| composite | A7 | Union A2(core) + A6(router) + F-DEPLOY-1 (uniform 7-op or declared subset) + F-DEPLOY-2 (no target logic in command surface) | The family as a whole is doctrine's Archetype 7, delivered through A5 | + +The doctrine tension named in `research/doctrine-constraints.md` (A7 composite vs A5 delivery) +resolves cleanly: **A7 describes the core+router shape; A5 describes the delivery vehicle.** Both +laws point the same direction — conventions in core, thin shells everywhere else. Doctrine 06/11 +get a follow-up amendment noting the plugin-delivered variant of A7 (debt-entry draft in +`plan.md` §Debt). + +## 4. Waves (delivery phasing — mirrors "extraction before invention") + +| Wave | Content | Exit proof | +| --- | --- | --- | +| **W1 — Core extraction** | `plugin-deploy-core` extracted from the CLI kernel (port, registry, conventions, compile engine, config base + schema registry); CLI re-wired over it; F-DEPLOY-1/2 → `gated`; debt `DEPLOY-ARCHETYPE-7-CORE-SEED` + `DEPLOY-SECRETS-ROLLBACK-CORE` retired | `deno task e2e:cli` green on unchanged verbs; arch:check green with new gates | +| **W2 — Adapter extraction** | `deploy-aspire`, `deploy-baremetal`, `deploy-deno` split out; per-target config member schemas move adapter-side (retiring the deploy slice of `config-plugin-specific-schema-debt`) | Same E2E green; each package passes its A2 gate set; capability manifests published | +| **W3 — Pluginization** | `plugins/deploy` + host contribution-axis extensions (DP-4); deploy group becomes plugin-mounted with built-in back-compat shim; scaffold emits deploy assets via plugin scaffolder | `plugin install deploy` on a scaffold; `scaffold.runtime` case; doctor + verify-plugin green | +| **W4 — Container path** | `deploy-container` (+ fly/koyeb/sevalla/coolify/dokploy subpaths); first *generated* Dockerfile/compose artifacts (retiring `cli-deploy-artifacts-missing` for the container lane) | Live smoke on ≥1 managed (fly or koyeb) + 1 self-hosted (coolify or dokploy) platform | +| **W5 — Probe-gated clouds** | CF-PROBE → `deploy-cloudflare`; Vercel Build-Output probe → `deploy-vercel`; AWS-PROBE (HTTP) → `deploy-aws` HTTP scope; event-semantics probes remain separate cards | Each adapter ships only after its probe card passes the shared conformance suite (L-1, L-7) | + +Provider-optimized scaffolds (the owner's point 3) ride W3–W5: the scaffold story for a target +ships with (or immediately after) its adapter (`scaffold-stories.md`). + +## 5. Versioning, publishing, conventions + +- All packages pin the workspace version; `jsr:@netscript/*@` specifiers centralized + (`deps:check`); npm upstream deps via `catalog:` (wrangler, `@vercel/sdk`, `@pulumi/pulumi` if + taken) — auth parity (`research/auth-composition-anatomy.md` §7). +- Adapter upstream CLIs invoked as tools (wrangler, vercel, aspire, deno, flyctl) are **invoked, + not imported**, wherever a supported CLI exists — the wrap seam is the process boundary, which + keeps vendor SDK weight out of the module graph (and JSR publish surface). SDK imports are the + exception and are named per adapter card (DP-3). +- String-constant templates (not text imports) for every emitted artifact — the JSR text-import + lineage rule (`jsr-text-import-lineage` memory; permanent doctrine). +- Each package README declares required permissions (F-9/AP-19) — deploy adapters are + permission-heavy (`--allow-run` for CLIs, `--allow-net` for APIs) and must say so exactly. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md new file mode 100644 index 000000000..76a0b4f61 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md @@ -0,0 +1,168 @@ +# DP-2 — `@netscript/plugin-deploy-core`: ports, registry, capabilities, conventions + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. + +Archetype 2 integration core. This is the extraction target of debt +`DEPLOY-ARCHETYPE-7-CORE-SEED` (`arch-debt.md:2011-2063`): the primitives below *already exist* +as target-agnostic modules in `packages/cli/src/kernel/domain/deploy/` and move here with their +tests; net-new elements are marked **NEW**. + +## 1. Public surface (subpaths, layer-named per auth-core parity) + +| Subpath | Contents | +| --- | --- | +| `.` | Curated barrel: `createDeployRuntime`, the two ports, registry factory, manifest types | +| `./domain` | `DeploymentPlan`, `DeployTargetDescriptor`, `ResourceBinding`, operation result types, error taxonomy | +| `./ports` | `DeployTargetPort` (7-op), `ArtifactEmitterPort`, `ContainerBuildPort`, `OsServicePort` (moved), registry port | +| `./capabilities` | `DeployCapabilityManifest`, capability IDs, `compileCapabilityVerdict` (the rejection compiler) — **NEW** (UR-5 reuse) | +| `./conventions` | activation (health-gated symlink/dir-swap), secrets (env-file reference + redaction), rollback, observability/OTEL, health-gate — moved verbatim from the CLI kernel | +| `./build` | The `deno compile` pipeline (compile-runner/targets/platform/bundler/config/format — moved) | +| `./config` | `DeployTargetBaseSchema` (moved from `@netscript/config`), target schema registry — retires the deploy slice of `config-plugin-specific-schema-debt` | +| `./registry` | `createDeployTargetRegistry` (closed-on-key), `DEFAULT_DEPLOY_TARGETS` relocation | +| `./testing` | In-memory target adapter, fake convention ports, manifest fixture builders (A2 requirement) | + +Budget: ≤ 20 exports per subpath (F-5); no oRPC contract in core (the optional plugin contract +lives with the plugin, DP-4), so **no `--allow-slow-types` exception is needed** — core must pass +`deno doc --lint` clean. + +## 2. The 7-op target port (moved, then sharpened) + +The shipped `DeployTargetPort` (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`) +moves as-is in W1; W2 sharpens it in place: + +```ts +type DeployOperation = 'plan' | 'up' | 'down' | 'status' | 'logs' | 'rollback' | 'secrets'; + +interface DeployTargetPort { + readonly key: DeployTargetKey; // 'deno-deploy' | 'compose' | … (open via registry) + readonly operations: readonly DeployOperation[]; // declared subset (F-DEPLOY-1) + readonly capabilities: DeployCapabilityManifest; // §4 — NEW field, backend-truthful + plan(ctx: DeployOperationContext): Promise; // dry-run + artifact emission + up(ctx): Promise; + down(ctx): Promise; + status(ctx): Promise; + logs(ctx): Promise; + rollback(ctx): Promise; // platform-native or convention-backed; never silent no-op + secrets(ctx): Promise; // reference/rotation over the secrets convention +} +``` + +- **Verb vocabulary locked** (resolves the deferred decision at + `06-archetypes.md:340-346` — plan-gate requires it): the canonical surface is + **`netscript deploy `** with the 7-op set. The legacy flat verbs + (`build/install/start/stop/upgrade/uninstall`) remain as **bare-metal-lane aliases** routed to + the `baremetal` target's ops, marked deprecated in help output for two minor releases before + removal (migration-map §4). `plan` subsumes "emit": artifact emission is `plan` with an + `--output-path` (the shipped compose adapter already behaves this way). +- Unsupported ops: **declared subsets**, exactly as shipped. An op absent from `operations` is + never advertised (backend-truthful, auth-S1 lesson); calling it yields + `DeployOperationUnsupportedError` — the auth `AuthBackendOperationUnsupportedError` pattern + (`research/auth-composition-anatomy.md` §4). +- `rollback`/`secrets` graduate from "declared-unsupported everywhere" (#341) to + convention-backed implementations per adapter card (DP-3); the conventions module already + ships the target-agnostic halves (`DEPLOY-SECRETS-ROLLBACK-CORE` debt retires here). + +## 3. `ArtifactEmitterPort` (NEW) — separated so lifecycle ≠ build + +```ts +interface ArtifactEmitterPort { + readonly formats: readonly ArtifactFormat[]; // 'deno-binary' | 'oci-image' | 'wrangler-worker' + // | 'vercel-build-output' | 'aspire-publish' | … + emit(plan: DeploymentPlan, out: EmitTarget): Promise; +} +``` + +Separating emission from lifecycle is what lets L-1 hold: *whatever emits, emits behind this +port* — a provider-native emitter, the shared container builder, or (if ever demanded) an +optional Nitro-driven emitter — all replaceable per target without touching `DeployTargetPort` +consumers. `EmittedArtifactManifest` adopts the market lesson (self-describing artifact: +entrypoints, assets, traced deps, migrations, durable resources, schedules, health/shutdown — +`research/prior-run-distillation.md` §5 rule 3). + +`ContainerBuildPort` is the OCI specialization implemented by `deploy-container` and consumed by +the cloudflare-containers and aws lanes (R-GRAPH-2's declared exception). + +## 4. Capability manifest + rejection compiler (NEW; the heart of honest agnosticism) + +```ts +interface DeployCapabilityManifest { + readonly target: DeployTargetKey; + readonly tier: 'deno-native' | 'web-standard' | 'node-compat'; // DP-0 §3 + readonly process: 'long-lived' | 'bounded-window' | 'isolate'; + readonly capabilities: Readonly>; + readonly sagas: 'supported' | 'externalized' | 'rejected'; + readonly notes?: Readonly>; // honest caveats, surfaced by CLI +} +``` + +- `DeployCapabilityId` is a **closed vocabulary of deployment-relevant capability IDs** + (`http-serve`, `static-assets`, `websocket`, `queue-consume`, `kv-atomic`, `cron`, + `long-running-process`, `exclusive-db-writer`, `offline-sync`, …). IDs *reference* leaf + semantics; their **definitions and conformance tests live with the leaf packages** + (R-GRAPH-4). Core imports only the ID types — this is the exact cut that stops the + `ResourceBindingResolverPort` god-object failure (adversarial F5): core never interprets what + `kv-atomic` means; it only matches requirements against declarations. +- `compileCapabilityVerdict(appRequirements, manifest)` is the **build-time rejection compiler** + (UR-5): `unsupported` → build failure with the manifest note; `partial` → warning that must be + acknowledged in config; never a runtime surprise, never a silent downgrade (L-3). App + requirements derive from the project's logical graph (`appsettings.json` resource/plugin + declarations) — the compiler is invoked by `deploy plan` and by scaffold-time target + selection. +- `sagas` tri-state is verbatim UR-5/sagas-constraint law: `externalized` means a macro-service + split of the same app model, never downgrade-to-tasks + (`research/prior-run-distillation.md` §6). +- **Backend-truthful**: the manifest is authored next to the adapter and exercised by the shared + conformance suite (DP-3 §1); a manifest row the suite cannot demonstrate is a gate failure — + deploy's version of the auth board's p0 (#872). + +## 5. Resource bindings (declarative, opaque to core) + +```ts +interface ResourceBinding { + readonly logicalName: string; // from the project graph (appsettings) + readonly kind: LeafBindingKind; // opaque leaf-owned discriminator: 'kv' | 'queue' | 'database' | … + readonly requirement: readonly DeployCapabilityId[]; // what the app needs of it +} +``` + +Deploy transports bindings **by name into artifacts and environment** (env vars, wrangler +bindings blocks, aspire references) — it never resolves them to SDK objects and never validates +leaf semantics (leaf cores do, at their own composition time). The concrete mapping table +(logicalName → provider handle) is target config authored in `deploy.targets..bindings`, +validated by the adapter's schema member. This replaces rev2's `ResourceBindingResolverPort` and +`ActivationRouterPort` with a declaration + transport contract; event *activation* (queue → +handler wiring on push-model providers) is leaf-adapter territory, gated by the leaf's own +conformance (adversarial F3 honored). + +## 6. Conventions, registry, config + +- **Conventions** move verbatim: `activation-convention.ts` (retain count, symlink/dir-swap, + health gate), `secrets-convention.ts` (env-file reference, 0o600, redaction), + `rollback-convention.ts`, `observability-convention.ts` (OTEL endpoint/protocol/prefix), + `health-gate.ts`, plus `servy-config.ts` → `deploy-baremetal` (target-specific). R-DEPLOY-3 + stands: adapters delegate, never fork. +- **Registry**: `createDeployTargetRegistry(entries, )` — closed-on-key, duplicate-guarded + (auth's `createAuthPresetRegistry` pattern), populated only at composition roots (plugin, + generated project registry, CLI shim). **Multi-target by design** — unlike auth's + single-active backend, a project registry may hold many targets keyed + `[@]` (the auth-inversion named in + `research/auth-composition-anatomy.md` §9). +- **Config**: `DeployTargetBaseSchema` (the shipped Zod raw shape with + activation/secrets/otel/health/docker sub-blocks) moves to `./config`; each adapter exports its + member schema (spreading the base — R-DEPLOY-4); the **target schema registry** composes the + `deploy.targets` discriminated union at load time, so `@netscript/config` retains only the + project-loader seam and never names vendors. Environment dimension: `deploy.targets.` gains + an optional `environments: Record>` overlay — the modeled + replacement for today's opaque `--environment` pass-through (inventory §4 gap). + +## 7. Gates and proof + +- Full A2 gate set; **F-DEPLOY-1** (AST + registry scan: every registered adapter implements the + 7-op contract or a declared subset) and **F-DEPLOY-2** (import graph + AST: no target-specific + business logic outside adapter packages; conventions imported from core) flip + `reviewed` → `gated` in W1 — extended with **R-GRAPH-1** (core imports leaf *types* only; no + provider SDK, no leaf implementation) as a new import-graph assertion. +- `./testing` ships the in-memory target + fixture builders so the plugin, CLI shim, and + conformance kit test without live providers (A2 Concept of Done). +- Quality bar: `deno task quality:scan` (no `any`/casts), `arch:check`, `deno doc --lint` clean, + jsr publish dry-run — per harness pitfall rules. From a178d31ae312e61cf504dc5c4fd6d508ff90a209 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:23:35 +0200 Subject: [PATCH 04/20] =?UTF-8?q?plan(deploy-plugin):=20canonical=20DP-3..?= =?UTF-8?q?DP-5=20=E2=80=94=20adapter=20cards,=20plugin+host=20design,=20w?= =?UTF-8?q?rapping=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DP-3: per-target adapter cards with wrap surfaces, capability sketches, probe gates, and the shared conformance suite. DP-4: plugins/deploy A5 shape, manifest triad, multi-target install model, three named host extensions (cli-command axis, doctor union, capability flag), CLI back-compat shim. DP-5: selective-wrapping decision map with the Nitro conditional re-entry path. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../design/canonical/DP-3-adapter-cards.md | 161 ++++++++++++++++++ .../design/canonical/DP-4-plugin-and-host.md | 145 ++++++++++++++++ .../canonical/DP-5-selective-wrapping.md | 66 +++++++ 3 files changed, 372 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md new file mode 100644 index 000000000..40bbb4067 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md @@ -0,0 +1,161 @@ +# DP-3 — Adapter cards: per-target design, wrap targets, capability sketches + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> Wrap-target evidence: `research/provider-deploy-surfaces.md`; feasibility law: DP-0 L-1…L-7. + +## 0. The shared conformance suite (binds every card) + +One suite, run per target, is the family's acceptance instrument (the deploy analogue of the +auth board's single test kit, S14): it exercises each declared `DeployCapabilityManifest` row — +HTTP serve + static assets, graceful shutdown through `ServiceShutdownCoordinator` (L-5), +health-gate + rollback behavior, secrets redaction, log retrieval, and (where declared) cron, +queue-consume, kv-atomic — against the target's realization (in-memory fake in CI; live probe +lane for the real platform). **A manifest row without a passing suite cell is a gate failure** +(backend-truthful, DP-2 §4). The suite lives in `plugin-deploy-core/testing` + per-adapter live +probes; CI matrix = target × capability × verdict (board-parity lesson 9). + +Every card below states: tier (DP-0 §3), wrapped surface, ops subset, manifest sketch (only +non-obvious rows), scaffold hooks, permissions, and probe gates. + +## 1. `deploy-aspire` (W2 extraction) — T1 + +- **Wraps:** the `aspire` CLI (`publish` / `deploy` / `destroy`) — shipped behavior moved from + `aspire-compose-deploy-target.ts` / `aspire-cloud-deploy-target.ts` unchanged; `docker compose` + shelling for the self-host lane. `@netscript/aspire` (SDK-neutral composition) stays a separate + substrate package — this adapter is the *executor*, that package is the *composer*. +- **Targets/ops:** `compose`, `docker`: plan/up/down/status/logs; `kubernetes`, `azure-aca`, + `azure-app-service`, `azure-aks`: plan/up/down (+status where `aspire` reports it). AppHost + platform-marker validation preserved. `rollback`: convention-backed for compose (previous + emitted dir + dir-swap); platform-native where Azure provides it — else absent from + `operations`. +- **Manifest sketch:** `process: long-lived`; sagas `supported` (long-lived containers); + `queue-consume: lossless` (in-process listeners); `exclusive-db-writer: lossless` on + single-replica compose, `partial` on k8s (replica-dependent, note required). +- **Scaffold hooks:** owns the `deploy-compose-ghcr.yml` workflow template; keeps emitting + nothing hand-authored (aspire publish output stays the artifact) until W4's container path + offers generated Dockerfiles as an alternative. +- **Permissions:** `--allow-run=aspire,docker`, `--allow-read/write` on output dirs. + +## 2. `deploy-baremetal` (W2 extraction) — T1 + +- **Wraps:** Servy (Windows) / systemd (Linux) via the shipped OS-service port; the + `deno compile` build pipeline consumed from core `./build`. +- **Ops:** full 7-op including convention-backed `rollback` (activation retain + symlink/ + dir-swap + health gate — already designed, `DEPLOY-BAREMETAL-PUBLIC-WIRING` debt closes by + composing the ports in the plugin's composition root) and `secrets` (env-file reference, + 0o600). Legacy flat verbs (`build/install/start/stop/upgrade/uninstall`) alias here + (DP-2 §2). +- **Manifest:** `process: long-lived`; everything `lossless` that the host machine provides; + sagas `supported`; `offline-sync: profile-dependent` note. The Linux systemd lane's + integration-test debt (`cli-deploy-linux-integration-untested`) becomes a live-probe card in + this adapter's suite. +- **Permissions:** `--allow-run=servy,systemctl,deno`, filesystem on install base. + +## 3. `deploy-deno` (W2 extraction; T1 flagship) + +- **Wraps:** the built-in **`deno deploy`** CLI (create/env/database/logs) + the `deno.json` + `deploy` config section; keeps the unstable-API preflight guard (improving its transitive-graph + scan is a named follow-up card). Target key stays `deno-deploy`. +- **Ops:** plan (preflight + build config)/up/down/status/logs; `secrets` via + `deno deploy env` (reference model — values never serialized into artifacts); `rollback` via + the platform's instant-rollback revision routing. +- **Manifest (the honesty showcase):** `tier: deno-native`; `process: bounded-window`; + `queue-consume: unsupported` (platform has no queues — note points to `externalized` MQ + bindings); `kv-atomic: partial` (Deno KV without queues/replication controls); + `cron: lossless` (`Deno.cron()` auto-discovered); sagas `externalized` (macro-service split or + external transport) — every row from `research/provider-deploy-surfaces.md` §5. Monorepo git + integration gap noted; local-source deploys (`--source local`) unaffected. +- **Scaffold hooks:** default target of the plain scaffold (Deno-native-first default); + `deploy-deno-deploy.yml` workflow with `DENO_DEPLOY_TOKEN` (org tokens). +- **Permissions:** `--allow-run=deno`, `--allow-net` (API), `--allow-read`. + +## 4. `deploy-container` (W4, NEW) — T1; the L-6 shared path + +- **Owns:** OCI image build (`ContainerBuildPort` impl: Dockerfile generation from the + `EmittedArtifactManifest` — `denoland/deno:2` base per the shipped config defaults — plus + build/push via `docker`/`podman`), registry auth, and a small **generic container-platform + port** (`create/deploy service, set env, logs, status, destroy` over REST). +- **Subpath clients:** `./fly` (Machines REST API, OpenAPI; flyctl fallback), `./koyeb`, + `./sevalla` (managed REST APIs), `./coolify`, `./dokploy` (self-hosted: user-supplied base URL + + token). Each ~thin JSON client mapping the generic port; one shared conformance run each. +- **Ops:** plan (emit Dockerfile/compose + image build)/up/down/status/logs; `secrets` = env-set + via platform API (reference model); `rollback` = platform revision/machine-image swap where the + API offers it, else declared absent. +- **Manifest:** `process: long-lived` containers ⇒ sagas `supported`; capability rows otherwise + inherited from what the *image* runs (the leaf bindings selected at scaffold time) — the + manifest mechanism keeps this honest per project. +- **This retires** the container half of `cli-deploy-artifacts-missing`: NetScript finally emits + a Dockerfile — but as adapter output, not scaffold hand-authoring. +- **Permissions:** `--allow-run=docker,podman,flyctl`, `--allow-net` per platform API. + +## 5. `deploy-cloudflare` (W5; CF-PROBE-gated) — T2 (Workers), T1 (Containers lane) + +- **Wraps:** `wrangler` (npm, invoked as a tool): emits `wrangler.jsonc` (entry, compatibility + date, bindings blocks) + a **worker entry adapting `ServiceApp.fetch`** (Web-standard, L-4: + in-process Fetch delegation), then `wrangler deploy` / `wrangler versions`. Containers lane + delegates image build to `deploy-container` + `@cloudflare/containers` wiring. +- **Probe gate (CF-PROBE, before adapter graduation):** build+deploy a NetScript service to a + live Workers target; measure Miniflare-vs-production fidelity for the dev loop (Miniflare is + a simulator, not an oracle — adversarial F2); demonstrate static-assets, `nodejs_compat` + coverage for the service runtime's `node:` touchpoints; document the CI-token story (no OIDC + yet — a real operational caveat). +- **Ops:** plan (emit config + entry)/up/down (delete)/status/logs (`wrangler tail`); `secrets` + via `wrangler secret` (reference model); `rollback` via `wrangler versions rollback`. +- **Manifest:** `tier: web-standard`; `process: isolate`; `queue-consume: unsupported` **in this + adapter** (CF Queues push-consumer wiring is leaf territory — a future `@netscript/queue-cf` + card, R-GRAPH-4; the binding transport still declares the queue by name); + `kv-atomic: unsupported` (Workers KV no-CAS — capability rejection per L-2); + `long-running-process: unsupported` on Workers / `lossless` on Containers lane; sagas + `rejected` on Workers, `externalized`-or-`supported` via Containers. DO-backed saga/worker + stores are explicitly **probe cards owned by the saga/worker leaves**, not this adapter. +- **Scaffold hooks:** the cloudflare-optimized scaffold story (scaffold-stories §2). +- **Permissions:** `--allow-run=wrangler,npx`, `--allow-net`. + +## 6. `deploy-vercel` (W5; probe-gated) — T3 default, T1 opt-in + +- **Wraps:** **Build Output API v3** — emit `.vercel/output` (config.json, `functions/*.func` + with `.vc-config.json`, `static/`) from the `EmittedArtifactManifest`, then + `vercel deploy --prebuilt`; management via `vercel` CLI/`@vercel/sdk`. +- **Runtime stance:** Node runtime default (fluid compute), honest T3; `vercel-deno` community + runtime as opt-in flag with a version-lag warning (research §2 risk). The Fetch-handler + boundary keeps app code identical across stances. +- **Ops:** plan (emit output dir)/up/down/status/logs; `rollback` via deployment aliasing; + `secrets` via Vercel env API (reference model). +- **Manifest:** `process: bounded-window`; `queue-consume: unsupported` in-adapter (QStash etc. + are leaf bindings); `kv-atomic`: marketplace-dependent note (Upstash Redis backing is a leaf + concern); sagas `externalized`; static assets `lossless` (first-class). +- **Probe:** a minimal NetScript service through Build Output → live deploy → conformance HTTP + suite; validates the `.vc-config.json` emission before the adapter is claimed. + +## 7. `deploy-aws` (W5; AWS-PROBE-gated, HTTP scope first) — T1 via containers + +- **Wraps (HTTP scope):** **AWS Lambda Web Adapter** as layer/binary in a `denoland/deno` + container image (built by `deploy-container`), fronted by Function URL/API GW/ALB; + Fargate/App Runner lanes via the container path. Adversarial F1 stands: **LWA is an HTTP + sidecar** — this adapter claims HTTP hosting only until the event-semantics probes pass. +- **IaC:** optional `./pulumi` subpath wrapping the **Pulumi Automation API** (Apache-2.0, + inline programs, no Pulumi Cloud) for users who want provisioned infra; plain + image+existing-infra deploys need no IaC dependency. Serverless Framework v4 excluded + (licensing). SST ion = reference only. +- **Ops:** plan (emit image + optional IaC program)/up/down/status/logs (CloudWatch); + `secrets` via SSM/Secrets Manager references; `rollback` via Lambda versions/App Runner + revisions. +- **Manifest:** HTTP rows only in v1; `queue-consume: unsupported` with note "SQS backing is + `@netscript/queue` leaf territory (`ReportBatchItemFailures`, per-record ack, visibility + timeout — see AWS-PROBE-EVENTS card)"; sagas `externalized` (or `supported` on Fargate + long-lived). CI auth: GitHub OIDC → `AssumeRoleWithWebIdentity` (no static keys) — scaffold + emits the role-trust snippet. +- **Probe gates:** AWS-PROBE-HTTP (live LWA conformance) before shipping; AWS-PROBE-EVENTS + (SQS conformance design + live probe, leaf-owned) before any event claims. + +## 8. Explicit non-cards + +- **Desktop packaging** (dmg/appimage/deb/msi + release channels) stays outside this family — + epic #830 desktop-graph owns it; the migration map only names the boundary. +- **`@netscript/kv-cloudflare`, `@netscript/queue-sqs`, DO saga stores** — leaf-owned provider + backings (R-GRAPH-4). The scaffold stories *select* them when they exist; this family never + ships them. +- **Netlify, Railway, Render, GCP-direct**: not designed; the registry is open — community + adapters implement the core port. cloud-run's current gcloud lane folds into + `deploy-container` + a `./cloudrun` client if demand holds (micro-decision left to W4). +- **Nitro emitter**: no package. DP-5 records the conditional re-entry path. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md new file mode 100644 index 000000000..ead921cb0 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md @@ -0,0 +1,145 @@ +# DP-4 — `plugins/deploy` and the plugin-host extensions + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. + +## 1. The plugin (`@netscript/plugin-deploy`, `plugins/deploy/`) — Archetype 5 + +Folder shape (doctrine 06:181-198; contribution folders sibling to `src/`): + +``` +plugins/deploy/ + mod.ts # few lines: re-export public surface + deno.json # subpaths: ./public ./plugin ./scaffold ./adapter-cli ./streams ./streams/server + README.md # permissions, target matrix, quick start + verify-plugin.ts # asserts declared axes against the host loader + scaffold.plugin.json # installer manifest (§3) + cli.ts # createPluginAdapter(deployAdapterPlugin).toCli() + scaffold.ts # …​.toScaffold() + src/ + public/mod.ts # definePlugin(...) manifest (§2) + adapter/plugin.ts # NetScriptPlugin connector (install/doctor seams) + adapter/resources/ # starter resources: deploy.config barrel + target snippets + composition/ # composition root: registry assembly from installed adapters + streams/ # deploy event stream schema + server producer (§4) +``` + +Thinness law: every convention lives in `plugin-deploy-core`; the plugin only **wires** +(composition root builds the multi-target registry from the adapters the project installed), +**declares** (manifest, installer JSON, connector), **contributes** (CLI mount, scaffolder, +streams, telemetry, doctor), and **re-exports** (core types). `mod.ts` stays a few lines +(R-PLUGIN-THIN). + +## 2. Runtime manifest (`definePlugin`) + +```ts +definePlugin('@netscript/plugin-deploy', DEPLOY_PLUGIN_VERSION) + .withDisplayName('Deploy').withType('utility') + .withPermissions(DEPLOY_PLUGIN_PERMISSIONS) // --allow-run=, net, read, write + .withCliCommands([{ group: 'deploy', loader: './adapter-cli' }]) // NEW axis (§5) + .withStreamTopics([{ name: 'deploy-events', subject: 'deploy' }]) + .withTelemetry([{ name: 'deploy', loader: './src/telemetry' }]) + .withRuntimeConfigTopics([{ name: 'deploy' }]) // rollout flags (pause target, freeze) + .withMetadata({ tier: 'official' }).build(); +// frozen with: contributions.cli.doctorChecks = ['deploy-target'] // NEW union member (§5) +``` + +**No `withService` in v1** (owner fork OF-4): deploy is CLI/CI-shaped; there is no long-running +HTTP surface, no port, `hasRoutes:false`. The machine-readable surface that replaces a `describe` +route is the **capability manifest**, exposed as `netscript deploy capabilities [] +--json`. If a deploy dashboard/orchestration API is ever wanted, it lands as a later +`withService` + `contracts/v1` addition through the standard base-contract seam +(`...BASE_PLUGIN_CONTRACT_ROUTES` + `satisfies BasePluginContract`) — the seam cost is one +follow-up card, so deferring is rework-safe (plan-gate open-decision sweep). + +## 3. Installer manifest (`scaffold.plugin.json`) + +Re-derived, not copied from auth (`research/auth-composition-anatomy.md` §9 item 8): + +```jsonc +{ + "schemaVersion": 1, + "name": "@netscript/plugin-deploy", + "capabilities": { "hasDatabaseMigrations": false, "hasRoutes": false, + "hasBackgroundWorkers": false, "contributesDeployTargets": true }, // NEW flag (§5) + "scaffolder": { "export": "./scaffold", + "requiredPermissions": { "net": true, "read": true, "write": true } }, + "provider": { "kind": "deploy", "pluginType": "utility", "infrastructureRequires": [] }, + "officialSource": { "canonicalName": "deploy", "pluginDir": "deploy" } +} +``` + +No `.prisma`, no service port, no port-range key, no db/kv requirement — the deploy plugin runs +at build/CI time and in the developer loop, not as a resident service. + +Peer model: **adapters are peerDependencies-style install choices.** `netscript plugin install +deploy` installs plugin + core; `netscript deploy target add ` (new verb, §6) adds the +adapter package import + its config member + scaffold assets for that target. This is the +multi-target inversion of auth's single-active env selection: targets are *added*, not switched. + +## 4. Contributions beyond CLI + +- **Streams**: `deploy-events` durable stream — versioned envelope + `deploy.started/succeeded/failed/rolled-back` with target key, environment, artifact digest, + actor, trace context (W3C propagation like auth's producer). Core deploy success **never + depends on the stream sink** (audit-provider independence — board-parity lesson 8). +- **Telemetry**: span vocabulary for plan/up/rollback, redaction rules (secrets never in spans — + the secrets convention's redaction applied at the telemetry seam). +- **Runtime-config topic `deploy`**: operational flags (freeze deploys, pause a target) readable + without redeploy via `@netscript/runtime-config` — deliberately tiny. +- **Scaffolder**: emits the userland `deploy/` leaf — `deploy/targets.ts` (typed target + definitions the user owns and edits — the named inversion of auth's never-rewritten barrel) + plus per-target assets on `target add` (wrangler.jsonc, fly.toml, workflows…; scaffold-stories + doc). Golden tests per emitter (R-PLUGIN-PARITY). +- **Doctor**: `deploy-target` check — validates each configured target: adapter installed, + required tool on PATH (`wrangler`, `aspire`, `docker`…), credentials env present (names only, + never values), config member parses, capability verdict for the project graph. + +## 5. Host extensions (`@netscript/plugin` — named, small, reviewed) + +Three host changes, each its own slice (they precede W3): + +1. **CLI-command contribution axis** (NEW): `CONTRIBUTION_AXES` + `'cli-command'`, + `CliCommandContribution { group: string; loader: string }`, `PluginCliCommandContribution` + abstract, `withCliCommands` builder verb. The registry emitter renders a generated CLI mount + module; `packages/cli` mounts contributed groups after built-ins at startup (declarative + `CliRoot` composition preserved — R-A6-N5). Collision rule: a contributed group may not + shadow a built-in group name except the deploy back-compat shim (§6). +2. **Doctor-check union widening**: `cli.doctorChecks` type from `readonly 'auth-backend'[]` to a + declared union `readonly ('auth-backend' | 'deploy-target')[]` — still closed (typed registry, + AP-24-safe), one PR whenever a plugin adds a check kind. +3. **Capability flag**: `PluginManifestCapabilities.contributesDeployTargets?: boolean` so the + installer/registry can reason about deploy plugins statically (parallel to + `supportsMcpScaffold`). + +**Frontend axis is out of scope here** — the parallel seed run (`plan/frontend-contrib`) owns the +`frontend` contribution axis design; this run only asserts the deploy plugin will *use* that axis +when it lands (deploy status surfaces in the dev dashboard) and must not design it. + +## 6. CLI delivery and back-compat (owner fork OF-3) + +- **W1–W2 (pre-plugin):** `packages/cli` deploy group re-wired over `plugin-deploy-core` + + extracted adapters. Zero verb changes; `deno task e2e:cli` is the invariant. +- **W3 (plugin-mounted):** the deploy group ships as the plugin's `cli-command` contribution. In + a scaffolded project the group appears when the plugin is installed. **The framework CLI keeps + a thin built-in shim**: if the plugin is absent, `netscript deploy` prints the install hint; + if present, the shim defers to the contributed group. This preserves the documented + `netscript deploy …` UX for every existing project while making the plugin the owner + (recommended resolution of OF-3). +- **New verbs** landing with the plugin: `deploy target add|remove ` (adapter + config + + scaffold assets), `deploy capabilities [] --json`, `deploy doctor` (alias of + `plugin doctor` scoped to deploy). Legacy flat verbs alias to `baremetal` ops with a two-release + deprecation notice (DP-2 §2). + +## 7. Acceptance (plugin Concept of Done, per doctrine 11 parity checklist) + +- `verify-plugin.ts` green (axes: cli-command group, stream topic, telemetry, runtime-config + topic, doctor check). +- Golden test per scaffold emitter; `plugin doctor` covers required-config + tool-missing + + credential-missing paths; a registered `scaffold.runtime` e2e case exercising + `plugin install deploy` → `deploy target add deno-deploy` → `deploy deno-deploy plan` on the + generated workspace. +- Contract-soundness: n/a in v1 (no service contract); the compensating check is a schema test + that `deploy capabilities --json` output validates against the published + `DeployCapabilityManifest` schema. +- Quality: `quality:scan`, `arch:check`, `deno doc --lint`, publish dry-run, jsr-audit — + "thin ≠ thin quality budget". diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md new file mode 100644 index 000000000..94dbb0765 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md @@ -0,0 +1,66 @@ +# DP-5 — Selective wrapping: Nitro, Aspire-native, own, and wrap targets + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> Applies the owner conformance rule (DP-0 L-1) and doctrine A7 (wrap-don't-reinvent) to every +> build/deploy surface. Evidence: `research/provider-deploy-surfaces.md`, +> `research/prior-run-distillation.md` §2/§5. + +## 1. The decision rule, restated for wrapping + +Wrap an upstream surface iff it is (a) the provider's first-class, maintained path, or a +well-written standard package; (b) callable behind a NetScript-owned port without leaking its +types into the public surface (AP-14); and (c) cheaper to maintain than owning the translation. +Prefer **invoking supported CLIs at the process boundary** over importing SDKs — the process +boundary is the thinnest possible wrap seam and keeps vendor weight out of the module graph +(DP-1 §5). Implement ourselves where the semantics are NetScript's value (conventions, +capability compiler, ports) — "own the semantics; rent the volatile provider translation." + +## 2. The map + +| Surface | Decision | Instrument | Rationale | +| --- | --- | --- | --- | +| Deploy semantics: ports, registry, capability compiler, conventions (activation/secrets/rollback/health/otel), binding transport | **Own, enterprise-grade** | `plugin-deploy-core` | This IS the product; no upstream owns these semantics (L-1). Already built target-agnostic in the CLI kernel — extraction, not invention. | +| Local/dev + self-host orchestration | **Aspire-native** | `@netscript/aspire` substrate + `deploy-aspire` shelling `aspire publish/deploy/destroy` | Aspire is where polyglot AppHost composition, dashboard, OTLP, and azure/k8s emission already work; reimplementing its publish pipeline fails the rule. Aspire stays the *composer/executor* — never enters the composition contract (same L-1 line as Nitro). | +| Deno Deploy | **Wrap** `deno deploy` CLI + `deno.json` deploy section | `deploy-deno` | First-party, Deno-native, token+org auth; wrapping deployctl-era surfaces is dead (Classic sunset 2026-07-20). | +| Cloudflare Workers | **Wrap** `wrangler` (invoke) + emit `wrangler.jsonc` + Web-standard entry | `deploy-cloudflare` (CF-PROBE) | Provider-owned Node tooling *callable from* Deno (adversarial F2 framing — honest, not "Deno-native"); the alternative (raw CF REST script upload) forfeits bindings/config/versions management wrangler owns. | +| Cloudflare Containers / Fly / koyeb / sevalla / coolify / dokploy / (cloud-run) | **Wrap platform REST APIs** over one owned container path | `deploy-container` + subpath clients | All are image+env platforms (L-6); the owned piece is Dockerfile/OCI emission + the generic platform port; each client is a thin JSON mapping. | +| Vercel | **Wrap Build Output API v3** (emit `.vercel/output`) + `vercel deploy --prebuilt` | `deploy-vercel` | The API exists precisely for framework authors ("framework-defined infrastructure") — the highest-quality wrap seam in the set. | +| AWS HTTP hosting | **Wrap Lambda Web Adapter** (container layer/binary) | `deploy-aws` (AWS-PROBE-HTTP) | AWS-official, portable image (Lambda/Fargate/EC2); scope-limited to HTTP by adversarial F1. | +| AWS IaC | **Wrap Pulumi Automation API** (optional subpath) | `deploy-aws/pulumi` | Apache-2.0, embeddable inline programs, no vendor account; the only cross-cutting IaC dependency that passes the rule. | +| Serverless Framework v4 | **Reject** | — | Login + license key; paid tier >$2M revenue — unacceptable in an open framework's deploy path. | +| SST ion | **Reference only** | — | MIT but components are welded to SST's runtime/resource-linking; study, don't depend. | +| OpenTofu | **Hold** | — | Only if a Terraform-shaped declarative lane is ever demanded; no current card. | +| Queue/KV/DB/saga provider primitives (SQS, CF Queues/KV/DO, DynamoDB, Upstash…) | **Leaf-owned wrapping, never deploy's** | future leaf adapter packages (`@netscript/queue-sqs`, `kv-cloudflare`, …), each behind its leaf port with a conformance card | R-GRAPH-4 / adversarial F3: activation semantics, ack models, and consistency are leaf feasibility questions. Deploy only transports bindings by name. | + +## 3. Nitro: the conditional re-entry path (owner fork OF-7) + +Position: **no Nitro dependency anywhere in the family.** Grounds (research §7): presets are +coupled to Nitro's rollup pipeline and not programmatically consumable without adopting the whole +Nitro build; its `deno_server` preset is Node-built ("build … using Node.js to run within Deno +Runtime") — against the goal frame; its runtime layers (storage/tasks/db0) were already excluded +from the composition contract (L-1). The presets remain a **reference corpus** (MIT): when +authoring an emitter (`.vercel/output`, `_worker.js` shapes), read the corresponding preset as +prior art and cite it in the adapter card. + +Re-entry condition (kept precise so "sprinkle some nitro where it makes sense" stays possible): +if a target emerges where Nitro's preset demonstrably beats an owned emitter **under the L-1 +conformance rule** (same suite, full native surface, cheaper to maintain), it enters as +`@netscript/deploy-nitro` — an `ArtifactEmitterPort` implementation that runs a full Nitro build +as a tool — competing per target, never hosting, never touching leaf ports. Candidates would be +long-tail presets we won't hand-write (netlify, firebase, zeabur…). This is exactly the rev1 +HYBRID position, now scoped to an optional adapter package instead of the host role. + +## 4. What "enterprise-grade, implement ourselves" concretely covers + +The pieces where NetScript's quality bar is the differentiator and no wrap exists: + +1. **Capability compiler + manifests** (DP-2 §4) — build-time rejection, backend-truthful + discovery. No framework ships this; it is the credibility mechanism. +2. **Activation/rollback conventions** — health-gated symlink/dir-swap with retain history; + platform-native rollback mapped per adapter, never silently absent (A13). +3. **Secrets reference model** — references + redaction end-to-end (config, artifacts, logs, + telemetry, stream events); rotation with overlap windows (auth S9 pattern) as a follow-up + card. +4. **Deploy event stream** — versioned audit envelope on `plugin-streams-core` (DP-4 §4). +5. **The conformance suite** (DP-3 §0) — target × capability × invariant matrix in CI; the + family's acceptance instrument and the enforcement of L-1 for every future wrap-vs-own flip. From 96b6c47b701b0a6c97d9f7a2542bb739034ea7cd Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:25:54 +0200 Subject: [PATCH 05/20] =?UTF-8?q?plan(deploy-plugin):=20canonical=20DP-6..?= =?UTF-8?q?DP-8=20=E2=80=94=20migration=20map,=20contribution=20matrix,=20?= =?UTF-8?q?scaffold=20stories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DP-6: item-by-item migration of the shipped deploy layer with debt-ledger effects and the user-facing compatibility contract. DP-7: per-layer contribution matrix and the leaf-backing selection mechanism that keeps provider-first scaffolds credible. DP-8: five walked scaffold stories (deno-native default, cloudflare, aws, container/paas, vercel) with cross-story acceptance gates. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../design/canonical/DP-6-migration-map.md | 72 +++++++++++++ .../canonical/DP-7-contribution-matrix.md | 58 ++++++++++ .../design/canonical/DP-8-scaffold-stories.md | 102 ++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md new file mode 100644 index 000000000..91e42dbe1 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md @@ -0,0 +1,72 @@ +# DP-6 — Migration map: the shipped deploy layer → the plugin family + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> Source inventory with citations: `research/deploy-layer-inventory.md`. Waves: DP-1 §4. +> Governing rule: **behavior-preserving extraction first** — `deno task e2e:cli` +> (`scaffold.runtime`) green is the invariant across W1–W3; no verb or config key breaks without +> a deprecation window. + +## 1. Item-by-item map + +| # | Shipped item (owner today) | Destination | Wave | Kind | +| --- | --- | --- | --- | --- | +| M-1 | `DeployTargetPort` + `DeployOperation` (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`) | `plugin-deploy-core/ports` | W1 | Move + sharpen (capability field) | +| M-2 | Deploy target registry + `DEFAULT_DEPLOY_TARGETS` (`kernel/application/registries/deploy-target-registry.ts`, registry port) | `plugin-deploy-core/registry` | W1 | Move (closed-on-key preserved) | +| M-3 | Conventions: `activation/secrets/observability/rollback-convention.ts`, `health-gate.ts`, `runtime-overrides.ts` | `plugin-deploy-core/conventions` | W1 | Move verbatim (R-DEPLOY-3) | +| M-4 | Compile/build engine (`kernel/adapters/deploy/compile/*`, domain `compile-target.ts`, `build/build-deploy.ts` pipeline core) | `plugin-deploy-core/build` | W1 | Move; CLI keeps the presentation wrapper | +| M-5 | `DeployTargetBaseSchema` + target-map (`packages/config/src/domain/schemas/deploy-schema.ts`, types `config-section-types.ts:357-592`) | Base + schema registry → `plugin-deploy-core/config`; per-target members → their adapter packages | W1 (base) / W2 (members) | Move; retires the deploy slice of `config-plugin-specific-schema-debt`; `@netscript/config` keeps the loader seam and a re-export for one deprecation window | +| M-6 | Windows/Linux service targets, Servy/systemd adapters, `servy-config.ts`, OS-service factory | `@netscript/deploy-baremetal` | W2 | Move; compose ports publicly (closes `DEPLOY-BAREMETAL-PUBLIC-WIRING` — the 7-op set advertised at runtime) | +| M-7 | `AspireComposeDeployTarget` + `AspireCloudDeployTarget` (`kernel/adapters/aspire/*`) | `@netscript/deploy-aspire` | W2 | Move; platform-marker validation intact | +| M-8 | Deno Deploy target + CLI port + unstable-API guard (`domain/deploy/deno-deploy-*`, `kernel/adapters/deno-deploy/*`) | `@netscript/deploy-deno` | W2 | Move; transitive-scan improvement is a named follow-up card | +| M-9 | cloud-run docker lane (inside aspire-cloud adapter) | `deploy-container` (+`./cloudrun` client) | W4 | Re-home (micro-decision at W4; until then stays in `deploy-aspire` unchanged) | +| M-10 | `deploy` CLI group + all verbs (`public/features/deploy/**`) | W1–W2: same group re-wired over core/adapters; W3: plugin-contributed group + built-in shim (DP-4 §6) | W1→W3 | Rewire, then re-home | +| M-11 | Legacy flat verbs `build/install/start/stop/status/logs/upgrade/uninstall/copy/package-cli` | Aliases onto `baremetal` target ops; deprecation notice two minor releases, then removal | W2 announce / W5 remove | Deprecate (resolves S12/#348 convergence; stale "Windows Service" group description fixed in W1) | +| M-12 | Scaffold deploy emission: 3 workflow templates, `netscript.config.ts` `deploy:{}`, appsettings model | Workflow templates move to their owning adapters' scaffold assets; `deploy:{}` gains the plugin-era schema (targets added via `deploy target add`); appsettings model unchanged (it is the logical graph — L-4) | W3 | Re-home + extend | +| M-13 | Aspire scaffold bundle (`apphost.mts`, `.helpers`, aspire.config.json…) | Unchanged — `@netscript/aspire` substrate + CLI templates stay; the deploy plugin consumes, never owns, AppHost emission | — | Keep | +| M-14 | `packages/aspire` | Unchanged (composer/diagnostics substrate) | — | Keep | +| M-15 | `packages/runtime-config` | Unchanged; plugin adds the tiny `deploy` topic | W3 | Keep + extend | +| M-16 | Desktop packaging (`public/features/deploy/target/desktop/**`) | **Out of family** — remains in CLI until epic #830 (desktop graph, beta.14) re-homes it; the deploy plugin neither moves nor blocks it | — | Boundary | +| M-17 | Maintainer `test-scaffold` + e2e scaffold gates | Extended: new `scaffold.runtime` case for `plugin install deploy` (DP-4 §7) | W3 | Extend | +| M-18 | Docs (`deploy.md`, `deploy-deno-deploy.md`, `deploy-local-aspire.md`, `explanation/aspire.md`, cli-reference) | Rewritten on the plugin story per wave; the "alpha-minimal" framing replaced by the target-matrix + capability story (drift D-C1) | each wave | Refresh | + +## 2. Debt ledger effects + +| Debt entry | Effect | +| --- | --- | +| `DEPLOY-ARCHETYPE-7-CORE-SEED` | **Retired by W1** (core package exists, conventions+registry owned, F-DEPLOY-1/2 → `gated`) | +| `DEPLOY-SECRETS-ROLLBACK-CORE` | **Retired by W1/W2** (adapters delegate to core conventions) | +| `DEPLOY-BAREMETAL-PUBLIC-WIRING` | **Retired by W2** (production composition root wires ports; 7-op advertised) | +| `cli-deploy-artifacts-missing` | **Partially retired W4** (container lane emits Dockerfile/compose); Helm/K8s hand-artifacts remain delegated to `aspire publish` (unchanged claim) | +| `DEPLOY-S7-APPHOST-COMPOSE-GEN` | Superseded-in-place: the shared compose-gen primitive becomes `deploy-container` emission; the Aspire `addExecutable` constraint documented in its card | +| `cli-deploy-linux-integration-untested` | Converted into a live-probe suite cell of `deploy-baremetal` (W2) | +| `config-plugin-specific-schema-debt` (deploy slice) | **Retired by W1/W2** (schema re-homed; loader seam only in `@netscript/config`) | +| `packages/cli` AP-1 restructure (Wave-6 deploy seams) | Continued, not duplicated: W1–W3 are its named downstream execution | +| NEW debt (opened by this design) | `doctrine-06/11 A7-plugin-delivery amendment` (DP-1 §3); `deno-deploy transitive unstable scan`; `secrets rotation overlap-window` follow-up | + +## 3. Compatibility contract (what users experience) + +1. **Config keys survive**: `deploy.targets.windows|linux|docker|compose|deno-deploy|kubernetes| + azure-*|cloud-run` parse identically through W1–W3 (schema re-home is transparent; one + deprecation window for import-path movers). +2. **Verbs survive**: every documented `netscript deploy …` invocation works unchanged through + W3; legacy flat verbs warn from W2, removed after two minor releases (M-11). +3. **Scaffolded projects**: existing projects keep working without the plugin (shim path); + `plugin install deploy` is additive. New scaffolds from W3 get the plugin preinstalled with + the `deno-deploy` default target (T1 default, DP-3 §3). +4. **Aspire keeps its place**: local dev loop (`aspire start`) and the compose/k8s/azure publish + lanes are unchanged in behavior — they change owner (adapter package), not shape. +5. **CI workflows**: previously scaffolded workflow files keep functioning (they call the same + verbs); new scaffolds get per-target workflows from the adapters (OIDC-first where the + platform supports it — AWS role-assume, Deno Deploy org tokens; token-based where not — + Cloudflare). + +## 4. Sequencing risks + +- **R-M1 The extraction tail wags the CLI**: W1 touches the CLI's largest feature. Mitigation: + behavior-freeze slices, e2e:cli as the gate, no verb work mixed into move commits. +- **R-M2 Schema re-home breaks config consumers**: mitigate with a one-window re-export from + `@netscript/config` + `deps:check` sweep. +- **R-M3 Plugin-mounted CLI regressions** (W3): the shim keeps the built-in path alive; the + `scaffold.runtime` deploy case gates the mount. +- **R-M4 Desktop entanglement**: desktop subgroup shares the deploy group namespace; W1 must + split routing without moving desktop (M-16) — a dedicated slice with its own review. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md new file mode 100644 index 000000000..8813a161a --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md @@ -0,0 +1,58 @@ +# DP-7 — Contribution matrix: what the deploy plugin contributes at every layer + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> The owner's point 3 made concrete: "the plugin allows us to contribute to every layer." + +## 1. The matrix + +| Layer | Contribution (mechanism) | Cloud-agnostic core part | Provider-optimized part | +| --- | --- | --- | --- | +| **CLI** | `cli-command` axis (DP-4 §5): the `deploy` group; `deploy target add/remove`, `deploy capabilities --json`, 7-op target verbs; doctor check `deploy-target` | Verb grammar, registry routing, capability rendering | Each adapter surfaces its target key + config member; doctor validates its tool/credential prerequisites | +| **Services runtime** | None resident in v1 (OF-4). Lifecycle contract only: artifacts drive services through `ServiceShutdownCoordinator` (L-5) | Hostable build/start/stop semantics identical across targets | Entry emission differs per target (deno process, worker entry, LWA container) but never changes app code | +| **Scaffolding** | Plugin scaffolder (DP-4 §4): userland `deploy/targets.ts` leaf + per-target assets on `target add`; provider-optimized project templates (DP-8) | The leaf file, the logical graph (appsettings) untouched by target choice | wrangler.jsonc / fly.toml / `.vercel` config / workflows / OIDC snippets; leaf-backing selection (below) | +| **Config** | `deploy.*` schema (base in core, members with adapters); environments overlay; runtime-config topic `deploy` (freeze/pause flags) | Base shape + conventions sub-blocks | Target member schemas spread the base + vendor fields only (R-DEPLOY-4) | +| **Database** | None (no `.prisma`) — deploy holds no server-side state in v1; deployment history rides the stream, not a schema | — | — | +| **Streams** | `deploy-events` durable stream (versioned envelope: started/succeeded/failed/rolled-back, artifact digest, actor, trace ctx) | Envelope schema, producer, redaction | Target key + environment dimensions in every event | +| **Telemetry** | Span vocabulary for plan/up/rollback; secrets redaction at the seam | Naming + redaction rules | Provider call spans (wrangler/aspire/deno-deploy invocations) | +| **Contracts** | v1: none (no HTTP service). The machine-readable surface is the published `DeployCapabilityManifest` schema + `--json` CLI output | Manifest schema | Each adapter's authored manifest | +| **E2E / testing** | `scaffold.runtime` deploy case; the shared conformance suite (DP-3 §0) in core `./testing` + per-adapter live probes | Suite harness, in-memory target | Live-probe lanes per platform | +| **Docs** | Target-matrix reference + per-target how-tos replacing the "alpha-minimal" page (M-18) | Concepts: targets, capabilities, tiers | One how-to per adapter with its honest caveats (manifest notes rendered) | +| **Frontend (forward)** | When the `frontend` axis lands (parallel seed run): deploy status/history panel fed by `deploy-events` | Panel consumes the stream schema | Per-target status detail | + +## 2. Leaf-backing selection — how scaffolds get provider-first seams + +The deploy plugin **selects but never owns** leaf backings (R-GRAPH-4). At `target add ` +(or provider-optimized `init`), the scaffolder: + +1. Reads the project's logical graph (appsettings resources: kv, queue, db, cache…). +2. Consults the target's **backing catalog** — a declarative table shipped by the adapter naming, + for each `LeafBindingKind`, the recommended provider backing *package* (a leaf-owned adapter, + e.g. `@netscript/kv-cloudflare`) or an `external` strategy, with its capability caveats. +3. Emits config + env wiring for the chosen backings and runs the **capability compiler** so an + impossible combination fails at scaffold time, not deploy time (L-3). + +Catalog sketches (each row exists only when the leaf-owned adapter exists — otherwise the +catalog row says `external` and the scaffold wires a URL-based binding): + +| Kind | Cloudflare-optimized | AWS-optimized | Vercel-optimized | Deno-native default | +| --- | --- | --- | --- | --- | +| kv | Workers KV (no-CAS caveat → capability-scoped) or DO-SQLite (probe) | DynamoDB | Upstash Redis (marketplace) | Deno KV / Redis | +| queue | CF Queues (leaf probe card) | SQS (leaf probe card) | QStash (leaf card) | external MQ (platform has none) | +| database | D1 / Hyperdrive→Postgres | RDS/Aurora | Neon (marketplace) | Prisma Postgres / linked PG | +| cache | Workers Cache | ElastiCache | — (CDN layer) | Redis/Garnet | +| sagas | `rejected` on Workers; Containers lane or DO-store probe | `externalized`/Fargate `supported` | `externalized` | `externalized`; baremetal/aspire `supported` | + +This is the "defeats cloud-agnosticism yet makes it credible" mechanism: the **ports and the app +model never change**; the scaffold's provider-first choices are explicit, capability-checked, and +reversible (swap the backing package + config; the logical graph is untouched). + +## 3. Credibility invariants + +1. A provider-optimized scaffold **must not** fork app code per provider — only config, bindings, + emitted artifacts, and backing package selection differ (L-4 one-graph rule). +2. Every optimization is **capability-manifest-visible** — `deploy capabilities` shows exactly + what the chosen target/backings support, with notes (backend-truthful, auth-S1). +3. Anything the catalog cannot honestly back is scaffolded as `external` with a named env + contract — never a lowest-common-denominator shim (adversarial F3 honored at scaffold time). +4. Switching targets is additive: `deploy target add aws` beside `cloudflare` composes; the + compiler reports per-target verdicts independently (multi-target registry, DP-2 §6). diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md new file mode 100644 index 000000000..eeb57b0cd --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md @@ -0,0 +1,102 @@ +# DP-8 — Scaffold stories: provider-optimized projects, walked end to end + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> Each story names: the command, what lands on disk, the leaf backings chosen (DP-7 §2), the dev +> loop, the deploy path, and the honest caveats the capability manifest will render. App code is +> identical across all four stories (L-4; DP-7 §3 invariant 1). + +## Story 0 — The Deno-native default (baseline every story diffs against) + +``` +netscript init my-app # deploy plugin preinstalled from W3 +netscript deploy target add deno-deploy +``` + +- **Lands:** `deploy/targets.ts` (user-owned leaf: `defineDeployTargets({ 'deno-deploy': {...} })`), + `deploy.targets.deno-deploy` config member, `.github/workflows/deploy-deno-deploy.yml` + (`DENO_DEPLOY_TOKEN` org token), `deno.json` `deploy` section. +- **Backings:** db → Prisma Postgres or linked PG; kv → Deno KV (partial: no queues/replication + controls); queue → **external MQ env contract** (platform has none — compiler note); + cron → `Deno.cron()` lossless. +- **Dev loop:** `aspire start` unchanged (M-13). **Deploy:** `netscript deploy deno-deploy plan` + (preflight + capability verdict) → `up`. Rollback: platform instant revision routing. +- **Caveats rendered:** sagas `externalized`; queue `unsupported` in-platform; monorepo git + integration gap (local-source deploys unaffected). + +## Story 1 — Cloudflare-optimized (`netscript init my-app --deploy cloudflare`) + +- **Lands:** everything in Story 0's shape plus `wrangler.jsonc` (entry, `compatibility_date`, + bindings blocks generated from the logical graph), a generated worker entry adapting + `ServiceApp.fetch` (T2 Web-standard), `deploy/targets.ts` with the `cloudflare` member, + workflow using `cloudflare/wrangler-action` (API token + account ID secrets — no OIDC on CF + yet, scaffold README says so). +- **Backings (catalog):** kv → Workers KV via `@netscript/kv-cloudflare` *when it exists* + (capability-scoped: `kv-atomic: unsupported`, CAS-dependent consumers rejected at build); + queue → CF Queues binding declared by name, consumption gated on the leaf probe card + (`@netscript/queue-cf`); db → D1 or Hyperdrive→Postgres; static assets → Workers Static + Assets (lossless). +- **Compute split:** HTTP + stateless services → Workers; anything requiring + `long-running-process` (sagas `supported`, exclusive DB writer, owned queue `listen()` loops) + → **Containers lane** (same image as Story 3's container path) — the compiler proposes the + macro-service split rather than silently degrading (L-3, sagas law). +- **Dev loop:** `aspire start` for the graph + `wrangler dev` for worker-path fidelity, with the + documented Miniflare caveat (simulator, not oracle — remote-binding smoke advised before + first deploy; CF-PROBE evidence backs the wording). +- **Deploy:** `deploy cloudflare plan` (emit config+entry, compile verdict) → `up` + (`wrangler deploy`); rollback `wrangler versions rollback`; secrets `wrangler secret` + references. + +## Story 2 — AWS-optimized (`--deploy aws`) + +- **Lands:** Dockerfile emitted by `deploy-container` (denoland/deno base + Lambda Web Adapter + layer/binary), `deploy/targets.ts` `aws` member (region, function/service names, or + `pulumi: true` for the IaC subpath), workflow with **GitHub OIDC → AssumeRoleWithWebIdentity** + (role-trust snippet emitted; no static keys). +- **Backings:** queue → SQS **by name** (consumption via `@netscript/queue-sqs` leaf card once + proven — AWS-PROBE-EVENTS; until then `external` contract); kv → DynamoDB leaf card; + cache → ElastiCache endpoint env; db → RDS/Aurora URL. +- **Compute:** HTTP → Lambda (LWA container, Function URL/API GW); long-lived (sagas + `supported`) → Fargate service from the same image — the image portability is the point + (research §3). +- **Deploy:** `deploy aws plan` (image + optional Pulumi inline-program preview) → `up`; + rollback via Lambda versions / service revisions; secrets via SSM/Secrets Manager references. +- **Caveats rendered:** event/queue semantics not claimed until the leaf probe passes + (adversarial F1 wording in the manifest note). + +## Story 3 — Container/PaaS (`--deploy fly` | `koyeb` | `sevalla` | `coolify` | `dokploy`) + +- **Lands:** generated Dockerfile (+ compose for local parity), `deploy/targets.ts` member with + the platform client selection (self-hosted platforms: base URL + token env names), workflow + calling `deploy up`. +- **Backings:** whatever the image runs — the Deno-native defaults (Story 0's leaf set) apply + unchanged; platform add-ons (Fly Postgres/Upstash) offered as catalog rows. +- **Sagas `supported`** (long-lived containers) — this lane is the "everything just works" tier + and the recommended default for stateful apps. +- **Deploy:** `plan` (image build) → `up` (push + platform API create/deploy); rollback via + platform revision/machine swap where offered. + +## Story 4 — Vercel-optimized (`--deploy vercel`) + +- **Lands:** `.vercel/output` emission wired into `plan` (config.json, `functions/*.func` with + `.vc-config.json`, `static/`), `deploy/targets.ts` `vercel` member (`runtime: 'node' | + 'vercel-deno'` — Node default, T3 honest; `vercel-deno` opt-in with version-lag warning), + workflow with `VERCEL_TOKEN`. +- **Backings:** kv → Upstash Redis (marketplace) leaf card; queue → QStash card or `external`; + db → Neon; static assets lossless (first-class). +- **Deploy:** `deploy vercel plan` (emit output dir + verdict) → `up` + (`vercel deploy --prebuilt`); rollback via deployment aliasing. +- **Caveats rendered:** sagas `externalized`; `bounded-window` process; Deno runtime is + community-maintained when opted in. + +## Cross-story acceptance (feeds the board's scaffold issue) + +- gate: the same app source tree scaffolds and passes `deploy plan` for every story + with zero app-code diffs (only `deploy/`, config, emitted artifacts, workflows differ). +- gate: every story's first `plan` runs the capability compiler and renders the target's honest + caveats; an app requiring an `unsupported` capability fails scaffold-time selection with the + macro-service or external-binding proposal. +- gate: golden tests cover every emitted artifact per story; emitted workflows reference only + documented secrets/OIDC contracts. +- gate: `scaffold.runtime` E2E covers Story 0 end-to-end; Stories 1–4 covered by emission golden + tests + their adapter live probes (full live E2E per cloud is the adapter card's probe, not + the scaffold suite's job). From f360deca36d64c6adecf441bad21e520cc037d03 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sat, 18 Jul 2026 23:28:38 +0200 Subject: [PATCH 06/20] =?UTF-8?q?plan(deploy-plugin):=20plan=20lock=20+=20?= =?UTF-8?q?worklog/context-pack=20=E2=80=94=20generator=20stage=20complete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LD-1..LD-12 locked; owner forks OF-1..OF-8 numbered, none silently taken; wave train W1-W5 with 21-child draft board sketch; risk register, gate selection, debt effects, deferred scope, downstream attack list. Worklog carries the Design checkpoint and ends STAGE-COMPLETE: generator; context-pack primes the integration resume. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../plan-deploy-plugin--seed/context-pack.md | 59 +++++++ .llm/runs/plan-deploy-plugin--seed/plan.md | 154 ++++++++++++++++++ .llm/runs/plan-deploy-plugin--seed/worklog.md | 72 ++++++++ 3 files changed, 285 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/context-pack.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/plan.md diff --git a/.llm/runs/plan-deploy-plugin--seed/context-pack.md b/.llm/runs/plan-deploy-plugin--seed/context-pack.md new file mode 100644 index 000000000..48e10b82f --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/context-pack.md @@ -0,0 +1,59 @@ +# Context pack — plan-deploy-plugin--seed + +Resumable summary. Read `supervisor.md` first (identity, pipeline, overrides), then this. + +## State (2026-07-18) + +**Generator stage COMPLETE** (`worklog.md` ends `STAGE-COMPLETE: generator`). Branch +`plan/deploy-plugin` (worktree `/home/codex/repos/wt-deploy-plugin-seed`), baseline `290c68ef`, +all work pushed with explicit refspec. No PR, no issue/label/milestone mutation (kickoff +stop-lines). Commits: `d7879e68` bootstrap → `fed30572` research corpus → `bcec7e53` DP-0..2 → +`a178d31a` DP-3..5 → `96b6c47b` DP-6..8 → (closing commit) plan+worklog+context-pack. + +## What this run is + +Clean seed run for the **deploy plugin family** concept the owner ratified 2026-07-18 +(supersedes #824's unified-runtime board; prior run `plan-unified-runtime--seed` in wt-g8-seed is +the evidence base, distilled into `research/prior-run-distillation.md`). Modified pipeline per +`kickoff.md`: this generator → supervisor-dispatched Sol-xhigh constructive adversarial → Kimi-K3 +doc story → generator integrates. Formal PLAN-EVAL/ratification later, supervisor-decided. + +## Artifact map + +- `research/` — six cited corpus files + `research.md` synthesis (drift candidates D-C1…4). +- `design/canonical/DP-0…DP-8` — concept/goal frame; family graph (R-GRAPH-1…5); deploy-core + (7-op port, emitter split, capability compiler, binding transport); adapter cards + shared + conformance suite; plugin+host design (3 host extensions); selective-wrapping map; migration + map M-1…18; contribution matrix; scaffold stories 0–4. +- `plan.md` — LD-1…12 locked, OF-1…8 owner forks, wave train W1–W5, 21-child board sketch, risks, + gates, debt, deferred scope, downstream attack list (§10). + +## Core design in one paragraph + +Auth-parity family: thin `plugins/deploy` + `@netscript/plugin-deploy-core` (extraction of the +CLI kernel's existing 7-op port/registry/conventions — closes `DEPLOY-ARCHETYPE-7-CORE-SEED`, +flips F-DEPLOY-1/2 to gated) + A2 adapters (`deploy-aspire`, `deploy-baremetal`, `deploy-deno` +extracted; `deploy-container` with fly/koyeb/sevalla/coolify/dokploy subpaths new; cloudflare/ +vercel/aws probe-gated). Deploy owns placement/artifacts/lifecycle; leaves own semantics +(bindings transported by name — the anti-god-object cut). Capability manifests + build-time +rejection make agnosticism honest; provider-optimized scaffolds (Stories 1–4) make +provider-first credible. Goal frame = three tiers (Deno-native process / Web-standard isolate / +Node-compat declared). Nitro: reference-only with an L-1-gated re-entry. Waves: W1 core, W2 +adapters, W3 pluginization (needs the three `@netscript/plugin` host extensions), W4 container +path, W5 probe-gated clouds. + +## For the resumed generator (integration stage) + +1. Read Sol findings + Kimi doc-story outputs (locations per supervisor dispatch). +2. Triage findings with per-finding dispositions (seed-run stage-F discipline); amend DP docs in + place; extend `drift.md`. +3. Author full issue bodies from the plan §5 sketch (board-parity template: `Part of #EPIC`, + anti-scope boundary, `- [ ] gate:` predicates, `Dependencies:`/`Delivery shape:`) + filing + manifest for stage H. Still drafts-only until owner ratifies. +4. Owner forks OF-1…OF-8 go in the decision brief; do not resolve them yourself. + +## Standing constraints + +Stop-lines verbatim in `kickoff.md` (drafts only; no product code; no self-dispatched stages; +push `HEAD:plan/deploy-plugin` only; HARD STOPs on release/milestone actions). Evaluator lanes +are open-models-only; generator ≠ evaluator. diff --git a/.llm/runs/plan-deploy-plugin--seed/plan.md b/.llm/runs/plan-deploy-plugin--seed/plan.md new file mode 100644 index 000000000..d1debe204 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/plan.md @@ -0,0 +1,154 @@ +# Plan — plan-deploy-plugin--seed + +> **Draft — no GitHub mutation.** Generator-stage plan (Fable 5 · xhigh). Pending: supervisor- +> dispatched Sol-xhigh constructive adversarial pass, Kimi-K3 doc-driven story pass, generator +> integration, then (supervisor decision) formal PLAN-EVAL and owner ratification. Nothing below +> files, closes, or edits any issue/PR/milestone/label. + +## 1. Scope statement + +Design (not implement) the NetScript **deploy plugin family** — `@netscript/plugin-deploy` + +`@netscript/plugin-deploy-core` + per-cloud adapters — composed exactly like the auth plugin, +under the goal frame "Deno native first, then Node compat where needed", including the migration +of the entire shipped deploy layer, per-layer plugin contributions, provider-optimized scaffold +stories, and a selective-wrapping map. Deliverable = this run's canonical corpus +(`design/canonical/DP-0…DP-8`) + this plan; a future stage H files the board from it. + +**Archetype statement** (plan-gate requirement): composite **Archetype 7** delivered as +**A5 plugin + A2 core + A2 adapters** (DP-1 §3); scope overlays: none (framework/plugin design; +docs refresh is a mapped consequence, not an overlay-driven docs run). Current doctrine verdict +inputs and in-scope anti-patterns: `research/doctrine-constraints.md` §5/§7 (AP-3, AP-4, AP-9, +AP-11, AP-13/14/16/19, AP-22–25; F-DEPLOY-1/2 flip to `gated`). + +## 2. Locked decisions (with rationale pointers) + +| # | Decision | Where argued | +| --- | --- | --- | +| LD-1 | Family topology: `plugins/deploy` (A5) + `plugin-deploy-core` (A2) + adapters `deploy-aspire`, `deploy-baremetal`, `deploy-deno`, `deploy-container` (+ thin PaaS subpaths), probe-gated `deploy-cloudflare` / `deploy-vercel` / `deploy-aws` | DP-1 §1 | +| LD-2 | Dependency law R-GRAPH-1…5 (leafward types only; adapters→core+one provider; plugin composes; leaf backings leaf-owned; CLI = presentation) | DP-1 §2 | +| LD-3 | 7-op verb vocabulary ` ` locked; `plan` subsumes emit; legacy flat verbs = deprecated bare-metal aliases | DP-2 §2 | +| LD-4 | Capability manifest + build-time rejection compiler in core; backend-truthful; sagas tri-state | DP-2 §4 | +| LD-5 | Bindings are declarative name+kind transport; deploy never resolves leaf semantics | DP-2 §5 | +| LD-6 | Multi-target registry (auth's single-active inverted); environments overlay in config | DP-2 §6 | +| LD-7 | Wrap map: wrangler / Build Output API / `deno deploy` / Machines+PaaS REST / LWA / Pulumi Automation API; Serverless v4 rejected; Nitro reference-only with conditional re-entry | DP-5 | +| LD-8 | No resident service/contract in v1; capability manifest + CLI JSON is the machine surface | DP-4 §2 | +| LD-9 | Three named host extensions: `cli-command` axis, doctor-union widening, `contributesDeployTargets` flag | DP-4 §5 | +| LD-10 | Behavior-preserving extraction first; e2e:cli invariant; compatibility contract (config keys + verbs survive; Aspire unchanged in behavior) | DP-6 | +| LD-11 | Provider-optimized scaffolds select leaf backings via per-target catalogs; app code never forks per provider | DP-7/DP-8 | +| LD-12 | Desktop packaging out of family (epic #830 boundary) | DP-3 §8, DP-6 M-16 | + +## 3. Owner-fork sweep (numbered; none silently taken) + +| Fork | Question | Recommendation (argued at) | Alternative | +| --- | --- | --- | --- | +| OF-1 | Core package name | `@netscript/plugin-deploy-core` (auth parity; DP-1 §1) | `@netscript/deploy-core` (kickoff literal) | +| OF-2 | Thin PaaS packaging | Subpaths of `deploy-container`; graduate on SDK need (DP-1 §1) | One package per platform from day one | +| OF-3 | CLI delivery | Plugin-contributed group + built-in shim (DP-4 §6) | Group stays built-in; plugin only registers targets | +| OF-4 | v1 service surface | None; CLI+manifest machine surface; service seam deferred rework-safe (DP-4 §2) | Ship a deploy service + contracts/v1 now | +| OF-5 | Legacy verb deprecation window | Warn from W2, remove after two minor releases (DP-6 M-11) | Keep aliases indefinitely | +| OF-6 | Board/milestone relation | New `epic:deploy-plugin` umbrella **supersedes #824** (unified-runtime seed) and re-scopes #823's deploy half; W1–W3 → `0.0.1-beta.13`, W4–W5 → beta.14/stable; #825 (Aspire NuGet packaging) unaffected | Fold under #823 as-is; or all-waves one milestone | +| OF-7 | Nitro | No dependency; reference corpus + L-1-gated re-entry as optional emitter package (DP-5 §3) | Commission `deploy-nitro` emitter in first waves | +| OF-8 | AWS first-wave scope | HTTP-only until AWS-PROBE-EVENTS passes (DP-3 §7) | Commit full event surface in W5 | + +Open decisions not taken and safe to defer (plan-gate sweep): cloud-run re-home timing (W4 +micro-decision, DP-6 M-9); `--deploy ` at `init` vs post-init `target add` only (pure +UX addition, additive); deploy dashboard service (seam priced, OF-4); secrets rotation +overlap-window card (follow-up debt, DP-6 §2). None forces rework if deferred — each lands +behind an already-designed seam. + +## 4. Milestone / wave train (pending OF-6) + +``` +W1 core extraction ──► W2 adapter extraction ──► W3 pluginization ──► W4 container path ──► W5 probe-gated clouds + └────────────── 0.0.1-beta.13 ──────────────┘ └──── beta.14 / stable ────┘ +``` + +Cross-epic edges: host extensions (LD-9) precede W3; frontend-contrib seed run (parallel) is a +**consumer**, not a dependency; #830 desktop consumes `deploy-baremetal`'s compile primitives at +most; leaf backing packages (kv/queue provider adapters) are separate leaf-owned cards that W5 +scaffold catalogs reference but do not block on (catalog rows say `external` until they exist). + +## 5. Draft board sketch (stage-H input; full bodies authored at integration) + +Mirrors the enterprise-auth template with its defects fixed +(`research/board-parity-871-887.md` §4): single `DP-N` id scheme, every child milestoned, p0 +milestoned, consistent status, GitHub-native sub-issues. + +| ID | Title (draft) | Wave | Priority | Delivery shape | Depends on | +| --- | --- | --- | --- | --- | --- | +| EPIC | Epic: Deploy plugin family (`type:umbrella`, `epic:deploy-plugin`, `area:cli` `area:plugin`) | — | p1 | umbrella; epic-acceptance gates from DP-8 + conformance matrix | — | +| DP-1 | Extract `plugin-deploy-core` (ports, registry, conventions, build, config base) | W1 | **p0** | core extraction, no vendor work | — | +| DP-2 | Capability manifest + rejection compiler + conformance-suite harness | W1 | p0 | core contract, backend-truthful | DP-1 | +| DP-3 | Re-wire CLI deploy group over core; verb lock + desktop routing split | W1 | p1 | thin-router rewire | DP-1 | +| DP-4 | Extract `deploy-baremetal` (+ compose public wiring; systemd live probe) | W2 | p1 | adapter over core port | DP-1 | +| DP-5 | Extract `deploy-aspire` | W2 | p1 | adapter delegating to aspire CLI | DP-1 | +| DP-6 | Extract `deploy-deno` (+ manifest honesty rows; transitive-scan follow-up) | W2 | p1 | adapter wrapping `deno deploy` | DP-1, DP-2 | +| DP-7 | Adapter-side config members + schema registry (config debt retirement) | W2 | p1 | config re-home | DP-4–DP-6 | +| DP-8 | Host extensions: `cli-command` axis, doctor union, capability flag | W2 | p1 | `@netscript/plugin` core change | — | +| DP-9 | `plugins/deploy`: manifest triad, composition root, doctor, verify-plugin | W3 | p1 | thin A5 plugin | DP-2, DP-8 | +| DP-10 | Plugin CLI mount + shim; `target add/remove`, `capabilities --json` | W3 | p1 | plugin CLI contribution | DP-3, DP-9 | +| DP-11 | Scaffolder: `deploy/targets.ts` leaf + per-target assets + Story-0 e2e | W3 | p1 | plugin scaffold contribution | DP-9 | +| DP-12 | `deploy-events` stream + telemetry + runtime-config topic | W3 | p2 | plugin contributions | DP-9 | +| DP-13 | `deploy-container` core path + fly/koyeb/sevalla/coolify/dokploy clients | W4 | p1 | new adapter + subpath clients | DP-2 | +| DP-14 | Container scaffold story (Story 3) + generated Dockerfile golden tests | W4 | p2 | scaffold assets | DP-11, DP-13 | +| DP-15 | CF-PROBE (live Workers conformance, Miniflare fidelity, token story) | W5 | p1 | probe card, findings-only | DP-2 | +| DP-16 | `deploy-cloudflare` adapter + Story 1 scaffold | W5 | p1 | probe-gated adapter | DP-15 | +| DP-17 | Vercel probe + `deploy-vercel` + Story 4 | W5 | p1 | probe-gated adapter | DP-2 | +| DP-18 | AWS-PROBE-HTTP + `deploy-aws` (HTTP scope) + Story 2 | W5 | p1 | probe-gated adapter, HTTP-only | DP-13 | +| DP-19 | Docs refresh: target-matrix reference + per-target how-tos (per wave) | W1–W5 | p1 | docs consolidation | rolling | +| DP-20 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation | backlog | p2 | RFC-first, leaf co-owned | DP-18 | + +(≈21 children; each future body: `Part of #EPIC` → scoping paragraph with anti-scope boundary → +`- [ ] gate:` acceptance → `Dependencies:` + `Delivery shape:` — per board-parity conventions.) + +## 6. Risk register + +| Risk | Sev | Mitigation | +| --- | --- | --- | +| Extraction destabilizes the CLI's largest feature | high | Behavior-freeze slices; e2e:cli gate per slice; desktop split isolated (DP-6 R-M1/R-M4) | +| Capability vocabulary designed wrong (too coarse/fine) | high | Vocabulary derived from shipped port set + prior-run L/P/U grid; reviewed in Sol pass; versioned schema so rows can be added without breaking | +| Host-axis creep (`cli-command` opens a contribution firehose) | med | Closed collision rules; one axis, typed registry; doctrine 11 promotion test governs future axes | +| Probe failures strand W5 adapters | med | Probes are findings-first cards; adapters ship only behind passing probes (L-7); W1–W4 value is independent of W5 | +| Provider surface drift (wrangler/Vercel/Deno Deploy move fast) | med | Wrap at CLI process boundaries; compatibility dates pinned in emitted configs; manifest notes carry dates; conformance suite re-run per upgrade | +| Live-probe CI cost/credentials | med | Probe lanes are manually-triggered/secret-gated workflows, not default CI; in-memory suite covers default CI | +| JSR surface growth (6+ new packages) | med | OF-2 subpath folding; F-5 export budgets; publish dry-run + jsr-audit per package before any release wave | +| Leaf-backing dependency (catalog rows need leaf adapters that don't exist) | med | Catalog rows degrade to `external` honestly (DP-7 §2); leaf cards tracked separately (DP-20) | +| Monorepo gap on Deno Deploy git integration | low | Local-source deploys unaffected; noted in manifest; revisit on platform changelog | + +## 7. Gates (selection from `archetype-gate-matrix.md`) + +Per DP-1 §3: A5 set for the plugin (+R-PLUGIN-PARITY checklist), full A2 set for core and every +adapter, A7 union + F-DEPLOY-1/2 flipped to `gated` at W1, plus: `quality:scan`, `arch:check`, +scoped check/lint/fmt wrappers, `deno doc --lint`, publish dry-run, jsr-audit per package, +`scaffold.runtime` e2e (Story 0) at W3, conformance suite matrix from W2 on. + +**jsr-audit on planned surfaces** (plan-gate item 8): no oRPC contract in core or plugin v1 ⇒ no +`--allow-slow-types` exception anywhere in the family (auth's sanctioned exception is *not* +inherited); adapters keep vendor CLIs at process boundaries so no vendor types can leak into +public signatures (AP-14 guarded by F-15); export budgets per subpath ≤ 20 (F-5); string-constant +templates for all emitted artifacts (JSR text-import doctrine). + +## 8. Debt implications + +Retires: `DEPLOY-ARCHETYPE-7-CORE-SEED`, `DEPLOY-SECRETS-ROLLBACK-CORE`, +`DEPLOY-BAREMETAL-PUBLIC-WIRING`, deploy slice of `config-plugin-specific-schema-debt`, +container half of `cli-deploy-artifacts-missing`, `cli-deploy-linux-integration-untested` +(as probe cell). Opens: doctrine 06/11 A7-plugin-delivery amendment; deno-deploy transitive +unstable scan; secrets rotation overlap-window. Details: DP-6 §2. + +## 9. Deferred scope (explicit) + +Frontend contribution axis (parallel seed run `plan/frontend-contrib`); leaf provider backing +packages (`kv-cloudflare`, `queue-sqs`, DO saga store — leaf-owned cards); desktop packaging +(#830); Netlify/Railway/Render/GKE adapters (open registry, community path); Nitro emitter +(OF-7 conditional); deploy dashboard service (OF-4 seam); secrets rotation overlap-window; +AWS event semantics (DP-20). + +## 10. What downstream passes should attack + +For the Sol adversarial (constructive) pass: the capability vocabulary granularity (§6 risk 2); +the `cli-command` axis design and its collision rules; whether `plan`-subsumes-`emit` survives +real artifact workflows; the W1 slice ordering against the CLI's actual file graph; the honesty +of every manifest sketch in DP-3. For the Kimi doc-story pass: write the public docs as if W3 +shipped (install → target add → plan/up per story) and surface every DX seam the docs cannot +explain — those are design bugs. diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 4d3d41fc6..9ffd2c922 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -10,3 +10,75 @@ kickoff stop-line) and D-2 (custom downstream pipeline) recorded. - Stop-lines in force: drafts only; no GitHub mutations; no product code; no self-dispatched downstream stages; push only `HEAD:plan/deploy-plugin`. +- Commit `d7879e68` (pushed). + +## 2026-07-18 — Stage B: discovery corpus + +Six-surface fan-out (read-only Opus 4.8 sub-agents; supervisor read the two pivotal prior-run +adversarial docs first-hand). Committed under `research/` with citations: + +- `prior-run-distillation.md` — full `plan-unified-runtime--seed` corpus: UR-0…12 + reusable-vs-dead map, nitro v3 facts, adapter-limit findings, Deno Deploy (new) facts, market + landscape, sagas constraint, both adversarial rounds, evidence freshness. +- `auth-composition-anatomy.md` — the composition precedent: topology, manifest triad, ports, + adapter thinness, layer contributions, install path, what does/doesn't generalize. +- `deploy-layer-inventory.md` — every shipped deploy surface (CLI verbs, aspire substrate, + config schema, scaffold artifacts, docs promises, gaps, ownership map). +- `doctrine-constraints.md` — binding axioms, A2/A5/A6/A7 fit, F-DEPLOY gates, plugin-host + contribution mechanics (incl. missing cli-command/frontend axes), live debt entries, + plan-gate essence. +- `board-parity-871-887.md` — enterprise-auth board template + 18 mirror/fix lessons. +- `provider-deploy-surfaces.md` — live wrap surfaces for CF/Vercel/AWS/Fly/Deno Deploy/thin + PaaS/Nitro-v3 + wrap-vs-implement table (primary sources, URLs). +- `research.md` — synthesis + drift-candidate ledger (D-C1…D-C4). +- Commit `fed30572` (pushed). + +## 2026-07-18 — Stages C–E: synthesis, canonical design corpus, plan lock + +## Design + +Canonical corpus `design/canonical/` (all drafts, no GitHub mutation): + +- **DP-0 concept & goal frame** — owner ratification operationalized; carried-forward laws + L-1…L-7; the three-tier "Deno native first" frame; credibility positioning; design principles. +- **DP-1 family architecture** — package topology (plugin / plugin-deploy-core / deploy-aspire / + deploy-baremetal / deploy-deno / deploy-container(+PaaS subpaths) / probe-gated + cloudflare-vercel-aws); the explicit dependency graph R-GRAPH-1…5 answering adversarial F5; + archetype+gate mapping; waves W1–W5. +- **DP-2 deploy-core** — subpath surface; 7-op port moved+sharpened (verb lock, declared + subsets, unsupported-op errors); `ArtifactEmitterPort` split; capability manifest + build-time + rejection compiler; declarative binding transport; conventions/registry/config re-home. +- **DP-3 adapter cards** — shared conformance suite; per-target cards with wrap surface, ops, + manifest sketch, scaffold hooks, permissions, probe gates; explicit non-cards. +- **DP-4 plugin & host** — plugins/deploy A5 shape; manifest triad re-derived (no v1 service); + multi-target install model (`target add`); three named host extensions; CLI shim + new verbs; + plugin Concept-of-Done. +- **DP-5 selective wrapping** — decision map (aspire-native, wrap targets, own-enterprise-grade + list); Serverless v4 rejected; Nitro reference-only + L-1-gated re-entry. +- **DP-6 migration map** — M-1…M-18 item map, debt ledger effects, user compatibility contract, + sequencing risks. +- **DP-7 contribution matrix** — per-layer contributions; leaf-backing catalog mechanism; + credibility invariants. +- **DP-8 scaffold stories** — Stories 0–4 walked end-to-end with cross-story acceptance gates. +- **plan.md** — LD-1…LD-12 locked; owner forks OF-1…OF-8 (none silently taken) + safe-deferral + sweep; wave/milestone train (pending OF-6); 21-child draft board sketch with dependencies and + delivery shapes; risk register; gate selection + jsr-audit statement; debt implications; + deferred scope; downstream-pass attack list. +- Commits `bcec7e53` (DP-0..2), `a178d31a` (DP-3..5), `96b6c47b` (DP-6..8), plus this closing + commit (plan.md, worklog, context-pack) — all pushed to `plan/deploy-plugin`. + +## Gate log + +Planning-only run: no code gates executed (no `packages/`/`plugins/` change; stop-line). Evidence +gates applied instead: every research claim cited (stage-B citation gate); board sketch follows +`netscript-pr` conventions as drafts; plan-gate items covered in `plan.md` §1–§9 for the future +PLAN-EVAL. + +## Handoff state + +Per kickoff pipeline: next stages are supervisor-dispatched — (1) GPT Sol xhigh constructive +adversarial pass over this corpus (attack list in `plan.md` §10), (2) Kimi K3 doc-driven story +pass forecasting public docs, then (3) this generator resumes to integrate. No downstream stage +was dispatched from this session. + +STAGE-COMPLETE: generator From 7facbd053ba1d1c1dc21b6020e15b1b16df680c8 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 05:57:24 +0200 Subject: [PATCH 07/20] plan(deploy-plugin): stage-2 adversarial brief (Sol xhigh, constructive) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../adversarial-brief.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-brief.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-brief.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-brief.md new file mode 100644 index 000000000..b480654ba --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-brief.md @@ -0,0 +1,82 @@ +use harness + +# Brief — Constructive adversarial pass over the deploy-plugin seed corpus (Sol · xhigh) + +You are the **stage-2 constructive/collaborative adversarial reviewer** of the harness run +`plan-deploy-plugin--seed` (branch `plan/deploy-plugin`, this worktree). The generator (Claude +Fable 5 · xhigh) produced a complete seed corpus for the NetScript **deploy plugin family**. Your +role is to **ENHANCE that output, not ruin it**: find real defects, weak spots, and missed +opportunities, and for each one propose a concrete, adoptable amendment. You are the +`review_claude` lane (Codex · GPT-5.6 Sol · xhigh) of `.llm/harness/workflow/lane-policy.md`. + +## SKILL + +Read and follow, in order: + +1. `.agents/skills/netscript-harness/SKILL.md` — run mechanics, artifact map, evaluator separation. +2. `.agents/skills/netscript-doctrine/SKILL.md` — pointers into `docs/architecture/doctrine/` + (you will judge archetype fit, thinness, anti-patterns, gates). +3. `.agents/skills/netscript-tools/SKILL.md` — only if you run validation tooling; prefix + read-heavy git commands with `rtk`. + +## Read order (the corpus under review) + +All under `.llm/runs/plan-deploy-plugin--seed/`: + +1. `kickoff.md` (the owner's ratified concept — the design must serve it) +2. `supervisor.md`, `drift.md` +3. `research.md`, then `research/*.md` (six cited corpus files) +4. `design/canonical/DP-0…DP-8` (the design under review) +5. `plan.md` (locked decisions LD-1…12, owner forks OF-1…8, board sketch, §10 attack list) + +Spot-verify load-bearing repo claims first-hand (`deno doc`, direct file reads): the shipped +7-op port (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`), the deploy conventions +(`packages/cli/src/kernel/domain/deploy/`), the auth composition pattern (`plugins/auth`, +`packages/plugin-auth-core`), the plugin host contribution types +(`packages/plugin/src/config/domain/plugin-contributions.ts`, `src/domain/constants.ts`), and the +config deploy schema (`packages/config/src/domain/schemas/deploy-schema.ts`). A corpus claim you +verify false is a finding. + +## Attack surface (start here, then go wherever the corpus is weakest) + +From `plan.md` §10 plus supervisor additions: + +- **Capability vocabulary granularity** (DP-2 §4): is the closed `DeployCapabilityId` set the + right cut? What breaks first — too coarse (verdicts lie) or too fine (manifest rot)? +- **`cli-command` contribution axis** (DP-4 §5): design soundness, collision rules, registry + emission, the shim interplay (DP-4 §6). Is the three-extension host change minimal and safe? +- **`plan` subsumes `emit`** (DP-2 §2): does that survive real artifact workflows (CI split + build-vs-deploy, `--prebuilt` flows, image push timing)? +- **W1 slice ordering vs the actual CLI file graph** (DP-6, plan §5): is the extraction sequence + real — check the desktop-subgroup entanglement (M-16/R-M4) against the actual + `public/features/deploy/` tree. +- **Manifest honesty in DP-3**: attack every capability sketch row; any row the cited provider + evidence does not support is a finding. +- **The dependency law R-GRAPH-1…5** (DP-1 §2): can you construct a cycle or a god-object path + the rules fail to forbid? Is the `deploy-container` shared-port exception sound? +- **Migration compatibility contract** (DP-6 §3): find the user-visible break the map misses. +- **Board sketch** (plan §5): wrong dependencies, missing children, mis-sized slices (<30 total). +- **Owner forks OF-1…8**: any recommendation you would flip, with grounds. +- Anything else: naming, JSR surface, gate selection, scaffold stories realism (DP-8). + +## Output contract (strict) + +1. Write your findings to `.llm/runs/plan-deploy-plugin--seed/adversarial-sol.md`: + - H1 + one-paragraph overall verdict (is this corpus sound to build on?). + - Findings numbered `SF-1…SF-n`, each: `[BLOCKER|MAJOR|MINOR|ENHANCE]` severity, one-line + claim, evidence (corpus refs `DP-N §x` and/or repo `path:line`), and a **concrete suggested + amendment** (exact replacement wording or design change — adoptable as-is). + - A final `## Quick wins` list: small improvements not worth a numbered finding. +2. Commit ONLY that file (message: + `plan(deploy-plugin): Sol xhigh constructive adversarial findings`) and push with the explicit + refspec `git push origin HEAD:plan/deploy-plugin`. +3. End your final response with exactly `DONE` on its own line (or `BLOCKED: `). + +## Stop-lines (verbatim, in force — same as the run's kickoff) + +- Findings only: do NOT edit the DP docs, plan.md, research files, or any other run artifact — + the supervisor triages and amends. +- No GitHub issues, PRs, labels, or milestones may be created or changed. +- No product code changes: no `packages/` or `plugins/` source edits (reading is required). +- Do not dispatch any other agent or eval. +- Commit to this branch only; push only `HEAD:plan/deploy-plugin`. From 9ed2eeab6ce80fe98b7982e39cf95f1869461fc4 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 06:20:45 +0200 Subject: [PATCH 08/20] plan(deploy-plugin): Sol xhigh constructive adversarial findings --- .../adversarial-sol.md | 463 ++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-sol.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-sol.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol.md new file mode 100644 index 000000000..9571b641a --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol.md @@ -0,0 +1,463 @@ +# Sol xhigh constructive adversarial review — deploy-plugin seed corpus + +The corpus is a strong design baseline and is worth building on: the A5 + A2 + A2 family shape, +wrap-first provider strategy, explicit migration map, and insistence on capability honesty are the +right center of gravity. It is not safe to implement unchanged. Five load-bearing seams are +underspecified or internally contradictory—the ownership of default target composition, the W1 +extraction boundary, the installer manifest, CLI bootstrap, and config bootstrap—and several +advertised compatibility and capability claims do not survive the shipped file graph. The amendments +below preserve the concept while making the dependency graph acyclic, the extension mechanisms +generic, and the migration/probe gates capable of proving what the corpus promises. + +## SF-1 — [BLOCKER] `DEFAULT_DEPLOY_TARGETS` cannot move into core without creating the dependency inversion R-GRAPH-2/3 forbid + +**Evidence.** DP-1 §2 says adapters depend on core and only plugin/project composition roots may +assemble adapters; DP-2 §1 and DP-6 M-2 nevertheless relocate `DEFAULT_DEPLOY_TARGETS` into +`plugin-deploy-core`. The shipped constant is not neutral data: it imports and instantiates Windows, +Linux, Aspire, Deno Deploy, and `DenoProcess` implementations +(`packages/cli/src/kernel/application/registries/deploy-target-registry.ts:4-9`, `:14-68`, +`:70-83`). Moving that constant yields `core -> adapters -> core`, or forces provider +implementations into the core. + +**Suggested amendment.** Replace DP-2 §1's registry row and DP-6 M-2 with: + +> `plugin-deploy-core/registry` owns only the empty duplicate-rejecting registry implementation, +> registry port, key/error types, and `createDeployTargetRegistry(entries = [])`. Each adapter +> exports a target factory/descriptor. `DEFAULT_DEPLOY_TARGETS` is deleted as a core concept; during +> W1 its compatibility equivalent remains in the CLI composition root, and from W2 onward the plugin +> or generated project registry supplies the ordered entries. Core never imports an adapter. + +Make the W1 debt-retirement statement conditional on this externalized composition root being in +place; do not claim the current default constant moved verbatim. + +## SF-2 — [BLOCKER] W1 is not a behavior-free move against the actual CLI file graph + +**Evidence.** DP-2's opening and DP-6 M-3/M-4 describe existing target-agnostic modules moving +verbatim. The current build use case imports CLI `ResolvedConfig` and CLI pipeline abstractions and +calls itself a Windows artifact flow +(`packages/cli/src/public/features/deploy/build/build-deploy.ts:1-7`, `:63-87`). Preparation +performs `Deno.mkdir`, imports Windows manifest sorting, and consumes the CLI config model +(`packages/cli/src/public/features/deploy/build/prepare-deploy-build.ts:15-20`, `:59-81`). The +compile runner imports CLI output, Windows constants, and Windows V8 profiles +(`packages/cli/src/kernel/adapters/deploy/compile/compile-runner.ts:1-17`). `runtime-overrides.ts` +duplicates leaf job/saga/task vocabulary and explicitly describes `.deploy/windows` +(`packages/cli/src/kernel/domain/deploy/runtime-overrides.ts:1-7`, `:67-77`), so it is not a shared +deploy convention. The same public group directly mounts desktop beside every deploy verb +(`packages/cli/src/public/features/deploy/deploy-group.ts:31-97`); plan §5 currently combines the +desktop split, verb lock, and router rewire in one child. + +**Suggested amendment.** Replace W1 with the following ordered, independently gated slices: + +1. Move only port/result/error contracts behind compatibility re-exports. +2. Add an empty core registry while leaving concrete default factories in the CLI composition root. +3. Extract a host-owned `deploy` shell that preserves the `desktop` subgroup and existing help; + lifecycle children remain unchanged. +4. Move demonstrably pure convention policies _with their constants_; leave `runtime-overrides.ts` + in the bare-metal/leaf owners. +5. Rewire the router through the contracts with no build implementation move. +6. In W2, move the current Windows/Linux build behavior to `deploy-baremetal`; only after + filesystem/process/output/config ports exist may an adapter-neutral compile emitter graduate to + core. + +Change DP-6 M-4 from “Move” to “Refactor then extract”; keep the full CLI E2E invariant on every +slice rather than on one large W1 issue. + +## SF-3 — [BLOCKER] The DP-4 installer manifest sketch is invalid under the protocol it claims to extend + +**Evidence.** DP-4 §3 omits required `version`, `displayName`, `description`, and +`peerDependencies`; it represents `requiredPermissions` as booleans although the protocol requires +string arrays (`packages/plugin/src/protocol/manifest.ts:6-14`, `:112-136`, `:138-160`). Its +`provider` omits nearly every required service-oriented field (`manifest.ts:44-80`), and its +`officialSource` omits required service entrypoint/config/port fields (`manifest.ts:82-109`). The +first-party auth manifest demonstrates the actual complete shape +(`plugins/auth/scaffold.plugin.json:1-66`). DP-4 §5 lists only a capability flag as the installer +protocol change, so no listed slice makes the sample parseable. + +**Suggested amendment.** Replace DP-4 §3 with a complete manifest and explicitly generalize the +source protocol for tooling-only plugins: + +```ts +type PluginManifestOfficialSource = + | ExistingServiceOfficialSource + | { + readonly sourceKind: 'tooling'; + readonly canonicalName: string; + readonly pluginDir?: string; + }; +``` + +The deploy manifest must include all required top-level metadata, use permission arrays such as +`read: ['']`, omit the service-shaped `provider`, and declare +`officialSource: { sourceKind: 'tooling', canonicalName: 'deploy', pluginDir: 'deploy' }`. Add +schema parse, official copy/install, and backward-compat fixtures to the host-extension slice. If +the protocol version is bumped, accept v1 service manifests and the new tooling variant +concurrently. + +## SF-4 — [BLOCKER] `cli-command` is named as an axis but has no viable discovery-to-startup path or safe deploy collision model + +**Evidence.** DP-4 §5 says the generic registry emitter renders a CLI mount and the CLI mounts it at +startup. Today the emitter only groups already-extracted source contributions into generated maps +(`packages/plugin/src/sdk/discovery/registry-emitter.ts:7-18`, `:35-67`), while its extractor only +recognizes `defineJob`, `defineSaga`, and `defineWebhook` +(`packages/plugin/src/sdk/discovery/ast-extractor.ts:4-8`, `:25-34`). Manifest resolution is +asynchronous and occurs after config loading +(`packages/cli/src/public/features/plugins/host/plugin-loader.ts:78-99`), but the public command +registry and command tree are built synchronously with `deploy` already registered +(`packages/cli/src/public/features/root/public-command-tree.ts:47-62`, `:114-124`; +`packages/cli/src/public/composition/cli-command-registry.ts:60-74`). `verifyPlugin` has no CLI or +doctor expectation surface (`packages/plugin/src/diagnostics/verify-plugin.ts:197-224`). The +proposed special exception allowing the deploy contribution to shadow the deploy shim makes a +generic host facility aware of one plugin and leaves ownership/help/error behavior ambiguous. + +**Suggested amendment.** Flip OF-3 to a host-owned mount point with plugin-owned children: + +> The built-in `deploy` shell remains reserved and owns `desktop`, the absent-plugin install hint, +> and shared help. A contribution declares `{ mount: 'deploy', id, loader, export }`; it never +> shadows a top-level command. CLI bootstrap becomes async: load manifests, validate safe loader +> subpaths, register built-ins, attach contributed children, then call `program()`. Duplicate +> `(mount,id)` pairs fail before parsing and report both plugin owners. No plugin-specific collision +> exception exists. + +Manifest loader resolution—not the AST source walker—must feed this registry. Add explicit slices +for the contribution type/builder/merger/verifier, async bootstrap, loader failure isolation, +duplicate/reserved-name rules, help rendering, and plugin-absent behavior. DP-4 §7's +`verify-plugin.ts` gate must be backed by new `PluginExpectations.cliCommands` and +`PluginExpectations.doctorChecks` checks. + +## SF-5 — [MAJOR] `plan` cannot simultaneously mean pure preview and artifact emission + +**Evidence.** DP-2 §2 calls `plan` “dry-run + artifact emission” and removes `emit`, while DP-2 §3 +adds a separate `ArtifactEmitterPort`. The shipped port has distinct `plan` and `emit` operations +(`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts:12-20`, `:106-110`). Their current +semantics already diverge: Deno Deploy `plan` is non-mutating preflight +(`packages/cli/src/kernel/domain/deploy/deno-deploy-target.ts:75-87`), whereas Aspire Compose `plan` +runs publish and writes artifacts, with a separate emit alias +(`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:90-98`). DP-3 §6 and DP-8 +Story 4 depend on a materialized `.vercel/output` consumed by `vercel deploy --prebuilt`; OCI and CI +pipelines likewise need build/attest/push/deploy to be separable. + +**Suggested amendment.** Replace LD-3 and DP-2 §2 with an eight-operation lifecycle: + +> `plan` is pure with respect to artifact directories and providers: it resolves topology, validates +> capabilities, and returns a serializable `DeploymentPlan`. `emit` materializes an +> `EmittedArtifactManifest` and content-addressed artifacts but does not deploy or push unless the +> artifact format explicitly models a publish phase. `up --prebuilt ` consumes an existing +> artifact; plain `up` may offer the convenience composition `plan -> emit -> up`. + +Record artifact digest, source revision, target variant, emitter version, and provenance so a CI +build job can hand the exact artifact to a later deploy job. Retain `ArtifactEmitterPort`; do not +hide its mutation behind a nominal dry run. + +## SF-6 — [MAJOR] The closed flat capability vocabulary couples core to every leaf and mixes three different dimensions + +**Evidence.** DP-2 §4 says `DeployCapabilityId` is closed in core while definitions and ID types +live in leaf packages; core must therefore import each participating leaf merely to assemble its +closed union. The examples mix runtime traits (`long-running-process`, `websocket`), leaf semantic +guarantees (`kv-atomic`, `queue-consume`), and topology/workload constraints (`exclusive-db-writer`, +`offline-sync`). DP-2 §5 then puts the same IDs on opaque leaf bindings, despite saying core never +interprets leaf semantics. This cut grows a deploy-core edit/import for each new leaf feature and +cannot say whether a verdict describes the platform, the installed adapter, a backing, or one target +mode. + +**Suggested amendment.** Replace the flat closed union with three explicit structures: + +```ts +interface CapabilityRef { + readonly namespace: string; + readonly name: string; + readonly major: number; +} +interface BindingRequirement { + readonly binding: string; + readonly capability: CapabilityRef; +} +interface WorkloadConstraint { + readonly kind: 'singleton' | 'long-running' | 'co-locate' | 'offline'; +} +``` + +Core owns only these structural types and a well-known runtime-trait vocabulary; leaf packages +export namespaced requirements and conformance descriptors without core importing them. A verdict +must carry `scope: 'runtime' | 'adapter' | 'binding'`, `level`, and evidence. Versioned namespaced +references allow leaf growth without manifest rot, while the small closed runtime vocabulary keeps +provider manifests comparable. Amend R-GRAPH-1 to “core imports no leaf package; leaves contribute +descriptors through structural contracts.” + +## SF-7 — [MAJOR] Several DP-3 manifest rows are unsupported, mode-collapsed, or contradicted by repository evidence + +**Evidence.** DP-3 §1 labels Aspire `queue-consume: lossless` from the existence of in-process +listeners, which proves process liveness but not queue delivery/ack/redelivery semantics; it also +changes `exclusive-db-writer` by replica count without defining a target variant. DP-3 §2 says +bare-metal is “everything lossless that the host machine provides,” which is neither an enumerable +manifest nor conformance evidence. DP-3 §3 calls Deno Deploy `kv-atomic: partial` because queues and +replication controls are absent, but those are separate capabilities; NetScript's Deno KV adapter +implements version checks and atomic commit directly +(`packages/kv/adapters/deno-kv.adapter.ts:155-194`). DP-3 §4 says container capabilities are +“inherited” without specifying the input manifest or composition rule. DP-3 §§5/7 put Workers and +Containers, or Lambda and Fargate, in one card even though their process/saga verdicts are +opposites. `lossless | partial | unsupported` also forces unproven/not-installed cases to masquerade +as provider impossibility. + +**Suggested amendment.** Make manifests target-variant and composed: + +```ts +interface CapabilityVerdict { + readonly level: 'lossless' | 'partial' | 'unsupported' | 'unverified'; + readonly scope: 'runtime' | 'adapter' | 'binding'; + readonly evidence?: string; + readonly note?: string; +} +interface DeployCapabilityManifest { + readonly target: string; + readonly variant: string; // e.g. workers, containers, lambda, fargate, compose, kubernetes +} +``` + +Runtime manifests declare only runtime evidence; installed leaf backing manifests supply queue/KV +semantics; the compiler composes them. Remove Aspire's queue and exclusive-writer claims until the +backing/replica conformance cell is present. Replace bare-metal's blanket sentence with explicit +runtime rows. Judge Deno KV atomicity by the atomic conformance suite, not by unrelated queue or +replication gaps. A `lossless` result requires a live-platform cell; an in-memory fake can validate +the harness but cannot certify the provider. + +## SF-8 — [MAJOR] The scaffold stories promise macro-service decomposition that no designed contract or board slice performs + +**Evidence.** DP-8 Stories 1 and 2 split one graph between Workers/Containers and Lambda/Fargate, +and the cross-story gate says the compiler proposes a macro-service/external-binding solution. DP-2 +§4's compiler only compares app requirements with one manifest and returns fail/warn; neither +`DeploymentPlan`, `ResourceBinding`, nor the board defines cells, selectors, cross-cell transport, +ownership of schedules/consumers, or a deterministic partition algorithm. One adapter-level manifest +cannot describe the mixed-compute result. + +**Suggested amendment.** Add a pre-W5 topology contract and issue: + +```ts +interface DeploymentCell { + readonly id: string; + readonly selectors: readonly string[]; + readonly target: string; + readonly variant: string; + readonly bindings: readonly string[]; +} +interface DeploymentTopologyPlan { + readonly cells: readonly DeploymentCell[]; + readonly transports: readonly CrossCellTransport[]; +} +``` + +For v1, require cells to be user-declared in `deploy/targets.ts`; the compiler may return +machine-readable `suggestedCells`, but must reject rather than silently partition. Add gates for +single ownership of every service/consumer/schedule, explicit cross-cell transport, and stable plan +output. Until that slice lands, narrow the Cloudflare/AWS stories to one compute variant per target +and describe the other as a manual second target. + +## SF-9 — [MAJOR] The compatibility map aliases commands whose observable semantics are not equivalent + +**Evidence.** DP-6 M-11 maps `start`, `stop`, `upgrade`, `copy`, and `package-cli` to the seven-op +bare-metal surface even though DP-2 §2 only names aliases for build/install/uninstall. In the +shipped port `up` means install + enable + start and `down` means stop + disable + uninstall +(`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts:110-113`). The existing `start` and +`stop` commands operate on selected already-registered services without installing/uninstalling +(`packages/cli/src/public/features/deploy/start/start-deploy-command.ts:7-14`, `:88-112`; +`packages/cli/src/public/features/deploy/stop/stop-deploy-command.ts:7-13`, `:86-108`). `copy` +deliberately syncs prebuilt artifacts without registration +(`packages/cli/src/public/features/deploy/copy/copy-deploy-command.ts:7-21`); `upgrade` is a +flag-sensitive five-step transaction +(`packages/cli/src/public/features/deploy/upgrade/upgrade-deploy-command.ts:32-75`, `:133-180`); +`package-cli` produces a self-shippable operator binary +(`packages/cli/src/public/features/deploy/package-cli/package-cli-deploy-command.ts:5-25`). + +**Suggested amendment.** Flip OF-5 and replace M-11 with: + +> Legacy flat commands remain first-class compatibility handlers owned by `deploy-baremetal` through +> the next semver-major release. Only `build -> plan + emit`, `status`, and `logs` may be direct +> aliases. `install`, `start`, `stop`, `copy`, `upgrade`, `package-cli`, and `uninstall` retain +> their current flags and side-effect boundaries behind a `BaremetalCompatibilityCommands` adapter. +> Help may deprecate them, but no minor-release removal date is claimed until an equivalent +> canonical workflow and migration telemetry exist. + +Add golden help/exit-code tests and state-transition tests proving that `stop` never uninstalls and +`start` never registers a service. + +## SF-10 — [BLOCKER] The target schema registry has a config/plugin bootstrap cycle and would silently erase new target config + +**Evidence.** DP-2 §6 and DP-6 M-5 move vendor schemas to adapters and compose them “at load time.” +Today the full config parses `deploy` through a static schema before exposing the `plugins` list +(`packages/config/src/domain/schemas/netscript-config-schema.ts:153-159`); unknown target keys are +stripped by design and tested as such +(`packages/config/tests/schema/netscript_config_test.ts:110-145`). Only after that parsed config +exists does the plugin host resolve manifests and contributions +(`packages/cli/src/public/features/plugins/host/plugin-loader.ts:78-99`). Thus an adapter schema +cannot be discovered before its config has already been discarded. A one-window re-export does not +solve the ordering or preserve `DeployConfig` inference for existing consumers. + +**Suggested amendment.** Define an explicit two-phase loader: + +1. Bootstrap-parse only project identity, `plugins`, and `deploy.targets` as + `Record` without stripping values. +2. Resolve plugin and generated adapter descriptors, including schema loaders. +3. Compose the final target schema registry and parse the entire config. +4. Reject an unrecognized target with `DeployTargetAdapterMissingError`; never silently drop it. + +Keep the legacy target types/schemas exported from `@netscript/config` for the compatibility window, +implemented as delegating aliases or a frozen legacy union. Add tests for installed custom target, +missing adapter, malformed adapter config, plugin-loader failure, and all existing target keys. Make +DP-7 depend on the two-phase loader slice rather than treating the loader seam as already present. + +## SF-11 — [MAJOR] The `deploy-container` exception is an unnecessary adapter-to-adapter dependency + +**Evidence.** R-GRAPH-2 says adapters depend only on core plus one provider, then exempts Cloudflare +and AWS so they may import `deploy-container`; DP-2 §3 simultaneously defines `ContainerBuildPort` +in core. Calling the exported implementation “itself a core-port implementation” does not make an +adapter package part of core. The exception permits container auth, process, registry, and platform +client dependencies to leak transitively into otherwise independent adapters and establishes the +precedent R-GRAPH-2 was meant to forbid. + +**Suggested amendment.** Delete the exception. `deploy-cloudflare` and `deploy-aws` accept a +core-owned `ContainerBuildPort` in their constructors/factories. `deploy-container` exports an +implementation/factory; the plugin or generated project registry injects it. Add an import-graph +gate forbidding every `deploy-*` package from importing another `deploy-*` package. If shared OCI +value objects are required, keep them with the port in core, not in an adapter implementation. + +## SF-12 — [MAJOR] “Peer install choices” lack a concrete optional-adapter composition and permission mechanism + +**Evidence.** DP-4 §§1/3 says the plugin composition root builds a registry from whichever adapter +packages the project installed, while DP-1 R-GRAPH-3 says the plugin is where all adapters meet. +Static imports from the plugin make all adapters mandatory; dynamic imports require package/export +descriptors, error behavior, schema association, and safe loader validation that are not designed. +The plugin manifest also declares one aggregate `withPermissions(DEPLOY_PLUGIN_PERMISSIONS)`, even +though actual permissions vary from local `systemctl` through `wrangler`, Docker, AWS networking, +and filesystem roots. `target add` is asked to add imports/config/assets but no canonical registry +file contract is specified. + +**Suggested amendment.** Make the plugin depend only on core. Define an adapter descriptor: + +```ts +interface DeployTargetContribution { + readonly key: string; + readonly targetLoader: SafePackageExport; + readonly schemaLoader: SafePackageExport; + readonly permissions: DeployPermissionProfile; +} +``` + +`deploy target add` writes one descriptor to the user-owned/generated target registry and installs +that peer; runtime loads only those descriptors. The plugin composes resolved instances, never +imports every official adapter. Doctor reports the exact installed target permission/tool set, and +the CLI launcher computes/prints the required permission profile instead of advertising every +provider permission for every project. Add missing-peer, invalid-export, duplicate-key, uninstall, +and stale-registry tests. + +## SF-13 — [MAJOR] “Duplicate-guarded/closed-on-key preserved” is a false repository claim + +**Evidence.** DP-2 §6 and DP-6 M-2 describe the shipped registry as closed-on-key and say duplicate +guarding is preserved. The actual method is documented “Register or replace” and unconditionally +calls `Map.set` +(`packages/cli/src/kernel/application/registries/deploy-target-registry.ts:101-104`). This matters +during migration: silently replacing a built-in with a plugin adapter would make the very collision +tests in DP-4 lie. + +**Suggested amendment.** Mark duplicate rejection as **NEW**, not moved behavior. Introduce +`DeployTargetCollisionError { key, existingOwner, incomingOwner }`; make `register` reject by +default and expose a separate, composition-root-only `replaceForCompatibility` method only if a +named W1 shim truly needs it. Test constructor duplicates, generated-registry duplicates, +environment-qualified keys, and deterministic entry ordering. Remove the explicit replacement API +after the compatibility shim is gone. + +## SF-14 — [MAJOR] The doctor union and `contributesDeployTargets` flag repeat the closed-host edit pattern the plugin system should remove + +**Evidence.** DP-4 §5 calls a union that needs one host PR for every plugin “AP-24-safe.” The +current hard-coded union contains only `auth-backend` +(`packages/plugin/src/config/domain/plugin-contributions.ts:11-17`); widening it to `deploy-target` +merely adds the next switch case. Likewise a deploy-specific installer boolean creates a new generic +manifest field for one family. The current axis list is already a central closed vocabulary +(`packages/plugin/src/domain/constants.ts:15-40`), so these one-off discriminants compound host +churn rather than contributing behavior. + +**Suggested amendment.** Replace both host changes with data: + +```ts +interface DoctorCheckContribution { + readonly id: string; + readonly loader: SafePackageExport; +} +interface PluginManifestCapabilities { + readonly contributionAxes?: readonly ContributionAxis[]; +} +``` + +Doctor loads contributed checks through a duplicate-guarded registry; the installer infers deploy +participation from declared axes/descriptors rather than `contributesDeployTargets`. Reserve closed +unions for host-executed protocols whose cases have genuinely different host semantics, not plugin +identities. Add loader isolation, duplicate ID, and failure-reporting tests. + +## SF-15 — [MAJOR] The board hides oversized W1/host work and contains dependency gaps + +**Evidence.** Plan §5 puts ports, registry, conventions, build, and config in DP-1 despite their +different ownership and migration hazards; DP-8 combines three host changes despite DP-4 §5 saying +each is its own slice. DP-9's composition root depends only on DP-2/DP-8 even though it must compose +the W2 adapters and schema registry. DP-11's Story-0 E2E invokes `target add` but does not depend on +DP-10, and DP-16 includes the Cloudflare Containers lane without depending on DP-13. DP-13 combines +the OCI engine and five independently authenticated/live-probed platform clients. These are not +commit-sized slices, and the current approximately 20 children omit the migration compatibility gate +entirely. + +**Suggested amendment.** Replace the draft board with 28 children (29 items including the epic, +still below 30): + +- Split current DP-1 into four children: contracts/re-exports; pure conventions/constants; empty + registry + CLI compatibility composition; compile-emitter refactor/extraction. +- Split current DP-8 into three: contribution contract/builder/verifier; async CLI bootstrap and + mount collisions; generic doctor/capability metadata. +- Split current DP-13 into OCI build/push and a thin-platform client tranche. +- Split current DP-18 into AWS-PROBE-HTTP and the gated adapter/scaffold. +- Add one first-class legacy/config compatibility issue carrying SF-9/SF-10 gates. + +Correct dependencies: plugin composition depends on extracted adapters + schema loader; Story-0 +scaffold E2E depends on the target-add CLI; Cloudflare depends on the OCI slice if Containers remain +in scope; every plugin-mount issue depends on async bootstrap. Each child body must name files, +anti-scope, and the smallest proof command; the expensive full runtime E2E remains a wave exit gate, +not an intermediate loop. + +## SF-16 — [MINOR] “Zero app-code diffs for every story” is too broad to be an enforceable scaffold gate + +**Evidence.** DP-7 §3 and DP-8's opening/cross-story acceptance make identical app code an absolute +invariant. Cloudflare Workers and Vercel Node/Bun are not Deno processes, and DP-3 itself requires +compatibility probes for `node:` touchpoints and runtime lag. A generated Fetch entry can adapt the +framework boundary, but it cannot make arbitrary application use of `Deno.*`, native addons, +filesystem/process APIs, or runtime-specific dependencies portable. The shipped compile target +extraction accepts general service/plugin/app entrypoints rather than a declared Web-standard +profile (`packages/cli/src/kernel/adapters/deploy/compile/compile-targets.ts:14-22`). + +**Suggested amendment.** Replace the gate with: + +> A canonical fixture constrained to the declared runtime profile uses the same domain/service +> source across targets; only generated entry modules, config, bindings, artifacts, workflows, and +> backing packages differ. For arbitrary projects, `plan` performs dependency/API compatibility +> analysis and rejects unsupported runtime touchpoints with file-level diagnostics. + +Publish the profile contract (`deno-native`, `web-standard`, `node-compat`) and give each adapter a +probe fixture. Treat a provider-specific generated entry as adapter output, not evidence that every +unrestricted application is portable. + +## Quick wins + +- Fix the stray signature typo `createDeployTargetRegistry(entries, )` in DP-2 §6. +- Reconcile every “7-op” label with the shipped eight canonical literals (`plan` and `emit` are both + present); if SF-5 is adopted, call the contract eight-op everywhere. +- Use one explicit mapping for config keys (`windows`, `linux`), registry keys (`windows-service`, + `linux-service`), and the proposed `baremetal` CLI target; the corpus currently treats them as + interchangeable. +- In Story 0, choose one flow: preinstall the default target, or require + `deploy target add deno-deploy`; do not state both. +- Change runtime-config wording from “readable without redeploy” to “checked at each CLI/CI + invocation”; there is no resident deploy service to observe changes mid-operation. +- Add `schemaVersion`, adapter version, upstream tool/version range, probe date, and evidence ID to + every published capability manifest. +- Distinguish `unsupported`, `unverified`, `adapter-not-installed`, and `credential-unavailable` in + doctor/capability output; only the first is a provider verdict. +- Require secret-reference tests to prove values never appear in plans, manifests, telemetry, + events, command arguments, or thrown errors. +- Expand the OF-2 graduation rule: a thin PaaS client graduates from a subpath when auth, + lifecycle/error semantics, release cadence, or live-gate ownership diverges—not only when it gains + a large SDK dependency. From 80882a3374a0649f81f88e2f1fcc6f6f5ba5f172 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 06:33:53 +0200 Subject: [PATCH 09/20] =?UTF-8?q?plan(deploy-plugin):=20integrate=20Sol=20?= =?UTF-8?q?adversarial=20(r2)=20=E2=80=94=20all=2016=20findings=20accepted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Triage record + corpus revision r2: eight-op lifecycle with pure plan/emit+provenance; structural namespaced capability contracts with scoped per-variant verdicts; user-declared deployment cells (suggestedCells, never silent partition); empty duplicate-rejecting core registry (collision error is NEW; defaults externalized); two-phase config loader (unknown target errors, never stripped); refactor-then-extract W1; protocol-valid tooling installer manifest; mount-children CLI contributions + async bootstrap; doctor-checks as data; descriptor-based adapter composition with per-target permission profiles; legacy verbs as compat handlers until semver-major; board recut to 29 DPB-n children. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../adversarial-sol-triage.md | 37 +++ .../codex-thread-ids.md | 16 + .../plan-deploy-plugin--seed/context-pack.md | 88 +++--- .../canonical/DP-0-concept-and-goal-frame.md | 2 +- .../canonical/DP-1-family-architecture.md | 57 ++-- .../design/canonical/DP-2-deploy-core.md | 283 +++++++++++------- .../design/canonical/DP-3-adapter-cards.md | 129 +++++--- .../design/canonical/DP-4-plugin-and-host.md | 217 +++++++++----- .../design/canonical/DP-6-migration-map.md | 26 +- .../canonical/DP-7-contribution-matrix.md | 17 +- .../design/canonical/DP-8-scaffold-stories.md | 37 ++- .llm/runs/plan-deploy-plugin--seed/drift.md | 2 + .llm/runs/plan-deploy-plugin--seed/plan.md | 115 +++---- .llm/runs/plan-deploy-plugin--seed/worklog.md | 46 +++ 14 files changed, 695 insertions(+), 377 deletions(-) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-sol-triage.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/codex-thread-ids.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-triage.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-triage.md new file mode 100644 index 000000000..70090b6c7 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-triage.md @@ -0,0 +1,37 @@ +# Triage — Sol xhigh adversarial findings (SF-1…SF-16) + +Supervisor (Fable 5, generator session resumed) triage of `adversarial-sol.md` (commit +`9ed2eeab`). Verdict adopted: the corpus is a sound baseline; all sixteen findings are +**ACCEPTED** — the reviewer verified claims against the shipped file graph with line citations, +and every amendment is adoptable without breaking the ratified concept. Fixes land as corpus +revision **r2** (this commit series). Where an amendment is adopted with a variation, the +disposition says so. + +| Finding | Sev | Disposition | Landed in | +| --- | --- | --- | --- | +| SF-1 `DEFAULT_DEPLOY_TARGETS` in core = dependency inversion | BLOCKER | **ACCEPT** — core owns only the empty duplicate-rejecting registry + port + key/error types; adapters export factories/descriptors; composition roots supply entries; W1 keeps the compat constant in the CLI composition root; debt retirement made conditional | DP-2 §1/§6, DP-6 M-2 | +| SF-2 W1 is not a behavior-free move | BLOCKER | **ACCEPT** — W1 recut into ordered refactor-then-extract sub-slices; `runtime-overrides.ts` stays with bare-metal/leaf owners; build extraction moves to W2 (`deploy-baremetal`), adapter-neutral compile emitter graduates to core only after ports exist | DP-2 intro, DP-6 M-3/M-4, plan §5 | +| SF-3 Installer manifest sketch protocol-invalid | BLOCKER | **ACCEPT** — complete manifest (required metadata, permission string arrays); `officialSource` generalized with a `sourceKind: 'tooling'` variant; protocol change added to the host-extension slices with parse/copy/compat fixtures | DP-4 §3/§5 | +| SF-4 `cli-command` axis lacks discovery-to-startup path; shadow exception unsafe | BLOCKER | **ACCEPT** — host-owned reserved `deploy` shell (owns `desktop`, install hint, shared help); contributions are mount-children `{mount,id,loader}` that never shadow top-level commands; async CLI bootstrap; duplicate `(mount,id)` fails before parsing; manifest-loader (not AST-walker) feeds the registry; `verify-plugin` gains cliCommands/doctorChecks expectations | DP-4 §5/§6/§7, plan OF-3 | +| SF-5 `plan` cannot be both preview and emission | MAJOR | **ACCEPT** — eight-op lifecycle: `plan` pure (serializable `DeploymentPlan`), `emit` materializes content-addressed artifacts + provenance, `up --prebuilt `, plain `up` = convenience composition | DP-2 §2, plan LD-3, DP-3 refs | +| SF-6 Flat closed capability vocabulary couples core to leaves | MAJOR | **ACCEPT** — structural `CapabilityRef` (namespaced, versioned) + `BindingRequirement` + `WorkloadConstraint`; core owns structures + a small runtime-trait vocabulary only; R-GRAPH-1 reworded ("core imports no leaf package; leaves contribute descriptors through structural contracts") | DP-2 §4/§5, DP-1 §2 | +| SF-7 Manifest rows unsupported / mode-collapsed | MAJOR | **ACCEPT** — per-target-**variant** manifests; `CapabilityVerdict {level(+unverified), scope, evidence}`; aspire queue/exclusive-writer claims withdrawn until conformance cells; bare-metal rows enumerated; Deno KV atomicity judged by the atomic conformance suite (the adapter implements CAS); `lossless` requires a live-platform cell | DP-3 §0–§7 | +| SF-8 Macro-service split promised without a topology contract | MAJOR | **ACCEPT** — `DeploymentCell`/`DeploymentTopologyPlan` added; v1 cells user-declared in `deploy/targets.ts`; compiler returns `suggestedCells` but rejects, never partitions silently; CF/AWS stories narrowed to one compute variant until the topology slice lands | DP-2 §5, DP-8, plan §5 | +| SF-9 Legacy verb aliasing collapses non-equivalent semantics | MAJOR | **ACCEPT** — OF-5 flipped: legacy flat commands remain first-class compat handlers owned by `deploy-baremetal` through the next semver-major; only `build → plan+emit`, `status`, `logs` are direct aliases; state-transition tests required | DP-2 §2, DP-6 M-11, plan OF-5 | +| SF-10 Config/plugin bootstrap cycle; unknown targets silently stripped | BLOCKER | **ACCEPT** — two-phase config loader (bootstrap parse without stripping → resolve plugin/adapter schema loaders → compose target schema registry → full parse; unknown target ⇒ `DeployTargetAdapterMissingError`); frozen legacy union exported for the compat window; DP-7 depends on this slice | DP-2 §6, DP-6 M-5, plan §5 | +| SF-11 `deploy-container` exception = adapter-to-adapter dependency | MAJOR | **ACCEPT** — exception deleted; CF/AWS accept an injected core-owned `ContainerBuildPort`; composition roots inject `deploy-container`'s implementation; new import gate: no `deploy-*` imports another `deploy-*` | DP-1 §2, DP-3 §5/§7 | +| SF-12 Peer-install composition + permission model unspecified | MAJOR | **ACCEPT** — plugin depends only on core; `DeployTargetContribution {key, targetLoader, schemaLoader, permissions}` descriptors written by `target add` into the generated registry; doctor + launcher report the per-target permission profile (no aggregate all-provider permission claim) | DP-4 §1–§3, DP-1 §2 | +| SF-13 "Duplicate-guarded / closed-on-key preserved" is false | MAJOR | **ACCEPT** — duplicate rejection marked NEW; `DeployTargetCollisionError`; composition-root-only `replaceForCompatibility` for the W1 shim, removed after | DP-2 §6, DP-6 M-2 | +| SF-14 Doctor union + deploy flag repeat the closed-host pattern | MAJOR | **ACCEPT** — `DoctorCheckContribution {id, loader}` registry (data, duplicate-guarded) instead of union widening; `capabilities.contributionAxes` instead of `contributesDeployTargets` | DP-4 §2/§3/§5, plan LD-9 | +| SF-15 Board hides oversized W1/host work; dependency gaps | MAJOR | **ACCEPT** — board recut to 28 children (+epic = 29): DP-1 split ×4, host extensions ×3, container ×2, AWS ×2, +1 compatibility-gate issue; dependencies corrected (plugin composition ← adapters+schema loader; Story-0 E2E ← target-add CLI; CF ← OCI slice; mounts ← async bootstrap) | plan §5 | +| SF-16 Zero-app-code-diff gate too broad | MINOR | **ACCEPT** — gate scoped to a canonical fixture within the declared runtime profile; `plan` performs dependency/API compatibility analysis with file-level diagnostics; runtime-profile contract (deno-native / web-standard / node-compat) published per adapter | DP-7 §3, DP-8 | + +## Quick wins — all applied + +Typo fix (DP-2 §6); "eight-op" naming reconciled everywhere; explicit config-key ↔ registry-key ↔ +CLI-target mapping note; Story 0 single flow (`target add` explicit; no preinstalled default +target); runtime-config wording ("checked at each CLI/CI invocation"); manifest `schemaVersion` + +adapter/tool versions + probe date + evidence id; doctor distinguishes +`unsupported | unverified | adapter-not-installed | credential-unavailable`; secret-reference +tests must prove absence in plans/manifests/telemetry/events/argv/errors; OF-2 graduation rule +expanded (auth/lifecycle/error-semantics/release-cadence divergence also graduates a client). diff --git a/.llm/runs/plan-deploy-plugin--seed/codex-thread-ids.md b/.llm/runs/plan-deploy-plugin--seed/codex-thread-ids.md new file mode 100644 index 000000000..bce3a1826 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/codex-thread-ids.md @@ -0,0 +1,16 @@ +# deploy-plugin-sol-adv — Codex implementation thread +- **Thread / session id:** `019f7885-e310-7d62-86f2-1b8d693d2647` +- **Rollout:** `/home/codex/.codex/sessions/2026/07/19/rollout-2026-07-19T05-57-48-019f7885-e310-7d62-86f2-1b8d693d2647.jsonl` +- **Worktree:** `/home/codex/repos/wt-deploy-plugin-seed` +- **Branch:** `plan/deploy-plugin` (NO upstream by design). +- **Push rule:** explicit refspec only — `git push origin HEAD:refs/heads/plan/deploy-plugin`. +- **Requested route:** provider=openai · model=gpt-5.6-sol · effort=xhigh +- **Observed route:** provider=openai · model=gpt-5.6-sol · effort=xhigh +- **Route verdict:** matched +- **Runtime:** approval=never · sandbox=dangerFullAccess +- **Brief (staged):** `/home/codex/deploy-plugin-sol-adv-brief.md` +## Steering (same thread — never a second send-message-v2 at this worktree) +```bash +codex exec resume 019f7885-e310-7d62-86f2-1b8d693d2647 -- "" +``` +_Written by `.llm/tools/agentic/codex/launch-codex-slice.ts`._ \ No newline at end of file diff --git a/.llm/runs/plan-deploy-plugin--seed/context-pack.md b/.llm/runs/plan-deploy-plugin--seed/context-pack.md index 48e10b82f..ff45f2c82 100644 --- a/.llm/runs/plan-deploy-plugin--seed/context-pack.md +++ b/.llm/runs/plan-deploy-plugin--seed/context-pack.md @@ -2,58 +2,52 @@ Resumable summary. Read `supervisor.md` first (identity, pipeline, overrides), then this. -## State (2026-07-18) +## State (2026-07-19) -**Generator stage COMPLETE** (`worklog.md` ends `STAGE-COMPLETE: generator`). Branch +**Corpus at revision r2** — generator stage complete AND Sol adversarial integrated. Branch `plan/deploy-plugin` (worktree `/home/codex/repos/wt-deploy-plugin-seed`), baseline `290c68ef`, -all work pushed with explicit refspec. No PR, no issue/label/milestone mutation (kickoff -stop-lines). Commits: `d7879e68` bootstrap → `fed30572` research corpus → `bcec7e53` DP-0..2 → -`a178d31a` DP-3..5 → `96b6c47b` DP-6..8 → (closing commit) plan+worklog+context-pack. - -## What this run is - -Clean seed run for the **deploy plugin family** concept the owner ratified 2026-07-18 -(supersedes #824's unified-runtime board; prior run `plan-unified-runtime--seed` in wt-g8-seed is -the evidence base, distilled into `research/prior-run-distillation.md`). Modified pipeline per -`kickoff.md`: this generator → supervisor-dispatched Sol-xhigh constructive adversarial → Kimi-K3 -doc story → generator integrates. Formal PLAN-EVAL/ratification later, supervisor-decided. +all work pushed with explicit refspec (`git push origin HEAD:plan/deploy-plugin`). No PR, no +issue/label/milestone mutation (kickoff stop-lines). Owner authorized (in-turn, 2026-07-19): +Codex adversarial → integrate valid findings → Kimi K3 doc-story. + +Commit trail: `d7879e68` bootstrap → `fed30572` research corpus → `bcec7e53` DP-0..2 → +`a178d31a` DP-3..5 → `96b6c47b` DP-6..8 → `f360deca` plan lock → `7facbd05` adversarial brief → +`9ed2eeab` Sol findings (Codex thread, id in `codex-thread-ids.md`) → r2 integration commit. + +## Pipeline position + +1. ✅ Generator (Fable 5 · xhigh) — full seed corpus. +2. ✅ Sol xhigh constructive adversarial (`adversarial-sol.md`, SF-1…SF-16) — **all 16 accepted** + (`adversarial-sol-triage.md`), integrated as r2. +3. ▶ **Kimi K3 doc-driven story** — dispatch via `deno task agentic:opencode` (OpenCode + + OpenRouter; K3 slug passed explicitly — drift D-4; brief = `kimi-doc-story-brief.md`). + Deliverable: `doc-story-kimi.md` forecasting the public docs as if W3 shipped; DX seams the + docs cannot explain are reported as findings, not fixed in prose. +4. Then: generator integrates Kimi findings (r3 if needed) → owner decisions OF-1…OF-8 → + (owner extension, 2026-07-18 evening, recorded in supervisor memory) terminal deliverable = + PR on this branch + filed epic & sub-issues (all Backlog/Triage) + supersession closes — + **supervisor-coordinated AFTER the full pipeline; still forbidden to this session until + then**. ## Artifact map -- `research/` — six cited corpus files + `research.md` synthesis (drift candidates D-C1…4). -- `design/canonical/DP-0…DP-8` — concept/goal frame; family graph (R-GRAPH-1…5); deploy-core - (7-op port, emitter split, capability compiler, binding transport); adapter cards + shared - conformance suite; plugin+host design (3 host extensions); selective-wrapping map; migration - map M-1…18; contribution matrix; scaffold stories 0–4. -- `plan.md` — LD-1…12 locked, OF-1…8 owner forks, wave train W1–W5, 21-child board sketch, risks, - gates, debt, deferred scope, downstream attack list (§10). - -## Core design in one paragraph - -Auth-parity family: thin `plugins/deploy` + `@netscript/plugin-deploy-core` (extraction of the -CLI kernel's existing 7-op port/registry/conventions — closes `DEPLOY-ARCHETYPE-7-CORE-SEED`, -flips F-DEPLOY-1/2 to gated) + A2 adapters (`deploy-aspire`, `deploy-baremetal`, `deploy-deno` -extracted; `deploy-container` with fly/koyeb/sevalla/coolify/dokploy subpaths new; cloudflare/ -vercel/aws probe-gated). Deploy owns placement/artifacts/lifecycle; leaves own semantics -(bindings transported by name — the anti-god-object cut). Capability manifests + build-time -rejection make agnosticism honest; provider-optimized scaffolds (Stories 1–4) make -provider-first credible. Goal frame = three tiers (Deno-native process / Web-standard isolate / -Node-compat declared). Nitro: reference-only with an L-1-gated re-entry. Waves: W1 core, W2 -adapters, W3 pluginization (needs the three `@netscript/plugin` host extensions), W4 container -path, W5 probe-gated clouds. - -## For the resumed generator (integration stage) - -1. Read Sol findings + Kimi doc-story outputs (locations per supervisor dispatch). -2. Triage findings with per-finding dispositions (seed-run stage-F discipline); amend DP docs in - place; extend `drift.md`. -3. Author full issue bodies from the plan §5 sketch (board-parity template: `Part of #EPIC`, - anti-scope boundary, `- [ ] gate:` predicates, `Dependencies:`/`Delivery shape:`) + filing - manifest for stage H. Still drafts-only until owner ratifies. -4. Owner forks OF-1…OF-8 go in the decision brief; do not resolve them yourself. +- `research/` — six cited corpus files + `research.md` synthesis. +- `design/canonical/DP-0…DP-8` (r2) — concept/laws L-1…L-7 + three-tier goal frame; family + graph R-GRAPH-1…5 r2 (no leaf imports, no adapter-to-adapter imports, descriptor + composition); deploy-core (eight-op port with pure `plan`/`emit`+provenance/`up --prebuilt`; + structural `CapabilityRef` verdicts per target-variant; user-declared `DeploymentCell`s with + `suggestedCells`; empty duplicate-rejecting registry; two-phase config loader); adapter cards + per variant; plugin+host (protocol-valid tooling manifest, mount-children CLI + async + bootstrap, doctor-as-data, per-target permission profiles); wrapping map; migration map + (compat handlers for legacy verbs; unknown-target error = the one documented behavior + change); contribution matrix; scaffold stories (one compute variant per story). +- `plan.md` (r2) — LD-1…12 locked, OF-1…8 (OF-3/OF-5 re-resolved per SF-4/SF-9), **29-child + `DPB-n` board**, risks, gates, §10 pass status. +- `adversarial-brief.md` / `adversarial-sol.md` / `adversarial-sol-triage.md` — stage-2 record. ## Standing constraints -Stop-lines verbatim in `kickoff.md` (drafts only; no product code; no self-dispatched stages; -push `HEAD:plan/deploy-plugin` only; HARD STOPs on release/milestone actions). Evaluator lanes -are open-models-only; generator ≠ evaluator. +Stop-lines verbatim in `kickoff.md` (drafts only; no product code; push `HEAD:plan/deploy-plugin` +only; HARD STOPs on release/milestone actions). Board filing waits for the full pipeline + owner +coordination. Evaluator lanes open-models-only; generator ≠ evaluator. One Codex sender per +worktree — steer the existing thread (`codex-thread-ids.md`), never a rival send. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md index cb8b8146d..d66ccc6bf 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-0-concept-and-goal-frame.md @@ -96,7 +96,7 @@ frontend axis lands) UI, at every layer, per provider. contract/versioning, install path — copy the auth pattern unless a deploy-specific inversion is named in `research/auth-composition-anatomy.md` §9 (multi-target registry, user-visible leaf config, no v1 HTTP service). -2. **Extraction before invention.** The shipped CLI kernel already contains the 7-op port, the +2. **Extraction before invention.** The shipped CLI kernel already contains the (already eight-op) target port, the registry, and the conventions (debt `DEPLOY-ARCHETYPE-7-CORE-SEED`). Wave 1 moves them; new capability lands in later waves. 3. **No board mutation from this run.** Draft texts only; the filing manifest is produced for a diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md index ba9da0aec..65ea27eff 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-1-family-architecture.md @@ -12,7 +12,7 @@ adapter-registration ownership", `adversarial-nitro-vs-own.md:35-41`). | Package | Dir | Archetype | Role | | --- | --- | --- | --- | | `@netscript/plugin-deploy` | `plugins/deploy/` | **A5** thin plugin | Delivery shell: manifest + contributions (CLI, scaffold, streams, telemetry, doctor), composition root, re-exports | -| `@netscript/plugin-deploy-core` | `packages/plugin-deploy-core/` | **A2** integration core | Ports (7-op target port, emitter port), closed-on-key target registry, capability manifest + compiler, shared conventions (activation/secrets/otel/rollback/health), compile/build primitive, deploy config schema base + schema registry, testing fakes | +| `@netscript/plugin-deploy-core` | `packages/plugin-deploy-core/` | **A2** integration core | Ports (eight-op target port, emitter port, container-build port), **empty** duplicate-rejecting target registry, structural capability + topology contracts + rejection compiler, shared pure conventions (activation/secrets/otel/rollback/health), deploy config schema base + two-phase schema registry, testing fakes (adapter-neutral compile emitter graduates here from `deploy-baremetal` once fs/process/output/config ports exist — r2, SF-1/SF-2) | | `@netscript/deploy-aspire` | `packages/deploy-aspire/` | **A2** adapter | Wraps the `aspire` CLI (`publish`/`deploy`/`destroy`): compose, docker, kubernetes, azure-aca, azure-app-service, azure-aks lanes (extraction of the shipped Aspire target adapters) | | `@netscript/deploy-baremetal` | `packages/deploy-baremetal/` | **A2** adapter | OS-service lane: Windows (Servy) + Linux (systemd) install/lifecycle (extraction of the shipped service targets) | | `@netscript/deploy-deno` | `packages/deploy-deno/` | **A2** adapter | Deno Deploy (new platform): wraps the built-in `deno deploy` CLI + `deno.json` deploy config. **T1 flagship.** | @@ -34,8 +34,9 @@ Naming decisions (owner fork OF-1 records the alternative): - Thin PaaS platforms are **subpaths of `deploy-container`**, not packages (owner fork OF-2): they are tiny REST clients with no heavy SDKs; A9 says pick the smallest structure and fold smaller into larger; five extra JSR packages would multiply publish surface for ~100-line - clients. A platform graduates to its own package the moment it needs a real SDK dependency or - its own scaffold story. + clients. (r2 quick win) A platform graduates to its own package when it gains a real SDK + dependency or its own scaffold story — **or** when its auth model, lifecycle/error semantics, + release cadence, or live-gate ownership diverges from the shared container path. ## 2. The dependency graph (the F5 answer) @@ -44,7 +45,8 @@ Naming decisions (owner fork OF-1 records the alternative): @netscript/config (project-level loader seam) leaf cores: kv, queue, database, plugin-{workers,sagas,triggers,streams}-core ▲ - │ (types only: capability IDs, binding declarations) + │ (NO import edge — leaves contribute namespaced capability + │ descriptors through core's structural contracts; r2, SF-6) ┌─────────────────┴───────────────┐ │ @netscript/plugin-deploy-core │ ◄── @netscript/plugin (base seams) └─────────────────▲───────────────┘ @@ -52,10 +54,13 @@ Naming decisions (owner fork OF-1 records the alternative): ┌──────────┬───────────┼────────────┬─────────────┬────────────┐ │deploy- │deploy- │deploy- │deploy- │deploy- │deploy- │aspire │baremetal │deno │container │cloudflare │vercel / aws - └──────────┴───────────┴────────────┴─────▲───────┴────────────┘ - (cloudflare containers-lane and aws http-lane depend on deploy-container) + └──────────┴───────────┴────────────┴─────────────┴────────────┘ + (no deploy-* imports another deploy-*; cloudflare/aws receive a core-owned + ContainerBuildPort by injection — deploy-container supplies the implementation + at composition time) ▲ - │ composes: selects adapters, builds registry, wires config + │ composes RESOLVED adapter descriptors (dynamic loaders), + │ builds registry, wires config — depends on core only @netscript/plugin-deploy (plugins/deploy) ▲ │ mounts CLI contribution, install/doctor, scaffold @@ -64,19 +69,25 @@ Naming decisions (owner fork OF-1 records the alternative): Rules that keep it acyclic and non-god: -- **R-GRAPH-1 — leafward types only.** `plugin-deploy-core` may import *types and IDs* from leaf - cores (capability identifiers, binding declaration types) — never leaf implementations, never - provider SDKs. Enforced by import-graph gate (F-DEPLOY-2 extension; see DP-2 §6). -- **R-GRAPH-2 — adapters depend on core + their one provider surface.** An adapter imports - `plugin-deploy-core` ports plus its wrapped upstream (wrangler, `@vercel/sdk`, Machines API - client, aspire CLI shell). Never another adapter, never the plugin — with the single declared - exception that `deploy-cloudflare` (Containers lane) and `deploy-aws` (image lane) depend on - `deploy-container`'s exported container-build port, which is itself a core-port implementation, - not a cross-adapter reach-around (it is the L-6 shared path). -- **R-GRAPH-3 — the plugin composes; nothing depends on the plugin.** `plugins/deploy` is the - only place all adapters meet. Adapter *registration* happens in the plugin's composition root - (closed-on-key registry, populated at composition — AP-24 avoidance), or in the scaffolded - project's generated registry for install-time target selection. +- **R-GRAPH-1 — core imports no leaf package.** (r2, SF-6) `plugin-deploy-core` owns only + *structural* capability/binding contracts (`CapabilityRef`, `BindingRequirement`, + `WorkloadConstraint` — DP-2 §4) plus a small well-known runtime-trait vocabulary; leaf packages + contribute namespaced capability descriptors **through those structural contracts**, so core + never imports a leaf package, a leaf implementation, or a provider SDK. Enforced by + import-graph gate (F-DEPLOY-2 extension; see DP-2 §7). +- **R-GRAPH-2 — adapters depend on core + their one provider surface, and never on each other.** + (r2, SF-11) An adapter imports `plugin-deploy-core` ports plus its wrapped upstream (wrangler, + `@vercel/sdk`, Machines API client, aspire CLI shell). **No `deploy-*` package imports another + `deploy-*` package** — an explicit import-graph gate. The L-6 shared container path composes by + injection: `deploy-cloudflare` (Containers lane) and `deploy-aws` (image lane) accept a + core-owned `ContainerBuildPort` in their factories, and the composition root injects + `deploy-container`'s implementation. +- **R-GRAPH-3 — the plugin composes descriptors; nothing depends on the plugin.** (r2, SF-12) + `plugins/deploy` depends only on core. Adapters reach the runtime as + `DeployTargetContribution` descriptors (`{key, targetLoader, schemaLoader, permissions}` — + DP-4 §3) written by `deploy target add` into the generated project registry; the plugin's + composition root resolves those loaders and populates the duplicate-rejecting registry + (AP-24 avoidance). The plugin never statically imports the official adapter set. - **R-GRAPH-4 — leaf provider backings live with the leaves.** A Cloudflare KV backing for `KvStore`, an SQS backing for `MessageQueue`, a DO-backed saga store — these are **leaf-domain adapter packages** (e.g. future `@netscript/kv-cloudflare`, `@netscript/queue-sqs`), owned and @@ -96,7 +107,7 @@ Rules that keep it acyclic and non-god: | plugin-deploy | A5 | F-1, F-3, F-5–F-12, F-13-subtype, F-14–F-19 + runtime/Aspire + consumer-import + R-PLUGIN-PARITY checklist | `verify-plugin.ts`, golden scaffold tests, `plugin doctor`, `scaffold.runtime` case | | plugin-deploy-core | A2 | Full F-1…F-19 + F-DEPLOY-1/2 (flip `reviewed`→`gated`) | oRPC not used ⇒ no slow-type exception needed unless a contract lands (DP-4) | | each adapter | A2 | Full F-1…F-19; runtime validation required when exercised against a real backend | Wrap-target permission READMEs (F-9/AP-19) | -| composite | A7 | Union A2(core) + A6(router) + F-DEPLOY-1 (uniform 7-op or declared subset) + F-DEPLOY-2 (no target logic in command surface) | The family as a whole is doctrine's Archetype 7, delivered through A5 | +| composite | A7 | Union A2(core) + A6(router) + F-DEPLOY-1 (uniform eight-op or declared subset) + F-DEPLOY-2 (no target logic in command surface) | The family as a whole is doctrine's Archetype 7, delivered through A5 | The doctrine tension named in `research/doctrine-constraints.md` (A7 composite vs A5 delivery) resolves cleanly: **A7 describes the core+router shape; A5 describes the delivery vehicle.** Both @@ -108,8 +119,8 @@ get a follow-up amendment noting the plugin-delivered variant of A7 (debt-entry | Wave | Content | Exit proof | | --- | --- | --- | -| **W1 — Core extraction** | `plugin-deploy-core` extracted from the CLI kernel (port, registry, conventions, compile engine, config base + schema registry); CLI re-wired over it; F-DEPLOY-1/2 → `gated`; debt `DEPLOY-ARCHETYPE-7-CORE-SEED` + `DEPLOY-SECRETS-ROLLBACK-CORE` retired | `deno task e2e:cli` green on unchanged verbs; arch:check green with new gates | -| **W2 — Adapter extraction** | `deploy-aspire`, `deploy-baremetal`, `deploy-deno` split out; per-target config member schemas move adapter-side (retiring the deploy slice of `config-plugin-specific-schema-debt`) | Same E2E green; each package passes its A2 gate set; capability manifests published | +| **W1 — Core extraction** (r2, SF-1/SF-2: refactor-then-extract, ordered sub-slices) | (a) port/result/error contracts move behind compatibility re-exports; (b) **empty** duplicate-rejecting registry in core, concrete default factories stay in the CLI composition root; (c) host-owned `deploy` shell split that preserves `desktop` + help; (d) demonstrably pure conventions move *with their constants* (`runtime-overrides.ts` stays with bare-metal/leaf owners); (e) router rewired through the contracts — **no build-implementation move in W1**. F-DEPLOY-1/2 → `gated`; debt `DEPLOY-ARCHETYPE-7-CORE-SEED` retirement is **conditional on the externalized composition root** | `deno task e2e:cli` green after every sub-slice; arch:check green with new gates | +| **W2 — Adapter extraction** | `deploy-aspire`, `deploy-baremetal` (receives the current Windows/Linux build behavior — the adapter-neutral compile emitter graduates to core only after fs/process/output/config ports exist), `deploy-deno` split out; per-target config member schemas move adapter-side over the **two-phase config loader** (SF-10; retiring the deploy slice of `config-plugin-specific-schema-debt`); `DEPLOY-SECRETS-ROLLBACK-CORE` retired | Same E2E green; each package passes its A2 gate set; capability manifests published | | **W3 — Pluginization** | `plugins/deploy` + host contribution-axis extensions (DP-4); deploy group becomes plugin-mounted with built-in back-compat shim; scaffold emits deploy assets via plugin scaffolder | `plugin install deploy` on a scaffold; `scaffold.runtime` case; doctor + verify-plugin green | | **W4 — Container path** | `deploy-container` (+ fly/koyeb/sevalla/coolify/dokploy subpaths); first *generated* Dockerfile/compose artifacts (retiring `cli-deploy-artifacts-missing` for the container lane) | Live smoke on ≥1 managed (fly or koyeb) + 1 self-hosted (coolify or dokploy) platform | | **W5 — Probe-gated clouds** | CF-PROBE → `deploy-cloudflare`; Vercel Build-Output probe → `deploy-vercel`; AWS-PROBE (HTTP) → `deploy-aws` HTTP scope; event-semantics probes remain separate cards | Each adapter ships only after its probe card passes the shared conformance suite (L-1, L-7) | diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md index 76a0b4f61..0c28d9407 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md @@ -1,68 +1,90 @@ # DP-2 — `@netscript/plugin-deploy-core`: ports, registry, capabilities, conventions > **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> **r2** — amended per Sol adversarial findings SF-1, SF-2, SF-5, SF-6, SF-7, SF-8, SF-9, SF-10, +> SF-13 (`adversarial-sol.md`, triage in `adversarial-sol-triage.md`). Archetype 2 integration core. This is the extraction target of debt -`DEPLOY-ARCHETYPE-7-CORE-SEED` (`arch-debt.md:2011-2063`): the primitives below *already exist* -as target-agnostic modules in `packages/cli/src/kernel/domain/deploy/` and move here with their -tests; net-new elements are marked **NEW**. +`DEPLOY-ARCHETYPE-7-CORE-SEED` (`arch-debt.md:2011-2063`) — with the r2 correction (SF-2) that +W1 is a **refactor-then-extract**, not a verbatim move: contracts and demonstrably pure +conventions move first behind compatibility re-exports; the build engine is entangled with CLI +config/output/Windows modules and moves to `deploy-baremetal` in W2, with an adapter-neutral +compile emitter graduating to core only once filesystem/process/output/config ports exist. +`runtime-overrides.ts` duplicates leaf job/saga/task vocabulary and describes `.deploy/windows` — +it stays with its bare-metal/leaf owners and is **not** a shared deploy convention. Net-new +elements are marked **NEW**. ## 1. Public surface (subpaths, layer-named per auth-core parity) | Subpath | Contents | | --- | --- | -| `.` | Curated barrel: `createDeployRuntime`, the two ports, registry factory, manifest types | -| `./domain` | `DeploymentPlan`, `DeployTargetDescriptor`, `ResourceBinding`, operation result types, error taxonomy | -| `./ports` | `DeployTargetPort` (7-op), `ArtifactEmitterPort`, `ContainerBuildPort`, `OsServicePort` (moved), registry port | -| `./capabilities` | `DeployCapabilityManifest`, capability IDs, `compileCapabilityVerdict` (the rejection compiler) — **NEW** (UR-5 reuse) | -| `./conventions` | activation (health-gated symlink/dir-swap), secrets (env-file reference + redaction), rollback, observability/OTEL, health-gate — moved verbatim from the CLI kernel | -| `./build` | The `deno compile` pipeline (compile-runner/targets/platform/bundler/config/format — moved) | -| `./config` | `DeployTargetBaseSchema` (moved from `@netscript/config`), target schema registry — retires the deploy slice of `config-plugin-specific-schema-debt` | -| `./registry` | `createDeployTargetRegistry` (closed-on-key), `DEFAULT_DEPLOY_TARGETS` relocation | +| `.` | Curated barrel: `createDeployRuntime`, the ports, registry factory, manifest/topology types | +| `./domain` | `DeploymentPlan`, `DeploymentCell` + `DeploymentTopologyPlan` (**NEW**, SF-8), `DeployTargetDescriptor`, `ResourceBinding`, operation result types, error taxonomy (incl. `DeployTargetCollisionError`, `DeployTargetAdapterMissingError`) | +| `./ports` | `DeployTargetPort` (eight-op), `ArtifactEmitterPort`, `ContainerBuildPort`, `OsServicePort` (moved), registry port | +| `./capabilities` | Structural capability contracts (`CapabilityRef`, `BindingRequirement`, `WorkloadConstraint`, `CapabilityVerdict`, `DeployCapabilityManifest`) + the runtime-trait vocabulary + `compileCapabilityVerdict` — **NEW** (UR-5 reuse, restructured per SF-6/SF-7) | +| `./conventions` | activation (health-gated symlink/dir-swap), secrets (env-file reference + redaction), rollback, observability/OTEL, health-gate — the demonstrably pure policies moved *with their constants* (SF-2); `runtime-overrides.ts` excluded | +| `./config` | `DeployTargetBaseSchema` (moved from `@netscript/config`), the target schema registry + **two-phase loader contract** (**NEW**, SF-10) | +| `./registry` | `createDeployTargetRegistry()` — **empty**, duplicate-rejecting (SF-1/SF-13); key/error types; no default target set | | `./testing` | In-memory target adapter, fake convention ports, manifest fixture builders (A2 requirement) | Budget: ≤ 20 exports per subpath (F-5); no oRPC contract in core (the optional plugin contract lives with the plugin, DP-4), so **no `--allow-slow-types` exception is needed** — core must pass `deno doc --lint` clean. -## 2. The 7-op target port (moved, then sharpened) +## 2. The eight-op target port (r2 — SF-5/SF-9) -The shipped `DeployTargetPort` (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`) -moves as-is in W1; W2 sharpens it in place: +The shipped port (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`) already carries +**eight** canonical operations — `plan` and `emit` are distinct there, and shipped adapters +already diverge on their semantics (Deno Deploy `plan` is non-mutating preflight; Aspire compose +`plan` writes artifacts). r2 locks the eight-op lifecycle with **pure `plan`**: ```ts -type DeployOperation = 'plan' | 'up' | 'down' | 'status' | 'logs' | 'rollback' | 'secrets'; +type DeployOperation = + | 'plan' | 'emit' | 'up' | 'down' | 'status' | 'logs' | 'rollback' | 'secrets'; interface DeployTargetPort { - readonly key: DeployTargetKey; // 'deno-deploy' | 'compose' | … (open via registry) + readonly key: DeployTargetKey; readonly operations: readonly DeployOperation[]; // declared subset (F-DEPLOY-1) readonly capabilities: DeployCapabilityManifest; // §4 — NEW field, backend-truthful - plan(ctx: DeployOperationContext): Promise; // dry-run + artifact emission - up(ctx): Promise; + plan(ctx): Promise; // PURE wrt artifact dirs and providers: resolves + // topology, validates capabilities, returns a + // serializable DeploymentPlan (+ diagnostics) + emit(ctx): Promise; // materializes content-addressed artifacts; does + // not deploy/push unless the format models a + // publish phase explicitly + up(ctx): Promise; // plain `up` = convenience plan → emit → up; + // `up --prebuilt ` consumes an artifact down(ctx): Promise; status(ctx): Promise; logs(ctx): Promise; - rollback(ctx): Promise; // platform-native or convention-backed; never silent no-op - secrets(ctx): Promise; // reference/rotation over the secrets convention + rollback(ctx): Promise; // platform-native or convention-backed; never a silent no-op + secrets(ctx): Promise; // reference/rotation over the secrets convention } ``` -- **Verb vocabulary locked** (resolves the deferred decision at - `06-archetypes.md:340-346` — plan-gate requires it): the canonical surface is - **`netscript deploy `** with the 7-op set. The legacy flat verbs - (`build/install/start/stop/upgrade/uninstall`) remain as **bare-metal-lane aliases** routed to - the `baremetal` target's ops, marked deprecated in help output for two minor releases before - removal (migration-map §4). `plan` subsumes "emit": artifact emission is `plan` with an - `--output-path` (the shipped compose adapter already behaves this way). -- Unsupported ops: **declared subsets**, exactly as shipped. An op absent from `operations` is - never advertised (backend-truthful, auth-S1 lesson); calling it yields - `DeployOperationUnsupportedError` — the auth `AuthBackendOperationUnsupportedError` pattern - (`research/auth-composition-anatomy.md` §4). +- **Verb vocabulary locked** (resolves the deferred decision at `06-archetypes.md:340-346`): + canonical surface is **`netscript deploy `** with the eight-op set. The + build/deploy split is CI-real: a build job runs `plan` + `emit` and hands the + `EmittedArtifactManifest` (artifact digest, source revision, target variant, emitter version, + provenance) to a later deploy job running `up --prebuilt`. +- **Legacy flat verbs** (r2, SF-9 — semantics are NOT equivalent to `up`/`down`): `build`, + `install`, `start`, `stop`, `copy`, `upgrade`, `package-cli`, `uninstall` remain **first-class + compatibility handlers owned by `deploy-baremetal`** through the next semver-major release, + behind a `BaremetalCompatibilityCommands` adapter that preserves their current flags and + side-effect boundaries (shipped `start`/`stop` operate on registered services without + install/uninstall; `copy` syncs prebuilt artifacts without registration; `upgrade` is a + five-step transaction). Only `build → plan + emit`, `status`, and `logs` are direct aliases. + Help output may deprecate; **no minor-release removal date is claimed** until an equivalent + canonical workflow and migration telemetry exist. Golden help/exit-code tests plus + state-transition tests prove `stop` never uninstalls and `start` never registers. +- Unsupported ops: **declared subsets**, exactly as shipped; an op absent from `operations` is + never advertised (backend-truthful); calling it yields `DeployOperationUnsupportedError` (the + auth `AuthBackendOperationUnsupportedError` pattern). - `rollback`/`secrets` graduate from "declared-unsupported everywhere" (#341) to - convention-backed implementations per adapter card (DP-3); the conventions module already - ships the target-agnostic halves (`DEPLOY-SECRETS-ROLLBACK-CORE` debt retires here). + convention-backed implementations per adapter card (DP-3); the target-agnostic halves ship in + `./conventions` (`DEPLOY-SECRETS-ROLLBACK-CORE` debt retires in W2). -## 3. `ArtifactEmitterPort` (NEW) — separated so lifecycle ≠ build +## 3. `ArtifactEmitterPort` — emission separated from lifecycle ```ts interface ArtifactEmitterPort { @@ -73,96 +95,147 @@ interface ArtifactEmitterPort { ``` Separating emission from lifecycle is what lets L-1 hold: *whatever emits, emits behind this -port* — a provider-native emitter, the shared container builder, or (if ever demanded) an -optional Nitro-driven emitter — all replaceable per target without touching `DeployTargetPort` -consumers. `EmittedArtifactManifest` adopts the market lesson (self-describing artifact: -entrypoints, assets, traced deps, migrations, durable resources, schedules, health/shutdown — -`research/prior-run-distillation.md` §5 rule 3). +port* — replaceable per target without touching `DeployTargetPort` consumers. +`EmittedArtifactManifest` is self-describing (entrypoints, assets, traced deps, migrations, +durable resources, schedules, health/shutdown — the market lesson) **plus provenance** (r2, +SF-5): artifact digest, source revision, target variant, emitter version. -`ContainerBuildPort` is the OCI specialization implemented by `deploy-container` and consumed by -the cloudflare-containers and aws lanes (R-GRAPH-2's declared exception). +`ContainerBuildPort` is the core-owned OCI specialization; `deploy-container` exports the +implementation and composition roots **inject** it where needed (R-GRAPH-2 r2 — no +adapter-to-adapter import, SF-11). -## 4. Capability manifest + rejection compiler (NEW; the heart of honest agnosticism) +## 4. Capability contracts (r2 — SF-6/SF-7: structural, scoped, variant-aware) + +The r1 design used one flat closed `DeployCapabilityId` union — which would have coupled core to +every leaf (the union must import leaf IDs) and mixed three dimensions (runtime traits, leaf +semantic guarantees, workload constraints). r2 replaces it with structural contracts owned by +core, populated by leaves and adapters without core importing either: ```ts +interface CapabilityRef { // namespaced + versioned — leaf growth without manifest rot + readonly namespace: string; // 'runtime' | '@netscript/kv' | '@netscript/queue' | … + readonly name: string; // 'long-running-process' | 'atomic' | 'consume' | … + readonly major: number; +} +interface BindingRequirement { // what the app needs of a named logical resource + readonly binding: string; // logicalName from the project graph + readonly capability: CapabilityRef; +} +interface WorkloadConstraint { // topology/workload dimension, distinct from capabilities + readonly kind: 'singleton' | 'long-running' | 'co-locate' | 'offline'; +} + +interface CapabilityVerdict { + readonly level: 'lossless' | 'partial' | 'unsupported' | 'unverified'; + readonly scope: 'runtime' | 'adapter' | 'binding'; // WHAT the verdict describes + readonly evidence?: string; // conformance-suite cell / probe id + readonly note?: string; // honest caveat, surfaced by CLI +} + interface DeployCapabilityManifest { + readonly schemaVersion: number; readonly target: DeployTargetKey; - readonly tier: 'deno-native' | 'web-standard' | 'node-compat'; // DP-0 §3 + readonly variant: string; // 'workers' | 'containers' | 'lambda' | 'fargate' | + // 'compose' | 'kubernetes' | … (SF-7: no mode collapse) + readonly tier: 'deno-native' | 'web-standard' | 'node-compat'; readonly process: 'long-lived' | 'bounded-window' | 'isolate'; - readonly capabilities: Readonly>; + readonly verdicts: ReadonlyMap; readonly sagas: 'supported' | 'externalized' | 'rejected'; - readonly notes?: Readonly>; // honest caveats, surfaced by CLI + readonly toolVersions?: Readonly>; // upstream tool/version range + readonly probedAt?: string; // evidence date } ``` -- `DeployCapabilityId` is a **closed vocabulary of deployment-relevant capability IDs** - (`http-serve`, `static-assets`, `websocket`, `queue-consume`, `kv-atomic`, `cron`, - `long-running-process`, `exclusive-db-writer`, `offline-sync`, …). IDs *reference* leaf - semantics; their **definitions and conformance tests live with the leaf packages** - (R-GRAPH-4). Core imports only the ID types — this is the exact cut that stops the - `ResourceBindingResolverPort` god-object failure (adversarial F5): core never interprets what - `kv-atomic` means; it only matches requirements against declarations. -- `compileCapabilityVerdict(appRequirements, manifest)` is the **build-time rejection compiler** - (UR-5): `unsupported` → build failure with the manifest note; `partial` → warning that must be - acknowledged in config; never a runtime surprise, never a silent downgrade (L-3). App - requirements derive from the project's logical graph (`appsettings.json` resource/plugin - declarations) — the compiler is invoked by `deploy plan` and by scaffold-time target - selection. -- `sagas` tri-state is verbatim UR-5/sagas-constraint law: `externalized` means a macro-service - split of the same app model, never downgrade-to-tasks - (`research/prior-run-distillation.md` §6). -- **Backend-truthful**: the manifest is authored next to the adapter and exercised by the shared - conformance suite (DP-3 §1); a manifest row the suite cannot demonstrate is a gate failure — - deploy's version of the auth board's p0 (#872). - -## 5. Resource bindings (declarative, opaque to core) +- **Core owns only the structures** plus the small well-known `runtime:*` trait vocabulary + (`http-serve`, `static-assets`, `websocket`, `cron`, `long-running-process`). Leaf packages + export their namespaced requirement/conformance descriptors; core never imports a leaf + (R-GRAPH-1 r2). Verdicts about leaf semantics carry `scope: 'binding'` and come from + **installed leaf backing manifests**, composed with the runtime manifest by the compiler — + a runtime manifest never claims queue/KV semantics by itself (SF-7). +- **`unverified` is not `unsupported`**: unproven, not-installed, and credential-unavailable + states are distinguished in doctor/capability output; only a demonstrated impossibility is + `unsupported`. A `lossless` verdict requires a **live-platform** conformance cell — an + in-memory fake validates the harness, never certifies the provider. +- `compileCapabilityVerdict(appRequirements, manifests…)` remains the **build-time rejection + compiler** (UR-5): `unsupported` → build failure with the note; `partial` → warning that must + be acknowledged in config; never a runtime surprise, never a silent downgrade (L-3). Sagas + tri-state law unchanged. + +## 5. Topology: cells are declared, never silently partitioned (**NEW**, SF-8) + +The mixed-compute stories (Workers + Containers; Lambda + Fargate) need a contract the r1 corpus +lacked: ```ts -interface ResourceBinding { - readonly logicalName: string; // from the project graph (appsettings) - readonly kind: LeafBindingKind; // opaque leaf-owned discriminator: 'kv' | 'queue' | 'database' | … - readonly requirement: readonly DeployCapabilityId[]; // what the app needs of it +interface DeploymentCell { + readonly id: string; + readonly selectors: readonly string[]; // which services/consumers/schedules it owns + readonly target: DeployTargetKey; + readonly variant: string; + readonly bindings: readonly string[]; +} +interface DeploymentTopologyPlan { + readonly cells: readonly DeploymentCell[]; + readonly transports: readonly CrossCellTransport[]; // explicit cross-cell communication } ``` -Deploy transports bindings **by name into artifacts and environment** (env vars, wrangler -bindings blocks, aspire references) — it never resolves them to SDK objects and never validates -leaf semantics (leaf cores do, at their own composition time). The concrete mapping table -(logicalName → provider handle) is target config authored in `deploy.targets..bindings`, -validated by the adapter's schema member. This replaces rev2's `ResourceBindingResolverPort` and -`ActivationRouterPort` with a declaration + transport contract; event *activation* (queue → -handler wiring on push-model providers) is leaf-adapter territory, gated by the leaf's own -conformance (adversarial F3 honored). - -## 6. Conventions, registry, config - -- **Conventions** move verbatim: `activation-convention.ts` (retain count, symlink/dir-swap, - health gate), `secrets-convention.ts` (env-file reference, 0o600, redaction), - `rollback-convention.ts`, `observability-convention.ts` (OTEL endpoint/protocol/prefix), - `health-gate.ts`, plus `servy-config.ts` → `deploy-baremetal` (target-specific). R-DEPLOY-3 - stands: adapters delegate, never fork. -- **Registry**: `createDeployTargetRegistry(entries, )` — closed-on-key, duplicate-guarded - (auth's `createAuthPresetRegistry` pattern), populated only at composition roots (plugin, - generated project registry, CLI shim). **Multi-target by design** — unlike auth's - single-active backend, a project registry may hold many targets keyed - `[@]` (the auth-inversion named in - `research/auth-composition-anatomy.md` §9). -- **Config**: `DeployTargetBaseSchema` (the shipped Zod raw shape with - activation/secrets/otel/health/docker sub-blocks) moves to `./config`; each adapter exports its - member schema (spreading the base — R-DEPLOY-4); the **target schema registry** composes the - `deploy.targets` discriminated union at load time, so `@netscript/config` retains only the - project-loader seam and never names vendors. Environment dimension: `deploy.targets.` gains - an optional `environments: Record>` overlay — the modeled - replacement for today's opaque `--environment` pass-through (inventory §4 gap). +v1 rule: **cells are user-declared** in `deploy/targets.ts`. The compiler may return +machine-readable `suggestedCells` when a single-cell verdict fails, but it **rejects rather than +partitions silently**. Gates: single ownership of every service/consumer/schedule across cells; +explicit cross-cell transport; deterministic plan output. Until the topology slice lands, the +Cloudflare/AWS scaffold stories are narrowed to one compute variant per target (DP-8). + +`ResourceBinding` is unchanged in spirit — declarative name+kind transport, leaf semantics never +interpreted by deploy — with requirements now expressed as `BindingRequirement` (namespaced +`CapabilityRef`s, §4). + +## 6. Conventions, registry, config (r2 — SF-1/SF-10/SF-13) + +- **Conventions**: the demonstrably pure policies (`activation`, `secrets`, `rollback`, + `observability`, `health-gate`) move *with their constants*; `servy-config.ts` and + `runtime-overrides.ts` go to `deploy-baremetal`/leaf owners (SF-2). R-DEPLOY-3 stands: + adapters delegate, never fork. +- **Registry**: `createDeployTargetRegistry(entries = [])` — **empty by construction**; core owns + only the registry implementation, port, key/error types. **Duplicate rejection is NEW + behavior** (the shipped registry is register-or-replace — SF-13): `register` rejects with + `DeployTargetCollisionError { key, existingOwner, incomingOwner }`; a composition-root-only + `replaceForCompatibility` exists solely for the W1 shim and is removed with it. + `DEFAULT_DEPLOY_TARGETS` is **deleted as a core concept** (SF-1): during W1 its compatibility + equivalent stays in the CLI composition root; from W3 the plugin/generated project registry + supplies entries (as resolved `DeployTargetContribution` descriptors, DP-4 §3). Multi-target + by design, keyed `[@]`. +- **Config — two-phase loader** (**NEW**, SF-10). Today the full config parses `deploy` through + a static schema *before* the plugin list is even available, and unknown target keys are + silently stripped — so "adapters contribute schemas at load time" needs a real bootstrap + contract: + 1. Bootstrap-parse only project identity, `plugins`, and `deploy.targets` as + `Record` — nothing stripped. + 2. Resolve plugin + generated adapter descriptors (including `schemaLoader`s). + 3. Compose the target schema registry; parse the full config. + 4. An unrecognized target key ⇒ `DeployTargetAdapterMissingError` — **never silently dropped** + (a deliberate, documented behavior change from today's strip-by-design). + `@netscript/config` keeps the project-loader seam and exports the legacy target types/schemas + as a frozen delegating union for the compatibility window. Environments overlay + (`environments: Record>`) unchanged from r1. Key-mapping note + (quick win): config keys (`windows`, `linux`), registry keys (`windows-service`, + `linux-service`), and the CLI target (`baremetal`) are distinct vocabularies with one explicit + mapping table in `./config` — never treated as interchangeable. ## 7. Gates and proof - Full A2 gate set; **F-DEPLOY-1** (AST + registry scan: every registered adapter implements the - 7-op contract or a declared subset) and **F-DEPLOY-2** (import graph + AST: no target-specific - business logic outside adapter packages; conventions imported from core) flip - `reviewed` → `gated` in W1 — extended with **R-GRAPH-1** (core imports leaf *types* only; no - provider SDK, no leaf implementation) as a new import-graph assertion. + eight-op contract or a declared subset) and **F-DEPLOY-2** (import graph + AST: no + target-specific business logic outside adapter packages; conventions imported from core) flip + `reviewed` → `gated` in W1 — extended with **R-GRAPH-1 r2** (core imports no leaf package, no + provider SDK) and the **no `deploy-*` → `deploy-*` import** assertion (SF-11) as import-graph + gates. +- Registry tests: constructor duplicates, generated-registry duplicates, environment-qualified + keys, deterministic ordering (SF-13). Loader tests: installed custom target, missing adapter, + malformed adapter config, plugin-loader failure, all existing target keys (SF-10). +- Secret-reference tests prove values never appear in plans, manifests, telemetry, events, + command argv, or thrown errors (quick win). - `./testing` ships the in-memory target + fixture builders so the plugin, CLI shim, and conformance kit test without live providers (A2 Concept of Done). -- Quality bar: `deno task quality:scan` (no `any`/casts), `arch:check`, `deno doc --lint` clean, - jsr publish dry-run — per harness pitfall rules. +- Quality bar: `deno task quality:scan`, `arch:check`, `deno doc --lint` clean, jsr publish + dry-run — per harness pitfall rules. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md index 40bbb4067..61a952184 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md @@ -5,17 +5,23 @@ ## 0. The shared conformance suite (binds every card) -One suite, run per target, is the family's acceptance instrument (the deploy analogue of the -auth board's single test kit, S14): it exercises each declared `DeployCapabilityManifest` row — -HTTP serve + static assets, graceful shutdown through `ServiceShutdownCoordinator` (L-5), -health-gate + rollback behavior, secrets redaction, log retrieval, and (where declared) cron, -queue-consume, kv-atomic — against the target's realization (in-memory fake in CI; live probe -lane for the real platform). **A manifest row without a passing suite cell is a gate failure** +One suite, run per **target variant**, is the family's acceptance instrument (the deploy +analogue of the auth board's single test kit, S14): it exercises each declared +`DeployCapabilityManifest` verdict — runtime traits (HTTP serve + static assets, graceful +shutdown through `ServiceShutdownCoordinator` (L-5), cron), health-gate + rollback behavior, +secrets redaction, log retrieval — against the variant's realization, and composes **installed +leaf backing manifests** for `scope: 'binding'` semantics (queue-consume, kv-atomic) rather than +letting a runtime manifest claim them (r2, SF-7). Verdict levels are +`lossless | partial | unsupported | unverified`: a **`lossless` verdict requires a live-platform +cell** (an in-memory fake validates the harness, never certifies the provider), and +unproven/not-installed/credential-unavailable states report as `unverified`-family diagnostics, +never as provider impossibility. **A verdict without its suite cell is a gate failure** (backend-truthful, DP-2 §4). The suite lives in `plugin-deploy-core/testing` + per-adapter live -probes; CI matrix = target × capability × verdict (board-parity lesson 9). +probes; CI matrix = target × variant × capability × verdict (board-parity lesson 9). -Every card below states: tier (DP-0 §3), wrapped surface, ops subset, manifest sketch (only -non-obvious rows), scaffold hooks, permissions, and probe gates. +Every card below states: tier (DP-0 §3), wrapped surface, ops subset, manifest sketch per +**variant** (only non-obvious rows), scaffold hooks, permissions, and probe gates. Manifests +carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidence ids (DP-2 §4). ## 1. `deploy-aspire` (W2 extraction) — T1 @@ -28,9 +34,13 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. platform-marker validation preserved. `rollback`: convention-backed for compose (previous emitted dir + dir-swap); platform-native where Azure provides it — else absent from `operations`. -- **Manifest sketch:** `process: long-lived`; sagas `supported` (long-lived containers); - `queue-consume: lossless` (in-process listeners); `exclusive-db-writer: lossless` on - single-replica compose, `partial` on k8s (replica-dependent, note required). +- **Manifest sketch (per variant: `compose`, `docker`, `kubernetes`, `azure-*`):** + `process: long-lived`; sagas `supported` (long-lived containers). (r2, SF-7) Queue and + exclusive-writer semantics are **`scope: 'binding'` verdicts composed from installed leaf + backing manifests + workload constraints — withdrawn from this runtime card** until their + conformance cells exist: process liveness proves neither delivery/ack/redelivery semantics nor + single-writer topology. The k8s variant carries a `singleton` `WorkloadConstraint` interaction + note (replica-dependent) instead of a mode-collapsed verdict. - **Scaffold hooks:** owns the `deploy-compose-ghcr.yml` workflow template; keeps emitting nothing hand-authored (aspire publish output stays the artifact) until W4's container path offers generated Dockerfiles as an alternative. @@ -40,15 +50,20 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. - **Wraps:** Servy (Windows) / systemd (Linux) via the shipped OS-service port; the `deno compile` build pipeline consumed from core `./build`. -- **Ops:** full 7-op including convention-backed `rollback` (activation retain + symlink/ +- **Ops:** full eight-op set including convention-backed `rollback` (activation retain + symlink/ dir-swap + health gate — already designed, `DEPLOY-BAREMETAL-PUBLIC-WIRING` debt closes by composing the ports in the plugin's composition root) and `secrets` (env-file reference, - 0o600). Legacy flat verbs (`build/install/start/stop/upgrade/uninstall`) alias here - (DP-2 §2). -- **Manifest:** `process: long-lived`; everything `lossless` that the host machine provides; - sagas `supported`; `offline-sync: profile-dependent` note. The Linux systemd lane's - integration-test debt (`cli-deploy-linux-integration-untested`) becomes a live-probe card in - this adapter's suite. + 0o600). Legacy flat verbs (`build/install/start/stop/copy/upgrade/package-cli/uninstall`) + live here as first-class compatibility handlers with their exact shipped semantics — + `BaremetalCompatibilityCommands`, DP-2 §2 (r2, SF-9). +- **Manifest (r2, SF-7 — enumerated rows, no blanket claim):** `process: long-lived`; runtime + rows: `runtime:http-serve` lossless, `runtime:static-assets` lossless, + `runtime:long-running-process` lossless, `runtime:cron` via OS scheduler `partial` (note), + `runtime:websocket` lossless; sagas `supported`; binding-scope semantics come from whatever + leaf backings the project installs (host machine ≠ automatic `lossless`). The Linux systemd + lane's integration-test debt (`cli-deploy-linux-integration-untested`) becomes a live-probe + card in this adapter's suite. Owns the `BaremetalCompatibilityCommands` legacy-verb adapter + (DP-2 §2, SF-9). - **Permissions:** `--allow-run=servy,systemctl,deno`, filesystem on install base. ## 3. `deploy-deno` (W2 extraction; T1 flagship) @@ -59,14 +74,19 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. - **Ops:** plan (preflight + build config)/up/down/status/logs; `secrets` via `deno deploy env` (reference model — values never serialized into artifacts); `rollback` via the platform's instant-rollback revision routing. -- **Manifest (the honesty showcase):** `tier: deno-native`; `process: bounded-window`; - `queue-consume: unsupported` (platform has no queues — note points to `externalized` MQ - bindings); `kv-atomic: partial` (Deno KV without queues/replication controls); - `cron: lossless` (`Deno.cron()` auto-discovered); sagas `externalized` (macro-service split or - external transport) — every row from `research/provider-deploy-surfaces.md` §5. Monorepo git - integration gap noted; local-source deploys (`--source local`) unaffected. -- **Scaffold hooks:** default target of the plain scaffold (Deno-native-first default); - `deploy-deno-deploy.yml` workflow with `DENO_DEPLOY_TOKEN` (org tokens). +- **Manifest (the honesty showcase; r2, SF-7):** `tier: deno-native`; + `process: bounded-window`; `@netscript/queue:consume → unsupported` at binding scope (the + platform has no queues — note points to `externalized` MQ bindings); + `@netscript/kv:atomic → judged by the leaf's atomic conformance suite` — NetScript's Deno KV + adapter implements version checks and atomic commit + (`packages/kv/adapters/deno-kv.adapter.ts:155-194`), so atomicity is NOT downgraded for the + platform's separate gaps; those gaps are their own refs (`kv:queues → unsupported`, + `kv:replication-controls → unsupported`); `runtime:cron → lossless` (`Deno.cron()` + auto-discovered); sagas `externalized` (macro-service split or external transport). Monorepo + git integration gap noted; local-source deploys (`--source local`) unaffected. +- **Scaffold hooks:** the **recommended first target** (Deno-native-first default), added + explicitly via `deploy target add deno-deploy` — no target is preinstalled (Story 0 single + flow, r2 quick win); `deploy-deno-deploy.yml` workflow with `DENO_DEPLOY_TOKEN` (org tokens). - **Permissions:** `--allow-run=deno`, `--allow-net` (API), `--allow-read`. ## 4. `deploy-container` (W4, NEW) — T1; the L-6 shared path @@ -81,9 +101,12 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. - **Ops:** plan (emit Dockerfile/compose + image build)/up/down/status/logs; `secrets` = env-set via platform API (reference model); `rollback` = platform revision/machine-image swap where the API offers it, else declared absent. -- **Manifest:** `process: long-lived` containers ⇒ sagas `supported`; capability rows otherwise - inherited from what the *image* runs (the leaf bindings selected at scaffold time) — the - manifest mechanism keeps this honest per project. +- **Manifest (r2, SF-7 — composition rule made explicit):** `process: long-lived` containers ⇒ + sagas `supported`; runtime rows from the image contract (`runtime:http-serve`, + `runtime:long-running-process` lossless). Binding-scope verdicts are **composed by the + compiler** from the *installed leaf backing manifests* the project selected at scaffold time — + the container runtime manifest itself claims nothing about queue/KV semantics; "inherited" + means that composition, not an implicit blanket. - **This retires** the container half of `cli-deploy-artifacts-missing`: NetScript finally emits a Dockerfile — but as adapter output, not scaffold hand-authoring. - **Permissions:** `--allow-run=docker,podman,flyctl`, `--allow-net` per platform API. @@ -92,8 +115,10 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. - **Wraps:** `wrangler` (npm, invoked as a tool): emits `wrangler.jsonc` (entry, compatibility date, bindings blocks) + a **worker entry adapting `ServiceApp.fetch`** (Web-standard, L-4: - in-process Fetch delegation), then `wrangler deploy` / `wrangler versions`. Containers lane - delegates image build to `deploy-container` + `@cloudflare/containers` wiring. + in-process Fetch delegation), then `wrangler deploy` / `wrangler versions`. The Containers + lane accepts a core-owned `ContainerBuildPort` **by injection** (`deploy-container` supplies + the implementation at composition time — no adapter-to-adapter import; r2, SF-11) + + `@cloudflare/containers` wiring. - **Probe gate (CF-PROBE, before adapter graduation):** build+deploy a NetScript service to a live Workers target; measure Miniflare-vs-production fidelity for the dev loop (Miniflare is a simulator, not an oracle — adversarial F2); demonstrate static-assets, `nodejs_compat` @@ -101,13 +126,19 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. yet — a real operational caveat). - **Ops:** plan (emit config + entry)/up/down (delete)/status/logs (`wrangler tail`); `secrets` via `wrangler secret` (reference model); `rollback` via `wrangler versions rollback`. -- **Manifest:** `tier: web-standard`; `process: isolate`; `queue-consume: unsupported` **in this - adapter** (CF Queues push-consumer wiring is leaf territory — a future `@netscript/queue-cf` - card, R-GRAPH-4; the binding transport still declares the queue by name); - `kv-atomic: unsupported` (Workers KV no-CAS — capability rejection per L-2); - `long-running-process: unsupported` on Workers / `lossless` on Containers lane; sagas - `rejected` on Workers, `externalized`-or-`supported` via Containers. DO-backed saga/worker - stores are explicitly **probe cards owned by the saga/worker leaves**, not this adapter. +- **Manifests (r2, SF-7 — two variants, no mode collapse):** + - `variant: workers` — `tier: web-standard`; `process: isolate`; + `runtime:long-running-process → unsupported`; sagas `rejected`; + `@netscript/queue:consume → unsupported` at binding scope in v1 (CF Queues push-consumer + wiring is leaf territory — a future `@netscript/queue-cf` card, R-GRAPH-4; the binding + transport still declares the queue by name); `@netscript/kv:atomic → unsupported` (Workers + KV no-CAS — capability rejection per L-2). + - `variant: containers` — `tier: deno-native`; `process: long-lived`; + `runtime:long-running-process → lossless`; sagas `supported`-or-`externalized` per the + project's leaf backings (container composition rule, §4). + DO-backed saga/worker stores are explicitly **probe cards owned by the saga/worker leaves**, + not this adapter. v1 scaffold story uses ONE variant per target (topology cells are + user-declared — DP-2 §5, SF-8); the second variant is added as a second declared cell/target. - **Scaffold hooks:** the cloudflare-optimized scaffold story (scaffold-stories §2). - **Permissions:** `--allow-run=wrangler,npx`, `--allow-net`. @@ -130,9 +161,10 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. ## 7. `deploy-aws` (W5; AWS-PROBE-gated, HTTP scope first) — T1 via containers - **Wraps (HTTP scope):** **AWS Lambda Web Adapter** as layer/binary in a `denoland/deno` - container image (built by `deploy-container`), fronted by Function URL/API GW/ALB; - Fargate/App Runner lanes via the container path. Adversarial F1 stands: **LWA is an HTTP - sidecar** — this adapter claims HTTP hosting only until the event-semantics probes pass. + container image (image build via an **injected** core-owned `ContainerBuildPort` — r2, SF-11), + fronted by Function URL/API GW/ALB; Fargate/App Runner lanes via the same injected container + path. Adversarial F1 stands: **LWA is an HTTP sidecar** — this adapter claims HTTP hosting + only until the event-semantics probes pass. - **IaC:** optional `./pulumi` subpath wrapping the **Pulumi Automation API** (Apache-2.0, inline programs, no Pulumi Cloud) for users who want provisioned infra; plain image+existing-infra deploys need no IaC dependency. Serverless Framework v4 excluded @@ -140,11 +172,14 @@ non-obvious rows), scaffold hooks, permissions, and probe gates. - **Ops:** plan (emit image + optional IaC program)/up/down/status/logs (CloudWatch); `secrets` via SSM/Secrets Manager references; `rollback` via Lambda versions/App Runner revisions. -- **Manifest:** HTTP rows only in v1; `queue-consume: unsupported` with note "SQS backing is - `@netscript/queue` leaf territory (`ReportBatchItemFailures`, per-record ack, visibility - timeout — see AWS-PROBE-EVENTS card)"; sagas `externalized` (or `supported` on Fargate - long-lived). CI auth: GitHub OIDC → `AssumeRoleWithWebIdentity` (no static keys) — scaffold - emits the role-trust snippet. +- **Manifests (r2, SF-7 — two variants):** + - `variant: lambda` — `process: bounded-window`; HTTP rows only in v1; sagas `externalized`; + `@netscript/queue:consume → unverified` at binding scope with note "SQS backing is + `@netscript/queue` leaf territory (`ReportBatchItemFailures`, per-record ack, visibility + timeout — see AWS-PROBE-EVENTS card)". + - `variant: fargate` — `process: long-lived`; sagas `supported` per leaf backings; same image. + v1 story uses one variant; the second is a second declared cell (DP-2 §5). CI auth: GitHub + OIDC → `AssumeRoleWithWebIdentity` (no static keys) — scaffold emits the role-trust snippet. - **Probe gates:** AWS-PROBE-HTTP (live LWA conformance) before shipping; AWS-PROBE-EVENTS (SQS conformance design + live probe, leaf-owned) before any event claims. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md index ead921cb0..7b59c5705 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md @@ -1,6 +1,8 @@ # DP-4 — `plugins/deploy` and the plugin-host extensions > **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`. +> **r2** — amended per Sol adversarial findings SF-3, SF-4, SF-12, SF-14 (+ quick wins); +> triage in `adversarial-sol-triage.md`. ## 1. The plugin (`@netscript/plugin-deploy`, `plugins/deploy/`) — Archetype 5 @@ -10,39 +12,66 @@ Folder shape (doctrine 06:181-198; contribution folders sibling to `src/`): plugins/deploy/ mod.ts # few lines: re-export public surface deno.json # subpaths: ./public ./plugin ./scaffold ./adapter-cli ./streams ./streams/server - README.md # permissions, target matrix, quick start - verify-plugin.ts # asserts declared axes against the host loader + README.md # per-target permission profiles, target matrix, quick start + verify-plugin.ts # asserts declared axes against the host loader (incl. cliCommands/doctorChecks) scaffold.plugin.json # installer manifest (§3) cli.ts # createPluginAdapter(deployAdapterPlugin).toCli() scaffold.ts # …​.toScaffold() src/ public/mod.ts # definePlugin(...) manifest (§2) adapter/plugin.ts # NetScriptPlugin connector (install/doctor seams) - adapter/resources/ # starter resources: deploy.config barrel + target snippets - composition/ # composition root: registry assembly from installed adapters + adapter/resources/ # starter resources: deploy/targets.ts leaf + target snippets + composition/ # composition root: resolves DeployTargetContribution descriptors streams/ # deploy event stream schema + server producer (§4) ``` -Thinness law: every convention lives in `plugin-deploy-core`; the plugin only **wires** -(composition root builds the multi-target registry from the adapters the project installed), -**declares** (manifest, installer JSON, connector), **contributes** (CLI mount, scaffolder, -streams, telemetry, doctor), and **re-exports** (core types). `mod.ts` stays a few lines -(R-PLUGIN-THIN). +Thinness law: every convention lives in `plugin-deploy-core`; the plugin only **wires**, +**declares**, **contributes**, and **re-exports**. (r2, SF-12) **The plugin depends only on +core** — it never statically imports the official adapter set. Adapters reach the runtime as +descriptors: + +```ts +interface DeployTargetContribution { + readonly key: string; // registry key, e.g. 'deno-deploy' + readonly targetLoader: SafePackageExport; // e.g. { pkg: '@netscript/deploy-deno', export: './target' } + readonly schemaLoader: SafePackageExport; // the target's config member schema (two-phase loader) + readonly permissions: DeployPermissionProfile; // exact tool/net/fs profile for THIS target +} +``` + +`deploy target add ` installs the adapter peer, writes one descriptor into the user-owned / +generated target registry file, and emits the target's scaffold assets. The composition root +resolves descriptors (validated safe loader subpaths), injects shared implementations (e.g. +`deploy-container`'s `ContainerBuildPort`), and populates the duplicate-rejecting registry. +Failure modes are first-class and tested: missing peer, invalid export, duplicate key, +uninstall, stale registry entry. ## 2. Runtime manifest (`definePlugin`) ```ts definePlugin('@netscript/plugin-deploy', DEPLOY_PLUGIN_VERSION) .withDisplayName('Deploy').withType('utility') - .withPermissions(DEPLOY_PLUGIN_PERMISSIONS) // --allow-run=, net, read, write - .withCliCommands([{ group: 'deploy', loader: './adapter-cli' }]) // NEW axis (§5) + .withPermissions(DEPLOY_PLUGIN_BASE_PERMISSIONS) // plugin's own baseline only (read/write + // workspace, net for registries) — NOT the + // union of all provider permissions (SF-12) + .withCliCommands([ // mount-children, NEVER top-level (§5, SF-4) + { mount: 'deploy', id: 'target', loader: './adapter-cli', export: 'targetGroup' }, + { mount: 'deploy', id: 'capabilities', loader: './adapter-cli', export: 'capabilitiesCommand' }, + // eight-op target router children attach under the host-owned 'deploy' shell + ]) + .withDoctorChecks([{ id: 'deploy-target', loader: './src/doctor', export: 'deployTargetCheck' }]) .withStreamTopics([{ name: 'deploy-events', subject: 'deploy' }]) .withTelemetry([{ name: 'deploy', loader: './src/telemetry' }]) - .withRuntimeConfigTopics([{ name: 'deploy' }]) // rollout flags (pause target, freeze) + .withRuntimeConfigTopics([{ name: 'deploy' }]) // rollout flags, checked at each CLI/CI + // invocation (no resident process observes + // changes mid-operation — r2 quick win) .withMetadata({ tier: 'official' }).build(); -// frozen with: contributions.cli.doctorChecks = ['deploy-target'] // NEW union member (§5) ``` +Per-target permissions are **not** aggregated here: each `DeployTargetContribution` carries its +own `DeployPermissionProfile`; `deploy doctor` reports the exact installed set, and the CLI +launcher computes/prints the required profile for the configured targets (SF-12). + **No `withService` in v1** (owner fork OF-4): deploy is CLI/CI-shaped; there is no long-running HTTP surface, no port, `hasRoutes:false`. The machine-readable surface that replaces a `describe` route is the **capability manifest**, exposed as `netscript deploy capabilities [] @@ -51,95 +80,133 @@ route is the **capability manifest**, exposed as `netscript deploy capabilities (`...BASE_PLUGIN_CONTRACT_ROUTES` + `satisfies BasePluginContract`) — the seam cost is one follow-up card, so deferring is rework-safe (plan-gate open-decision sweep). -## 3. Installer manifest (`scaffold.plugin.json`) +## 3. Installer manifest (`scaffold.plugin.json`) — r2, SF-3: protocol-valid + tooling variant -Re-derived, not copied from auth (`research/auth-composition-anatomy.md` §9 item 8): +The r1 sketch was invalid under `PluginInstallerManifest` (missing required `version`, +`displayName`, `description`, `peerDependencies`; boolean `requiredPermissions` where the +protocol requires string arrays; service-shaped `provider`/`officialSource` requirements a +tooling plugin cannot meet). r2 (a) writes the complete manifest and (b) **generalizes the +protocol** for tooling-only plugins as part of the host-extension slices: + +```ts +type PluginManifestOfficialSource = + | ExistingServiceOfficialSource // v1 shape, unchanged (auth et al.) + | { readonly sourceKind: 'tooling'; + readonly canonicalName: string; + readonly pluginDir?: string }; +``` ```jsonc { "schemaVersion": 1, "name": "@netscript/plugin-deploy", - "capabilities": { "hasDatabaseMigrations": false, "hasRoutes": false, - "hasBackgroundWorkers": false, "contributesDeployTargets": true }, // NEW flag (§5) - "scaffolder": { "export": "./scaffold", - "requiredPermissions": { "net": true, "read": true, "write": true } }, - "provider": { "kind": "deploy", "pluginType": "utility", "infrastructureRequires": [] }, - "officialSource": { "canonicalName": "deploy", "pluginDir": "deploy" } + "version": "", + "displayName": "Deploy", + "description": "Deploy targets, capability verdicts, and per-provider scaffolds for NetScript workspaces.", + "peerDependencies": { "@netscript/plugin-deploy-core": "" }, + "capabilities": { + "hasDatabaseMigrations": false, + "hasRoutes": false, + "hasBackgroundWorkers": false, + "contributionAxes": ["cli-command", "doctor-check", "stream-topic", "telemetry", "runtime-config-topic"] + }, + "scaffolder": { + "export": "./scaffold", + "requiredPermissions": { "net": ["jsr.io"], "read": [""], "write": [""] } + }, + "officialSource": { "sourceKind": "tooling", "canonicalName": "deploy", "pluginDir": "deploy" } } ``` -No `.prisma`, no service port, no port-range key, no db/kv requirement — the deploy plugin runs -at build/CI time and in the developer loop, not as a resident service. - -Peer model: **adapters are peerDependencies-style install choices.** `netscript plugin install -deploy` installs plugin + core; `netscript deploy target add ` (new verb, §6) adds the -adapter package import + its config member + scaffold assets for that target. This is the -multi-target inversion of auth's single-active env selection: targets are *added*, not switched. +(r2, SF-14) There is **no `contributesDeployTargets` boolean**: the installer infers deploy +participation from the declared `contributionAxes` / adapter descriptors — no deploy-specific +field enters the generic protocol. Schema-parse, official copy/install, and +backward-compatibility fixtures (v1 service manifests keep parsing) ship with the protocol +slice. Adapter packages are peer install choices driven by `target add` (§1), not manifest +dependencies. ## 4. Contributions beyond CLI - **Streams**: `deploy-events` durable stream — versioned envelope - `deploy.started/succeeded/failed/rolled-back` with target key, environment, artifact digest, - actor, trace context (W3C propagation like auth's producer). Core deploy success **never - depends on the stream sink** (audit-provider independence — board-parity lesson 8). -- **Telemetry**: span vocabulary for plan/up/rollback, redaction rules (secrets never in spans — - the secrets convention's redaction applied at the telemetry seam). -- **Runtime-config topic `deploy`**: operational flags (freeze deploys, pause a target) readable - without redeploy via `@netscript/runtime-config` — deliberately tiny. -- **Scaffolder**: emits the userland `deploy/` leaf — `deploy/targets.ts` (typed target + `deploy.started/succeeded/failed/rolled-back` with target key + variant, environment, artifact + digest, actor, trace context (W3C propagation like auth's producer). Core deploy success + **never depends on the stream sink** (audit-provider independence). +- **Telemetry**: span vocabulary for plan/emit/up/rollback, redaction rules (secrets never in + spans — the secrets convention's redaction applied at the telemetry seam). +- **Runtime-config topic `deploy`**: operational flags (freeze deploys, pause a target) read at + each CLI/CI invocation — deliberately tiny. +- **Scaffolder**: emits the userland `deploy/` leaf — `deploy/targets.ts` (typed target + cell definitions the user owns and edits — the named inversion of auth's never-rewritten barrel) - plus per-target assets on `target add` (wrangler.jsonc, fly.toml, workflows…; scaffold-stories - doc). Golden tests per emitter (R-PLUGIN-PARITY). -- **Doctor**: `deploy-target` check — validates each configured target: adapter installed, - required tool on PATH (`wrangler`, `aspire`, `docker`…), credentials env present (names only, - never values), config member parses, capability verdict for the project graph. - -## 5. Host extensions (`@netscript/plugin` — named, small, reviewed) - -Three host changes, each its own slice (they precede W3): - -1. **CLI-command contribution axis** (NEW): `CONTRIBUTION_AXES` + `'cli-command'`, - `CliCommandContribution { group: string; loader: string }`, `PluginCliCommandContribution` - abstract, `withCliCommands` builder verb. The registry emitter renders a generated CLI mount - module; `packages/cli` mounts contributed groups after built-ins at startup (declarative - `CliRoot` composition preserved — R-A6-N5). Collision rule: a contributed group may not - shadow a built-in group name except the deploy back-compat shim (§6). -2. **Doctor-check union widening**: `cli.doctorChecks` type from `readonly 'auth-backend'[]` to a - declared union `readonly ('auth-backend' | 'deploy-target')[]` — still closed (typed registry, - AP-24-safe), one PR whenever a plugin adds a check kind. -3. **Capability flag**: `PluginManifestCapabilities.contributesDeployTargets?: boolean` so the - installer/registry can reason about deploy plugins statically (parallel to - `supportsMcpScaffold`). + plus per-target assets on `target add` (wrangler.jsonc, fly.toml, workflows…; + scaffold-stories doc). Golden tests per emitter (R-PLUGIN-PARITY). +- **Doctor** (`deploy-target` check, contributed as data — §5): validates each configured + target: adapter peer installed, descriptor loader resolves, required tool on PATH + (`wrangler`, `aspire`, `docker`…), credentials env present (names only, never values), config + member parses, capability verdict for the project graph. Output distinguishes + `unsupported | unverified | adapter-not-installed | credential-unavailable` (r2 quick win). + +## 5. Host extensions (`@netscript/plugin` + CLI bootstrap) — r2, SF-4/SF-14: generic, data-driven + +Three slices, each independently gated (they precede W3). The r1 shapes (a widened doctor +literal-union, a deploy-specific capability boolean, a shadowing CLI axis) repeated the +closed-host edit pattern and had no viable discovery-to-startup path; r2 replaces them: + +1. **CLI mount-children contribution + async bootstrap** (SF-4). Contribution type + `CliCommandContribution { mount: string; id: string; loader: string; export: string }` + + `withCliCommands` builder verb + `PluginCliCommandContribution` abstract. **Host-owned mount + points**: the built-in `deploy` shell stays reserved and owns `desktop`, the absent-plugin + install hint, and shared help; contributions attach as **children under a mount** and can + never shadow a top-level command — no plugin-specific exception exists. CLI bootstrap becomes + async: resolve plugin manifests (the **manifest loader**, not the AST source walker, feeds + this registry), validate safe loader subpaths, register built-ins, attach contributed + children, then `program()`. Duplicate `(mount, id)` pairs fail before parsing and report both + plugin owners; loader failures are isolated (one broken plugin cannot take down the CLI). + Slices: contribution contract/builder/merger/verifier; async bootstrap + collision rules; + help rendering + plugin-absent behavior. +2. **Doctor-check contributions as data** (SF-14). Replace the hard-coded + `cli.doctorChecks: readonly 'auth-backend'[]` literal union with + `DoctorCheckContribution { id: string; loader: SafePackageExport }` resolved through a + duplicate-guarded registry (auth migrates to `{ id: 'auth-backend', … }` compatibly). Closed + unions remain reserved for host-executed protocols whose cases have genuinely different host + semantics — not plugin identities. Loader isolation, duplicate-id, and failure-reporting + tests. +3. **Installer-protocol generalization** (SF-3/SF-14). The `sourceKind: 'tooling'` + `officialSource` variant + `capabilities.contributionAxes` (§3), accepting v1 service + manifests concurrently; parse/copy/compat fixtures. **Frontend axis is out of scope here** — the parallel seed run (`plan/frontend-contrib`) owns the `frontend` contribution axis design; this run only asserts the deploy plugin will *use* that axis when it lands (deploy status surfaces in the dev dashboard) and must not design it. -## 6. CLI delivery and back-compat (owner fork OF-3) +## 6. CLI delivery and back-compat (owner fork OF-3 — r2 resolution per SF-4) - **W1–W2 (pre-plugin):** `packages/cli` deploy group re-wired over `plugin-deploy-core` + extracted adapters. Zero verb changes; `deno task e2e:cli` is the invariant. -- **W3 (plugin-mounted):** the deploy group ships as the plugin's `cli-command` contribution. In - a scaffolded project the group appears when the plugin is installed. **The framework CLI keeps - a thin built-in shim**: if the plugin is absent, `netscript deploy` prints the install hint; - if present, the shim defers to the contributed group. This preserves the documented - `netscript deploy …` UX for every existing project while making the plugin the owner - (recommended resolution of OF-3). -- **New verbs** landing with the plugin: `deploy target add|remove ` (adapter + config + - scaffold assets), `deploy capabilities [] --json`, `deploy doctor` (alias of - `plugin doctor` scoped to deploy). Legacy flat verbs alias to `baremetal` ops with a two-release - deprecation notice (DP-2 §2). +- **W3 (plugin-mounted):** the host-owned `deploy` shell remains built-in (reserved mount, + `desktop`, help, install hint when the plugin is absent); the plugin contributes the + **children** — `target add/remove`, `capabilities`, the eight-op target router — under that + mount. Ownership is unambiguous: shell = host, behavior = plugin. This preserves the + documented `netscript deploy …` UX for every existing project while making the plugin the + behavior owner. +- **Legacy flat verbs** (SF-9): first-class compatibility handlers owned by `deploy-baremetal` + through the next semver-major (DP-2 §2) — routed through the shell, not aliased onto + `up`/`down`. +- **New verbs** landing with the plugin: `deploy target add|remove ` (peer install + + descriptor + config member + scaffold assets), `deploy capabilities [] --json`, + `deploy doctor` (alias of `plugin doctor` scoped to deploy). ## 7. Acceptance (plugin Concept of Done, per doctrine 11 parity checklist) -- `verify-plugin.ts` green (axes: cli-command group, stream topic, telemetry, runtime-config - topic, doctor check). +- `verify-plugin.ts` green — backed by **new host `PluginExpectations.cliCommands` and + `PluginExpectations.doctorChecks` surfaces** (SF-4): axes verified = cli mount-children, + doctor check, stream topic, telemetry, runtime-config topic. - Golden test per scaffold emitter; `plugin doctor` covers required-config + tool-missing + - credential-missing paths; a registered `scaffold.runtime` e2e case exercising - `plugin install deploy` → `deploy target add deno-deploy` → `deploy deno-deploy plan` on the - generated workspace. + credential-missing + adapter-not-installed paths; a registered `scaffold.runtime` e2e case + exercising `plugin install deploy` → `deploy target add deno-deploy` → `deploy deno-deploy + plan` on the generated workspace (depends on the target-add CLI slice — board dependency + corrected per SF-15). - Contract-soundness: n/a in v1 (no service contract); the compensating check is a schema test that `deploy capabilities --json` output validates against the published - `DeployCapabilityManifest` schema. + `DeployCapabilityManifest` schema (incl. `schemaVersion`). - Quality: `quality:scan`, `arch:check`, `deno doc --lint`, publish dry-run, jsr-audit — "thin ≠ thin quality budget". diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md index 91e42dbe1..388569471 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md @@ -11,16 +11,16 @@ | # | Shipped item (owner today) | Destination | Wave | Kind | | --- | --- | --- | --- | --- | | M-1 | `DeployTargetPort` + `DeployOperation` (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts`) | `plugin-deploy-core/ports` | W1 | Move + sharpen (capability field) | -| M-2 | Deploy target registry + `DEFAULT_DEPLOY_TARGETS` (`kernel/application/registries/deploy-target-registry.ts`, registry port) | `plugin-deploy-core/registry` | W1 | Move (closed-on-key preserved) | -| M-3 | Conventions: `activation/secrets/observability/rollback-convention.ts`, `health-gate.ts`, `runtime-overrides.ts` | `plugin-deploy-core/conventions` | W1 | Move verbatim (R-DEPLOY-3) | -| M-4 | Compile/build engine (`kernel/adapters/deploy/compile/*`, domain `compile-target.ts`, `build/build-deploy.ts` pipeline core) | `plugin-deploy-core/build` | W1 | Move; CLI keeps the presentation wrapper | -| M-5 | `DeployTargetBaseSchema` + target-map (`packages/config/src/domain/schemas/deploy-schema.ts`, types `config-section-types.ts:357-592`) | Base + schema registry → `plugin-deploy-core/config`; per-target members → their adapter packages | W1 (base) / W2 (members) | Move; retires the deploy slice of `config-plugin-specific-schema-debt`; `@netscript/config` keeps the loader seam and a re-export for one deprecation window | -| M-6 | Windows/Linux service targets, Servy/systemd adapters, `servy-config.ts`, OS-service factory | `@netscript/deploy-baremetal` | W2 | Move; compose ports publicly (closes `DEPLOY-BAREMETAL-PUBLIC-WIRING` — the 7-op set advertised at runtime) | +| M-2 | Deploy target registry (`kernel/application/registries/deploy-target-registry.ts`, registry port) | `plugin-deploy-core/registry` — **empty registry + port + key/error types only** (r2, SF-1); adapters export target factories; `DEFAULT_DEPLOY_TARGETS` is deleted as a core concept — its compatibility equivalent stays in the CLI composition root in W1, replaced by descriptor composition from W3 | W1 | Extract + **NEW behavior**: duplicate rejection (`DeployTargetCollisionError`) — the shipped registry is register-or-replace (r2, SF-13); composition-root-only `replaceForCompatibility` for the W1 shim | +| M-3 | Conventions: `activation/secrets/observability/rollback-convention.ts`, `health-gate.ts` | `plugin-deploy-core/conventions` — the demonstrably pure policies move *with their constants*; **`runtime-overrides.ts` stays with its bare-metal/leaf owners** (it duplicates leaf job/saga/task vocabulary and describes `.deploy/windows` — r2, SF-2) | W1 | Move (R-DEPLOY-3) | +| M-4 | Compile/build engine (`kernel/adapters/deploy/compile/*`, domain `compile-target.ts`, `build/build-deploy.ts` pipeline) | (r2, SF-2) **Refactor then extract**: the pipeline imports CLI `ResolvedConfig`, CLI output, and Windows manifest/V8 modules — it is not a verbatim move. W2 moves the current Windows/Linux build behavior to `deploy-baremetal`; an adapter-neutral compile emitter graduates to `plugin-deploy-core/build` only after filesystem/process/output/config ports exist | W2 (behavior) / later (core emitter) | Refactor then extract | +| M-5 | `DeployTargetBaseSchema` + target-map (`packages/config/src/domain/schemas/deploy-schema.ts`, types `config-section-types.ts:357-592`) | Base + schema registry → `plugin-deploy-core/config` over the **two-phase loader** (r2, SF-10: bootstrap-parse without stripping → resolve adapter `schemaLoader`s → compose → full parse; unknown target ⇒ `DeployTargetAdapterMissingError`, never silently dropped); per-target members → their adapter packages; `@netscript/config` keeps the loader seam + a frozen delegating legacy union for the compat window | W1 (base+loader) / W2 (members) | Move + **NEW loader phase**; retires the deploy slice of `config-plugin-specific-schema-debt` | +| M-6 | Windows/Linux service targets, Servy/systemd adapters, `servy-config.ts`, OS-service factory | `@netscript/deploy-baremetal` | W2 | Move; compose ports publicly (closes `DEPLOY-BAREMETAL-PUBLIC-WIRING` — the full declared op set advertised at runtime) | | M-7 | `AspireComposeDeployTarget` + `AspireCloudDeployTarget` (`kernel/adapters/aspire/*`) | `@netscript/deploy-aspire` | W2 | Move; platform-marker validation intact | | M-8 | Deno Deploy target + CLI port + unstable-API guard (`domain/deploy/deno-deploy-*`, `kernel/adapters/deno-deploy/*`) | `@netscript/deploy-deno` | W2 | Move; transitive-scan improvement is a named follow-up card | | M-9 | cloud-run docker lane (inside aspire-cloud adapter) | `deploy-container` (+`./cloudrun` client) | W4 | Re-home (micro-decision at W4; until then stays in `deploy-aspire` unchanged) | | M-10 | `deploy` CLI group + all verbs (`public/features/deploy/**`) | W1–W2: same group re-wired over core/adapters; W3: plugin-contributed group + built-in shim (DP-4 §6) | W1→W3 | Rewire, then re-home | -| M-11 | Legacy flat verbs `build/install/start/stop/status/logs/upgrade/uninstall/copy/package-cli` | Aliases onto `baremetal` target ops; deprecation notice two minor releases, then removal | W2 announce / W5 remove | Deprecate (resolves S12/#348 convergence; stale "Windows Service" group description fixed in W1) | +| M-11 | Legacy flat verbs `build/install/start/stop/status/logs/upgrade/uninstall/copy/package-cli` | (r2, SF-9 — semantics are NOT `up`/`down`-equivalent: shipped `start`/`stop` act on registered services without install/uninstall; `copy` syncs artifacts without registration; `upgrade` is a five-step transaction; `package-cli` builds an operator binary) **First-class compatibility handlers owned by `deploy-baremetal`** (`BaremetalCompatibilityCommands`) preserving current flags and side-effect boundaries **through the next semver-major**. Only `build → plan+emit`, `status`, `logs` are direct aliases. Help deprecates; no minor-release removal date until an equivalent canonical workflow + migration telemetry exist. Golden help/exit-code + state-transition tests (`stop` never uninstalls; `start` never registers) | W2 (handlers) / next semver-major (removal) | Compat handlers (resolves S12/#348; stale "Windows Service" group description fixed in W1) | | M-12 | Scaffold deploy emission: 3 workflow templates, `netscript.config.ts` `deploy:{}`, appsettings model | Workflow templates move to their owning adapters' scaffold assets; `deploy:{}` gains the plugin-era schema (targets added via `deploy target add`); appsettings model unchanged (it is the logical graph — L-4) | W3 | Re-home + extend | | M-13 | Aspire scaffold bundle (`apphost.mts`, `.helpers`, aspire.config.json…) | Unchanged — `@netscript/aspire` substrate + CLI templates stay; the deploy plugin consumes, never owns, AppHost emission | — | Keep | | M-14 | `packages/aspire` | Unchanged (composer/diagnostics substrate) | — | Keep | @@ -33,9 +33,9 @@ | Debt entry | Effect | | --- | --- | -| `DEPLOY-ARCHETYPE-7-CORE-SEED` | **Retired by W1** (core package exists, conventions+registry owned, F-DEPLOY-1/2 → `gated`) | +| `DEPLOY-ARCHETYPE-7-CORE-SEED` | **Retired by W1, conditional** (r2, SF-1): core package exists, pure conventions + empty registry owned, F-DEPLOY-1/2 → `gated`, **and** the externalized composition root (defaults in the CLI root, not core) is in place — the retirement claim is not made on a verbatim `DEFAULT_DEPLOY_TARGETS` move | | `DEPLOY-SECRETS-ROLLBACK-CORE` | **Retired by W1/W2** (adapters delegate to core conventions) | -| `DEPLOY-BAREMETAL-PUBLIC-WIRING` | **Retired by W2** (production composition root wires ports; 7-op advertised) | +| `DEPLOY-BAREMETAL-PUBLIC-WIRING` | **Retired by W2** (production composition root wires ports; full declared op set advertised) | | `cli-deploy-artifacts-missing` | **Partially retired W4** (container lane emits Dockerfile/compose); Helm/K8s hand-artifacts remain delegated to `aspire publish` (unchanged claim) | | `DEPLOY-S7-APPHOST-COMPOSE-GEN` | Superseded-in-place: the shared compose-gen primitive becomes `deploy-container` emission; the Aspire `addExecutable` constraint documented in its card | | `cli-deploy-linux-integration-untested` | Converted into a live-probe suite cell of `deploy-baremetal` (W2) | @@ -46,10 +46,14 @@ ## 3. Compatibility contract (what users experience) 1. **Config keys survive**: `deploy.targets.windows|linux|docker|compose|deno-deploy|kubernetes| - azure-*|cloud-run` parse identically through W1–W3 (schema re-home is transparent; one - deprecation window for import-path movers). + azure-*|cloud-run` parse identically through W1–W3 (schema re-home is transparent; legacy + types stay exported from `@netscript/config` as a frozen delegating union for the compat + window). One deliberate, documented behavior change (r2, SF-10): an **unknown** target key + becomes `DeployTargetAdapterMissingError` instead of today's silent strip — surfacing + misconfiguration is the point. 2. **Verbs survive**: every documented `netscript deploy …` invocation works unchanged through - W3; legacy flat verbs warn from W2, removed after two minor releases (M-11). + W3 and beyond; legacy flat verbs keep their exact current semantics as compatibility handlers + until the next semver-major (M-11, r2). 3. **Scaffolded projects**: existing projects keep working without the plugin (shim path); `plugin install deploy` is additive. New scaffolds from W3 get the plugin preinstalled with the `deno-deploy` default target (T1 default, DP-3 §3). diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md index 8813a161a..dad13a589 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-7-contribution-matrix.md @@ -7,7 +7,7 @@ | Layer | Contribution (mechanism) | Cloud-agnostic core part | Provider-optimized part | | --- | --- | --- | --- | -| **CLI** | `cli-command` axis (DP-4 §5): the `deploy` group; `deploy target add/remove`, `deploy capabilities --json`, 7-op target verbs; doctor check `deploy-target` | Verb grammar, registry routing, capability rendering | Each adapter surfaces its target key + config member; doctor validates its tool/credential prerequisites | +| **CLI** | `cli-command` axis (DP-4 §5): the `deploy` group; `deploy target add/remove`, `deploy capabilities --json`, eight-op target verbs; doctor check `deploy-target` | Verb grammar, registry routing, capability rendering | Each adapter surfaces its target key + config member; doctor validates its tool/credential prerequisites | | **Services runtime** | None resident in v1 (OF-4). Lifecycle contract only: artifacts drive services through `ServiceShutdownCoordinator` (L-5) | Hostable build/start/stop semantics identical across targets | Entry emission differs per target (deno process, worker entry, LWA container) but never changes app code | | **Scaffolding** | Plugin scaffolder (DP-4 §4): userland `deploy/targets.ts` leaf + per-target assets on `target add`; provider-optimized project templates (DP-8) | The leaf file, the logical graph (appsettings) untouched by target choice | wrangler.jsonc / fly.toml / `.vercel` config / workflows / OIDC snippets; leaf-backing selection (below) | | **Config** | `deploy.*` schema (base in core, members with adapters); environments overlay; runtime-config topic `deploy` (freeze/pause flags) | Base shape + conventions sub-blocks | Target member schemas spread the base + vendor fields only (R-DEPLOY-4) | @@ -29,7 +29,9 @@ The deploy plugin **selects but never owns** leaf backings (R-GRAPH-4). At `targ for each `LeafBindingKind`, the recommended provider backing *package* (a leaf-owned adapter, e.g. `@netscript/kv-cloudflare`) or an `external` strategy, with its capability caveats. 3. Emits config + env wiring for the chosen backings and runs the **capability compiler** so an - impossible combination fails at scaffold time, not deploy time (L-3). + impossible combination fails at scaffold time, not deploy time (L-3). (r2, SF-10) Backing + config members parse through the **two-phase loader** (DP-2 §6) — the catalog mechanism + depends on that slice, not on a loader seam that does not exist yet. Catalog sketches (each row exists only when the leaf-owned adapter exists — otherwise the catalog row says `external` and the scaffold wires a URL-based binding): @@ -48,8 +50,15 @@ reversible (swap the backing package + config; the logical graph is untouched). ## 3. Credibility invariants -1. A provider-optimized scaffold **must not** fork app code per provider — only config, bindings, - emitted artifacts, and backing package selection differ (L-4 one-graph rule). +1. (r2, SF-16 — scoped to be enforceable) A provider-optimized scaffold **must not** fork app + code per provider **within the declared runtime profile**: the canonical fixture, constrained + to its profile (`deno-native` / `web-standard` / `node-compat`), uses the same domain/service + source across targets; only generated entry modules, config, bindings, emitted artifacts, + workflows, and backing packages differ (L-4 one-graph rule). For arbitrary projects, `plan` + performs dependency/API compatibility analysis and rejects unsupported runtime touchpoints + (`Deno.*` on an isolate target, native addons, fs/process APIs) with file-level diagnostics — + a generated Fetch entry adapts the framework boundary, it does not make arbitrary code + portable. 2. Every optimization is **capability-manifest-visible** — `deploy capabilities` shows exactly what the chosen target/backings support, with notes (backend-truthful, auth-S1). 3. Anything the catalog cannot honestly back is scaffolded as `external` with a named env diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md index eeb57b0cd..f882a7e86 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md @@ -8,8 +8,8 @@ ## Story 0 — The Deno-native default (baseline every story diffs against) ``` -netscript init my-app # deploy plugin preinstalled from W3 -netscript deploy target add deno-deploy +netscript init my-app # deploy plugin preinstalled from W3; NO target preinstalled +netscript deploy target add deno-deploy # the one flow — targets are always added explicitly ``` - **Lands:** `deploy/targets.ts` (user-owned leaf: `defineDeployTargets({ 'deno-deploy': {...} })`), @@ -35,10 +35,13 @@ netscript deploy target add deno-deploy queue → CF Queues binding declared by name, consumption gated on the leaf probe card (`@netscript/queue-cf`); db → D1 or Hyperdrive→Postgres; static assets → Workers Static Assets (lossless). -- **Compute split:** HTTP + stateless services → Workers; anything requiring - `long-running-process` (sagas `supported`, exclusive DB writer, owned queue `listen()` loops) - → **Containers lane** (same image as Story 3's container path) — the compiler proposes the - macro-service split rather than silently degrading (L-3, sagas law). +- **Compute (r2, SF-8 — one variant per story until the topology slice lands):** this story + targets the **`workers` variant only**. An app whose requirements exceed the isolate profile + (sagas, exclusive DB writer, owned queue `listen()` loops) gets a **rejection with + machine-readable `suggestedCells`** proposing a second, user-declared cell on the + `containers` variant (`deploy/targets.ts` — DP-2 §5); the compiler never partitions silently + (L-3, sagas law). The Containers cell is documented as a manual second target sharing Story + 3's image path. - **Dev loop:** `aspire start` for the graph + `wrangler dev` for worker-path fidelity, with the documented Miniflare caveat (simulator, not oracle — remote-binding smoke advised before first deploy; CF-PROBE evidence backs the wording). @@ -55,9 +58,10 @@ netscript deploy target add deno-deploy - **Backings:** queue → SQS **by name** (consumption via `@netscript/queue-sqs` leaf card once proven — AWS-PROBE-EVENTS; until then `external` contract); kv → DynamoDB leaf card; cache → ElastiCache endpoint env; db → RDS/Aurora URL. -- **Compute:** HTTP → Lambda (LWA container, Function URL/API GW); long-lived (sagas - `supported`) → Fargate service from the same image — the image portability is the point - (research §3). +- **Compute (r2, SF-8):** this story targets the **`lambda` variant only** (LWA container, + Function URL/API GW). Long-lived requirements (sagas `supported`) are a **user-declared second + cell** on the `fargate` variant from the same image — proposed via `suggestedCells`, never + auto-partitioned; image portability is what makes the second cell cheap (research §3). - **Deploy:** `deploy aws plan` (image + optional Pulumi inline-program preview) → `up`; rollback via Lambda versions / service revisions; secrets via SSM/Secrets Manager references. - **Caveats rendered:** event/queue semantics not claimed until the leaf probe passes @@ -90,11 +94,16 @@ netscript deploy target add deno-deploy ## Cross-story acceptance (feeds the board's scaffold issue) -- gate: the same app source tree scaffolds and passes `deploy plan` for every story - with zero app-code diffs (only `deploy/`, config, emitted artifacts, workflows differ). -- gate: every story's first `plan` runs the capability compiler and renders the target's honest - caveats; an app requiring an `unsupported` capability fails scaffold-time selection with the - macro-service or external-binding proposal. +- gate (r2, SF-16): the **canonical fixture, constrained to each story's declared runtime + profile**, scaffolds and passes `deploy plan` for every story with the same + domain/service source — only generated entry modules, `deploy/`, config, bindings, emitted + artifacts, workflows, and backing packages differ. For arbitrary projects, `plan` performs + dependency/API compatibility analysis and rejects unsupported runtime touchpoints with + file-level diagnostics. +- gate (r2, SF-8): every story's first `plan` runs the capability compiler and renders the + target variant's honest caveats; an app requiring an `unsupported` capability is **rejected + with `suggestedCells` / external-binding proposals** — the compiler never silently partitions + or degrades. - gate: golden tests cover every emitted artifact per story; emitted workflows reference only documented secrets/OIDC contracts. - gate: `scaffold.runtime` E2E covers Story 0 end-to-end; Stories 1–4 covered by emission golden diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md index 917e445e2..a4b7421f1 100644 --- a/.llm/runs/plan-deploy-plugin--seed/drift.md +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -6,3 +6,5 @@ Append-only. Severity: `note` | `significant` | `architectural`. | --- | --- | --- | --- | --- | | D-1 | 2026-07-18 | note | `workflow/seed-run.md` stage A requires a draft PR as commit trail; kickoff stop-line forbids creating any PR. | Kickoff wins (owner-authored, verbatim, in force). Commit trail = branch `plan/deploy-plugin` pushed with explicit refspec. | | D-2 | 2026-07-18 | note | Seed-run stages F (adversarial) and G (PLAN-EVAL) are replaced by a kickoff-defined pipeline: supervisor-dispatched Sol-xhigh constructive adversarial + Kimi-K3 doc-story, then generator resume. Formal PLAN-EVAL timing is a supervisor decision downstream. | Recorded; this session generates and stops at `STAGE-COMPLETE: generator`. | +| D-3 | 2026-07-19 | significant | Sol adversarial pass (SF-1…SF-16) overturned five r1 design seams: verb lifecycle (7-op `plan`-subsumes-emit → eight-op with pure `plan`), capability vocabulary (flat closed union → structural namespaced refs with scoped verdicts), host extensions (union-widening + deploy flag → data-driven contributions + tooling protocol variant), W1 shape (verbatim move → refactor-then-extract), and registry claims (closed-on-key "preserved" → duplicate rejection is NEW). | All integrated as corpus r2 (`adversarial-sol-triage.md`); board recut to 29 `DPB-n` children. The adversarial stage worked as designed — no escalation. | +| D-4 | 2026-07-19 | note | Kickoff names "Kimi K3" for stage 3; `.llm/tools/agentic/config/models.ts` only binds `visionEval: openrouter/moonshotai/kimi-k2.6`. Stage-3 dispatch passes the K3 OpenRouter slug explicitly to the OpenCode launcher (owner-directed model; config update left to a framework slice — this run cannot edit repo code). | Slug verified against the public OpenRouter model list before dispatch; recorded below in worklog. | diff --git a/.llm/runs/plan-deploy-plugin--seed/plan.md b/.llm/runs/plan-deploy-plugin--seed/plan.md index d1debe204..e50ab96f3 100644 --- a/.llm/runs/plan-deploy-plugin--seed/plan.md +++ b/.llm/runs/plan-deploy-plugin--seed/plan.md @@ -1,9 +1,10 @@ # Plan — plan-deploy-plugin--seed -> **Draft — no GitHub mutation.** Generator-stage plan (Fable 5 · xhigh). Pending: supervisor- -> dispatched Sol-xhigh constructive adversarial pass, Kimi-K3 doc-driven story pass, generator -> integration, then (supervisor decision) formal PLAN-EVAL and owner ratification. Nothing below -> files, closes, or edits any issue/PR/milestone/label. +> **Draft — no GitHub mutation.** Generator-stage plan (Fable 5 · xhigh), **revision r2**: the +> Sol-xhigh constructive adversarial pass (`adversarial-sol.md`, SF-1…SF-16) is integrated — +> all sixteen findings accepted (`adversarial-sol-triage.md`). Pending: Kimi-K3 doc-driven story +> pass, generator integration, then (supervisor decision) formal PLAN-EVAL and owner +> ratification. Nothing below files, closes, or edits any issue/PR/milestone/label. ## 1. Scope statement @@ -25,15 +26,15 @@ AP-11, AP-13/14/16/19, AP-22–25; F-DEPLOY-1/2 flip to `gated`). | # | Decision | Where argued | | --- | --- | --- | | LD-1 | Family topology: `plugins/deploy` (A5) + `plugin-deploy-core` (A2) + adapters `deploy-aspire`, `deploy-baremetal`, `deploy-deno`, `deploy-container` (+ thin PaaS subpaths), probe-gated `deploy-cloudflare` / `deploy-vercel` / `deploy-aws` | DP-1 §1 | -| LD-2 | Dependency law R-GRAPH-1…5 (leafward types only; adapters→core+one provider; plugin composes; leaf backings leaf-owned; CLI = presentation) | DP-1 §2 | -| LD-3 | 7-op verb vocabulary ` ` locked; `plan` subsumes emit; legacy flat verbs = deprecated bare-metal aliases | DP-2 §2 | -| LD-4 | Capability manifest + build-time rejection compiler in core; backend-truthful; sagas tri-state | DP-2 §4 | -| LD-5 | Bindings are declarative name+kind transport; deploy never resolves leaf semantics | DP-2 §5 | -| LD-6 | Multi-target registry (auth's single-active inverted); environments overlay in config | DP-2 §6 | +| LD-2 | Dependency law R-GRAPH-1…5 r2: core imports **no leaf package** (structural capability contracts); **no `deploy-*` imports another `deploy-*`** (`ContainerBuildPort` by injection); the plugin depends only on core and composes **descriptors**; leaf backings leaf-owned; CLI = presentation | DP-1 §2 | +| LD-3 | **Eight-op** lifecycle ` ` locked: `plan` pure (serializable `DeploymentPlan`), `emit` materializes artifacts + provenance, `up --prebuilt` consumes them; legacy flat verbs = first-class compat handlers owned by `deploy-baremetal` until next semver-major (only build/status/logs alias) | DP-2 §2 | +| LD-4 | Capability contracts are structural (`CapabilityRef` namespaced+versioned, `BindingRequirement`, `WorkloadConstraint`); verdicts carry `scope` + `level` (incl. `unverified`) + evidence; manifests are per target-**variant**; build-time rejection compiler; backend-truthful; sagas tri-state; `lossless` requires a live cell | DP-2 §4 | +| LD-5 | Bindings are declarative name+kind transport; deploy never resolves leaf semantics. Topology: `DeploymentCell`/`DeploymentTopologyPlan`; v1 cells are **user-declared**; the compiler returns `suggestedCells` but rejects, never partitions silently | DP-2 §5 | +| LD-6 | Multi-target duplicate-rejecting registry (**empty in core** — no `DEFAULT_DEPLOY_TARGETS`; rejection is NEW behavior, `DeployTargetCollisionError`); environments overlay; **two-phase config loader** (unknown target ⇒ error, never silently stripped) | DP-2 §6 | | LD-7 | Wrap map: wrangler / Build Output API / `deno deploy` / Machines+PaaS REST / LWA / Pulumi Automation API; Serverless v4 rejected; Nitro reference-only with conditional re-entry | DP-5 | | LD-8 | No resident service/contract in v1; capability manifest + CLI JSON is the machine surface | DP-4 §2 | -| LD-9 | Three named host extensions: `cli-command` axis, doctor-union widening, `contributesDeployTargets` flag | DP-4 §5 | -| LD-10 | Behavior-preserving extraction first; e2e:cli invariant; compatibility contract (config keys + verbs survive; Aspire unchanged in behavior) | DP-6 | +| LD-9 | Three named host extensions, generic and data-driven: CLI **mount-children** contribution + async bootstrap (host-owned reserved mounts, no shadowing, duplicate `(mount,id)` fails); doctor-check contributions as data (`{id, loader}` registry — no union widening); installer-protocol `sourceKind:'tooling'` variant + `capabilities.contributionAxes` (no deploy-specific flag) | DP-4 §5 | +| LD-10 | **Refactor-then-extract** (W1 sub-sliced; build engine moves W2 to `deploy-baremetal`; `runtime-overrides.ts` stays leaf-side); e2e:cli invariant per sub-slice; compatibility contract (config keys + verbs survive with exact semantics; unknown-target error is the one documented change; Aspire unchanged in behavior) | DP-2, DP-6 | | LD-11 | Provider-optimized scaffolds select leaf backings via per-target catalogs; app code never forks per provider | DP-7/DP-8 | | LD-12 | Desktop packaging out of family (epic #830 boundary) | DP-3 §8, DP-6 M-16 | @@ -43,9 +44,9 @@ AP-11, AP-13/14/16/19, AP-22–25; F-DEPLOY-1/2 flip to `gated`). | --- | --- | --- | --- | | OF-1 | Core package name | `@netscript/plugin-deploy-core` (auth parity; DP-1 §1) | `@netscript/deploy-core` (kickoff literal) | | OF-2 | Thin PaaS packaging | Subpaths of `deploy-container`; graduate on SDK need (DP-1 §1) | One package per platform from day one | -| OF-3 | CLI delivery | Plugin-contributed group + built-in shim (DP-4 §6) | Group stays built-in; plugin only registers targets | +| OF-3 | CLI delivery | (r2, SF-4) Host-owned reserved `deploy` shell (owns `desktop`, help, install hint); plugin contributes the **children** under that mount — never shadows top-level (DP-4 §6) | Fully plugin-owned group with a shadowing exception (rejected: makes a generic host facility plugin-aware) | | OF-4 | v1 service surface | None; CLI+manifest machine surface; service seam deferred rework-safe (DP-4 §2) | Ship a deploy service + contracts/v1 now | -| OF-5 | Legacy verb deprecation window | Warn from W2, remove after two minor releases (DP-6 M-11) | Keep aliases indefinitely | +| OF-5 | Legacy verb handling | (r2, SF-9 — flipped) First-class compat handlers owned by `deploy-baremetal` through next semver-major; only build/status/logs alias; no minor-release removal claim (DP-6 M-11) | Two-minor-release alias-and-remove (rejected: `start`/`stop`/`copy`/`upgrade` semantics are not `up`/`down`-equivalent) | | OF-6 | Board/milestone relation | New `epic:deploy-plugin` umbrella **supersedes #824** (unified-runtime seed) and re-scopes #823's deploy half; W1–W3 → `0.0.1-beta.13`, W4–W5 → beta.14/stable; #825 (Aspire NuGet packaging) unaffected | Fold under #823 as-is; or all-waves one milestone | | OF-7 | Nitro | No dependency; reference corpus + L-1-gated re-entry as optional emitter package (DP-5 §3) | Commission `deploy-nitro` emitter in first waves | | OF-8 | AWS first-wave scope | HTTP-only until AWS-PROBE-EVENTS passes (DP-3 §7) | Commit full event surface in W5 | @@ -70,35 +71,48 @@ scaffold catalogs reference but do not block on (catalog rows say `external` unt ## 5. Draft board sketch (stage-H input; full bodies authored at integration) -Mirrors the enterprise-auth template with its defects fixed -(`research/board-parity-871-887.md` §4): single `DP-N` id scheme, every child milestoned, p0 -milestoned, consistent status, GitHub-native sub-issues. +(r2 — recut per SF-15: oversized W1/host children split, the compatibility gate added, +dependencies corrected.) Mirrors the enterprise-auth template with its defects fixed +(`research/board-parity-871-887.md` §4): single **`DPB-n`** id scheme used in titles AND +cross-references (distinct from the `DP-N` design-doc numbering), every child milestoned, p0 +milestoned, consistent status, GitHub-native sub-issues. **29 children** (< 30); each body names +files, an anti-scope boundary, and the smallest proof command; the expensive full runtime E2E +remains a **wave exit gate**, not an intermediate loop. -| ID | Title (draft) | Wave | Priority | Delivery shape | Depends on | +| ID | Title (draft) | Wave | Pri | Delivery shape | Depends on | | --- | --- | --- | --- | --- | --- | -| EPIC | Epic: Deploy plugin family (`type:umbrella`, `epic:deploy-plugin`, `area:cli` `area:plugin`) | — | p1 | umbrella; epic-acceptance gates from DP-8 + conformance matrix | — | -| DP-1 | Extract `plugin-deploy-core` (ports, registry, conventions, build, config base) | W1 | **p0** | core extraction, no vendor work | — | -| DP-2 | Capability manifest + rejection compiler + conformance-suite harness | W1 | p0 | core contract, backend-truthful | DP-1 | -| DP-3 | Re-wire CLI deploy group over core; verb lock + desktop routing split | W1 | p1 | thin-router rewire | DP-1 | -| DP-4 | Extract `deploy-baremetal` (+ compose public wiring; systemd live probe) | W2 | p1 | adapter over core port | DP-1 | -| DP-5 | Extract `deploy-aspire` | W2 | p1 | adapter delegating to aspire CLI | DP-1 | -| DP-6 | Extract `deploy-deno` (+ manifest honesty rows; transitive-scan follow-up) | W2 | p1 | adapter wrapping `deno deploy` | DP-1, DP-2 | -| DP-7 | Adapter-side config members + schema registry (config debt retirement) | W2 | p1 | config re-home | DP-4–DP-6 | -| DP-8 | Host extensions: `cli-command` axis, doctor union, capability flag | W2 | p1 | `@netscript/plugin` core change | — | -| DP-9 | `plugins/deploy`: manifest triad, composition root, doctor, verify-plugin | W3 | p1 | thin A5 plugin | DP-2, DP-8 | -| DP-10 | Plugin CLI mount + shim; `target add/remove`, `capabilities --json` | W3 | p1 | plugin CLI contribution | DP-3, DP-9 | -| DP-11 | Scaffolder: `deploy/targets.ts` leaf + per-target assets + Story-0 e2e | W3 | p1 | plugin scaffold contribution | DP-9 | -| DP-12 | `deploy-events` stream + telemetry + runtime-config topic | W3 | p2 | plugin contributions | DP-9 | -| DP-13 | `deploy-container` core path + fly/koyeb/sevalla/coolify/dokploy clients | W4 | p1 | new adapter + subpath clients | DP-2 | -| DP-14 | Container scaffold story (Story 3) + generated Dockerfile golden tests | W4 | p2 | scaffold assets | DP-11, DP-13 | -| DP-15 | CF-PROBE (live Workers conformance, Miniflare fidelity, token story) | W5 | p1 | probe card, findings-only | DP-2 | -| DP-16 | `deploy-cloudflare` adapter + Story 1 scaffold | W5 | p1 | probe-gated adapter | DP-15 | -| DP-17 | Vercel probe + `deploy-vercel` + Story 4 | W5 | p1 | probe-gated adapter | DP-2 | -| DP-18 | AWS-PROBE-HTTP + `deploy-aws` (HTTP scope) + Story 2 | W5 | p1 | probe-gated adapter, HTTP-only | DP-13 | -| DP-19 | Docs refresh: target-matrix reference + per-target how-tos (per wave) | W1–W5 | p1 | docs consolidation | rolling | -| DP-20 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation | backlog | p2 | RFC-first, leaf co-owned | DP-18 | - -(≈21 children; each future body: `Part of #EPIC` → scoping paragraph with anti-scope boundary → +| EPIC | Epic: Deploy plugin family (`type:umbrella`, `epic:deploy-plugin`, `area:cli` `area:plugin`) | — | p1 | umbrella; epic-acceptance gates from DP-8 + conformance matrix; each wave exit includes its docs refresh | — | +| DPB-1 | Move deploy port/result/error contracts to core behind compatibility re-exports | W1 | **p0** | contracts move, no behavior change | — | +| DPB-2 | Move pure conventions (activation/secrets/rollback/otel/health-gate) with their constants | W1 | p1 | conventions move; `runtime-overrides.ts` stays leaf-side | DPB-1 | +| DPB-3 | Empty duplicate-rejecting core registry + CLI compatibility composition root | W1 | p0 | NEW rejection behavior + externalized defaults | DPB-1 | +| DPB-4 | Host-owned `deploy` shell split (desktop preserved) + router rewired over contracts | W1 | p1 | thin-router refactor, no verb changes | DPB-1, DPB-3 | +| DPB-5 | Capability + topology contracts (`CapabilityRef`/verdicts/variants; `DeploymentCell`/`suggestedCells`) + rejection compiler + conformance harness | W1 | **p0** | core contract, backend-truthful | DPB-1 | +| DPB-6 | Two-phase config loader + base schema re-home + frozen legacy union | W1 | **p0** | config bootstrap contract (unknown target ⇒ error) | DPB-1 | +| DPB-7 | Extract `deploy-baremetal` (build behavior, Servy/systemd, `BaremetalCompatibilityCommands`, systemd live probe) | W2 | p1 | adapter over core port + compat handlers | DPB-2, DPB-3 | +| DPB-8 | Extract `deploy-aspire` | W2 | p1 | adapter delegating to aspire CLI | DPB-2, DPB-3 | +| DPB-9 | Extract `deploy-deno` (+ honest manifest rows; transitive-scan follow-up) | W2 | p1 | adapter wrapping `deno deploy` | DPB-3, DPB-5 | +| DPB-10 | Adapter-side config member schemas over the schema registry (config debt retirement) | W2 | p1 | config re-home | DPB-6, DPB-7–DPB-9 | +| DPB-11 | Legacy/config compatibility gate: state-transition + help goldens + unknown-target error paths | W2 | p1 | first-class compat verification (SF-9/SF-10) | DPB-7, DPB-6 | +| DPB-12 | Host: CLI mount-children contribution contract/builder/merger/verifier | pre-W3 | p1 | `@netscript/plugin` change | — | +| DPB-13 | Host: async CLI bootstrap, mount collision rules, loader isolation, plugin-absent behavior | pre-W3 | p1 | CLI bootstrap change | DPB-12 | +| DPB-14 | Host: doctor-check contributions as data + installer-protocol `tooling` variant + `contributionAxes` | pre-W3 | p1 | protocol generalization (auth migrates compatibly) | — | +| DPB-15 | `plugins/deploy`: manifest triad, descriptor composition root, verify-plugin expectations | W3 | p1 | thin A5 plugin (depends on extracted adapters + schema loader) | DPB-5, DPB-9, DPB-10, DPB-12, DPB-14 | +| DPB-16 | Plugin CLI children under the `deploy` mount: `target add/remove`, `capabilities --json`, eight-op router | W3 | p1 | plugin CLI contribution | DPB-13, DPB-15 | +| DPB-17 | Scaffolder: `deploy/targets.ts` leaf + Story-0 assets + golden tests | W3 | p1 | plugin scaffold contribution | DPB-15 | +| DPB-18 | Story-0 `scaffold.runtime` E2E case (`install deploy` → `target add deno-deploy` → `plan`) | W3 | p1 | e2e registration | DPB-16, DPB-17 | +| DPB-19 | `deploy-events` stream + telemetry + runtime-config topic | W3 | p2 | plugin contributions | DPB-15 | +| DPB-20 | `deploy-container`: OCI build/push, `ContainerBuildPort` impl, Dockerfile emission | W4 | p1 | new adapter (injectable shared path) | DPB-5 | +| DPB-21 | Thin platform client tranche (fly/koyeb/sevalla/coolify/dokploy) + live smokes (≥1 managed, ≥1 self-hosted) | W4 | p1 | subpath REST clients | DPB-20 | +| DPB-22 | Container scaffold story (Story 3) + generated-artifact goldens | W4 | p2 | scaffold assets | DPB-17, DPB-20 | +| DPB-23 | CF-PROBE: live Workers conformance, Miniflare fidelity, token story | W5 | p1 | probe card, findings-only | DPB-5 | +| DPB-24 | `deploy-cloudflare` (workers variant) + Story 1 | W5 | p1 | probe-gated adapter; containers cell via injected OCI path | DPB-20, DPB-23 | +| DPB-25 | Vercel probe + `deploy-vercel` (Build Output API) + Story 4 | W5 | p1 | probe-gated adapter | DPB-5, DPB-17 | +| DPB-26 | AWS-PROBE-HTTP (live LWA conformance) | W5 | p1 | probe card, findings-only | DPB-20 | +| DPB-27 | `deploy-aws` (lambda variant, HTTP scope) + Story 2 | W5 | p1 | probe-gated adapter, HTTP-only | DPB-26 | +| DPB-28 | Docs: target-matrix reference + per-target how-tos replace the alpha-minimal page | W3–W5 | p1 | docs consolidation (wave exits carry interim refreshes) | rolling | +| DPB-29 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation | backlog | p2 | RFC-first, leaf co-owned | DPB-27 | + +(Each future body: `Part of #EPIC` → scoping paragraph with anti-scope boundary → `- [ ] gate:` acceptance → `Dependencies:` + `Delivery shape:` — per board-parity conventions.) ## 6. Risk register @@ -106,13 +120,13 @@ milestoned, consistent status, GitHub-native sub-issues. | Risk | Sev | Mitigation | | --- | --- | --- | | Extraction destabilizes the CLI's largest feature | high | Behavior-freeze slices; e2e:cli gate per slice; desktop split isolated (DP-6 R-M1/R-M4) | -| Capability vocabulary designed wrong (too coarse/fine) | high | Vocabulary derived from shipped port set + prior-run L/P/U grid; reviewed in Sol pass; versioned schema so rows can be added without breaking | +| Capability vocabulary designed wrong (too coarse/fine) | ~~high~~ resolved (r2) | SF-6 adopted: structural namespaced+versioned `CapabilityRef`s with scoped verdicts — leaves grow without core edits or manifest rot; residual risk is descriptor-authoring discipline, covered by the conformance-cell gate | | Host-axis creep (`cli-command` opens a contribution firehose) | med | Closed collision rules; one axis, typed registry; doctrine 11 promotion test governs future axes | | Probe failures strand W5 adapters | med | Probes are findings-first cards; adapters ship only behind passing probes (L-7); W1–W4 value is independent of W5 | | Provider surface drift (wrangler/Vercel/Deno Deploy move fast) | med | Wrap at CLI process boundaries; compatibility dates pinned in emitted configs; manifest notes carry dates; conformance suite re-run per upgrade | | Live-probe CI cost/credentials | med | Probe lanes are manually-triggered/secret-gated workflows, not default CI; in-memory suite covers default CI | | JSR surface growth (6+ new packages) | med | OF-2 subpath folding; F-5 export budgets; publish dry-run + jsr-audit per package before any release wave | -| Leaf-backing dependency (catalog rows need leaf adapters that don't exist) | med | Catalog rows degrade to `external` honestly (DP-7 §2); leaf cards tracked separately (DP-20) | +| Leaf-backing dependency (catalog rows need leaf adapters that don't exist) | med | Catalog rows degrade to `external` honestly (DP-7 §2); leaf cards tracked separately (DPB-29) | | Monorepo gap on Deno Deploy git integration | low | Local-source deploys unaffected; noted in manifest; revisit on platform changelog | ## 7. Gates (selection from `archetype-gate-matrix.md`) @@ -142,13 +156,14 @@ Frontend contribution axis (parallel seed run `plan/frontend-contrib`); leaf pro packages (`kv-cloudflare`, `queue-sqs`, DO saga store — leaf-owned cards); desktop packaging (#830); Netlify/Railway/Render/GKE adapters (open registry, community path); Nitro emitter (OF-7 conditional); deploy dashboard service (OF-4 seam); secrets rotation overlap-window; -AWS event semantics (DP-20). +AWS event semantics (DPB-29). -## 10. What downstream passes should attack +## 10. Downstream passes -For the Sol adversarial (constructive) pass: the capability vocabulary granularity (§6 risk 2); -the `cli-command` axis design and its collision rules; whether `plan`-subsumes-`emit` survives -real artifact workflows; the W1 slice ordering against the CLI's actual file graph; the honesty -of every manifest sketch in DP-3. For the Kimi doc-story pass: write the public docs as if W3 -shipped (install → target add → plan/up per story) and surface every DX seam the docs cannot -explain — those are design bugs. +**Sol adversarial — DONE (r2).** `adversarial-sol.md` (SF-1…SF-16, all accepted), triage in +`adversarial-sol-triage.md`; every attack item from r1 §10 produced an adopted amendment +(capability structure, mount-children CLI axis, plan/emit split, W1 recut, manifest honesty). + +**Kimi K3 doc-story — pending.** Write the public docs as if W3 shipped (install → `target add` +→ `plan`/`emit`/`up` per story, capability output, doctor); surface every DX seam the docs +cannot explain — those are design bugs to report back, not to fix in docs. diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 9ffd2c922..6e20101b0 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -82,3 +82,49 @@ pass forecasting public docs, then (3) this generator resumes to integrate. No d was dispatched from this session. STAGE-COMPLETE: generator + +## 2026-07-19 — Stage 2: Sol xhigh constructive adversarial + integration (r2) + +- Owner authorized stage dispatch (in-turn, 2026-07-19): Codex first, integrate valid findings, + then Kimi K3. +- Dispatch: brief `adversarial-brief.md` (commit `7facbd05`); launched via + `agentic/codex/launch-codex-slice.ts` (route requested+observed: openai · `gpt-5.6-sol` · + xhigh; fail-closed identity; upstream unset to pass push safety; daemon managed 0.144.6). + Thread id recorded in `codex-thread-ids.md`; turn 23 min. +- Result: `adversarial-sol.md` (commit `9ed2eeab`) — 16 findings (5 BLOCKER-class seams: + default-target composition ownership, W1 extraction boundary, installer manifest validity, + CLI bootstrap path, config bootstrap cycle) + quick wins. Verdict: sound baseline, not safe to + implement unchanged. +- Triage: `adversarial-sol-triage.md` — **all 16 ACCEPTED** (reviewer verified claims against + the shipped file graph with line citations; every amendment adoptable without breaking the + ratified concept). +- Integration (r2) applied across the corpus: + - DP-1: R-GRAPH-1/2/3 revised (no leaf imports — structural contracts; no `deploy-*` → + `deploy-*` imports, `ContainerBuildPort` by injection; plugin composes descriptors, depends + only on core); W1/W2 recut (refactor-then-extract); OF-2 graduation rule expanded. + - DP-2: full r2 rewrite — eight-op lifecycle (`plan` pure / `emit` materializes + + provenance / `up --prebuilt`); structural capability contracts (`CapabilityRef` namespaced + + versioned, scoped `CapabilityVerdict` incl. `unverified`, per-variant manifests); + `DeploymentCell`/`DeploymentTopologyPlan` (user-declared cells, `suggestedCells`, never + silent partition); empty duplicate-rejecting registry (`DeployTargetCollisionError` = NEW; + no `DEFAULT_DEPLOY_TARGETS` in core); two-phase config loader (unknown target ⇒ error); + legacy verbs = compat handlers. + - DP-3: per-variant manifest cards (workers|containers, lambda|fargate); aspire queue/ + exclusive-writer claims withdrawn to binding scope; baremetal rows enumerated; deno + kv-atomic corrected (adapter implements CAS; platform gaps are separate refs); injection + wording; Story-0 single flow. + - DP-4: full r2 rewrite — protocol-valid installer manifest + `sourceKind:'tooling'` variant; + mount-children CLI contributions + async bootstrap (host-owned reserved `deploy` shell, no + shadowing); doctor checks as data (`{id, loader}` registry); `contributionAxes` instead of + a deploy flag; descriptor-based adapter composition + per-target permission profiles. + - DP-6: M-2/M-3/M-4/M-5/M-11 corrected; conditional debt retirement; unknown-target error + documented as the one deliberate behavior change. + - DP-7/DP-8: profile-scoped zero-fork invariant; catalog depends on the two-phase loader; + stories narrowed to one compute variant (second variant = user-declared cell). + - plan.md: r2 header; LD-2/3/4/5/6/9/10 revised; OF-3/OF-5 re-resolved; board recut to **29 + `DPB-n` children** with corrected dependencies; §10 Sol marked done. + - Naming reconciled: no "7-op" left outside the adversarial/triage records. +- Commit trail this stage: `7facbd05` brief → `9ed2eeab` findings (Codex) → r2 integration + commit (this one). All pushed `HEAD:plan/deploy-plugin`. + +STAGE-COMPLETE: adversarial-integration From 3d086488aa1a38e130f6c578f7f460a010c4b063 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 06:34:41 +0200 Subject: [PATCH 10/20] plan(deploy-plugin): stage-3 doc-story brief (Kimi K3) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../kimi-doc-story-brief.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/kimi-doc-story-brief.md diff --git a/.llm/runs/plan-deploy-plugin--seed/kimi-doc-story-brief.md b/.llm/runs/plan-deploy-plugin--seed/kimi-doc-story-brief.md new file mode 100644 index 000000000..1b258a4c1 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/kimi-doc-story-brief.md @@ -0,0 +1,63 @@ +use harness + +# Brief — Kimi K3 doc-driven story pass (deploy plugin seed, stage 3) + +You are the **doc-driven story** stage of harness run `plan-deploy-plugin--seed` (branch +`plan/deploy-plugin`, this worktree). The design corpus (revision r2, adversarially hardened) is +complete. Your job: **forecast the public-facing documentation as if the deploy plugin had +shipped through wave W3**, so the API/DX becomes concrete before implementation. Where the docs +cannot explain something cleanly, that is a **design bug to report — not something to paper +over with prose**. + +## SKILL + +- `.agents/skills/netscript-harness/SKILL.md` — run mechanics (context only; you write one file). +- Docs voice: mirror the existing site (`docs/site/orchestration-runtime/how-to/deploy.md`, + `docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md`) — practical, honest about + limits, command-first. + +## Read (in order) + +All under `.llm/runs/plan-deploy-plugin--seed/`: + +1. `design/canonical/DP-0-concept-and-goal-frame.md` (the concept + three-tier goal frame) +2. `design/canonical/DP-2-deploy-core.md` (eight-op lifecycle, capability verdicts, cells) +3. `design/canonical/DP-4-plugin-and-host.md` (CLI surface: mount-children, `target add`, + `capabilities --json`, doctor) +4. `design/canonical/DP-8-scaffold-stories.md` (the five walked stories) +5. `design/canonical/DP-3-adapter-cards.md`, `DP-6-migration-map.md`, + `DP-7-contribution-matrix.md` (reference as needed) +6. `plan.md` §2–§5 (locked decisions, board) + +## Deliverable (ONE file) + +Write `.llm/runs/plan-deploy-plugin--seed/doc-story-kimi.md`: + +1. **Docs IA outline** — the deploy section's page tree for the docs site (titles + one-line + scope each), replacing today's "alpha-minimal" deploy page. +2. **Four fully-written forecast pages** (in the site's voice, fenced as documents inside the + file): + - *Deploy a NetScript workspace* (the new getting-started page: `plugin install deploy` → + `deploy target add deno-deploy` → `plan` → `emit` → `up`; reading capability output; + CI split build-vs-deploy with `up --prebuilt`). + - *Deploy to Cloudflare Workers* (Story 1: target add, wrangler config emission, the + workers-variant capability caveats — sagas rejected, KV no-CAS — and what + `suggestedCells` looks like when the app exceeds the isolate profile). + - *Reference: `deploy capabilities` and `deploy doctor`* (verdict levels incl. `unverified` + vs `unsupported` vs adapter-not-installed vs credential-unavailable; per-target permission + profiles). + - *Migrating from the built-in deploy commands* (legacy verbs keep working; what changed: + unknown-target config errors; where `build/start/stop/upgrade` now live). +3. **`## DX findings`** — numbered `KF-1…KF-n`: every seam the docs could not explain cleanly + (naming friction, missing commands/flags, confusing concept counts, outputs users can't + parse, migration traps). One-line claim + why it hurt the page + the corpus ref (DP-N §x). + These findings are your primary value — be adversarial about the DX, constructive in form. + +## Hard rules + +- **Public-docs hygiene**: no internal process/agent/PR-number/codename mentions in the + forecast pages; no invented capabilities — every claim must trace to the r2 corpus; where the + corpus is honest about a limit, the docs are too. +- Write ONLY `doc-story-kimi.md`. Do not edit any other file. Do not run git commands. No + GitHub mutations. No `packages/`/`plugins/` reads needed beyond the two docs-voice references. +- End your final message with `DONE` (or `BLOCKED: `). From 005c521aea89c73e8f7eafedef1c15456628d5f4 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 07:26:00 +0200 Subject: [PATCH 11/20] =?UTF-8?q?plan(deploy-plugin):=20integrate=20Kimi?= =?UTF-8?q?=20K3=20doc-story=20(r3)=20=E2=80=94=20all=2013=20DX=20findings?= =?UTF-8?q?=20accepted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doc-story-kimi.md (docs IA + 4 forecast pages + KF-1..KF-13) + triage + corpus r3: DP-6 default-target contradiction fixed; deploy-deno declares no emit (flagship showcases declared subsets; getting-started = plan -> up); baremetal unified as one target with windows|linux variants; declarations/settings/generated-descriptor homes pinned; --env invocation grammar; .deploy//artifact-manifest.json --prebuilt contract; op-level CLI grammar sketch; deploy cells apply + selector vocabulary; target remove semantics; capability preview catalog; verdict-surface precedence; stories re-headed on the locked target-add flow. Board stays 29 children (KF scope folded into DPB-16/17). Doc-story authored by Kimi K3 (moonshotai/kimi-k3 via OpenCode+OpenRouter). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../plan-deploy-plugin--seed/context-pack.md | 39 +- .../design/canonical/DP-2-deploy-core.md | 49 +- .../design/canonical/DP-3-adapter-cards.md | 13 +- .../design/canonical/DP-4-plugin-and-host.md | 35 +- .../design/canonical/DP-6-migration-map.md | 6 +- .../design/canonical/DP-8-scaffold-stories.md | 14 +- .../doc-story-kimi-triage.md | 25 + .../doc-story-kimi.md | 887 ++++++++++++++++++ .llm/runs/plan-deploy-plugin--seed/drift.md | 3 +- .llm/runs/plan-deploy-plugin--seed/plan.md | 26 +- .llm/runs/plan-deploy-plugin--seed/worklog.md | 44 + 11 files changed, 1086 insertions(+), 55 deletions(-) create mode 100644 .llm/runs/plan-deploy-plugin--seed/doc-story-kimi-triage.md create mode 100644 .llm/runs/plan-deploy-plugin--seed/doc-story-kimi.md diff --git a/.llm/runs/plan-deploy-plugin--seed/context-pack.md b/.llm/runs/plan-deploy-plugin--seed/context-pack.md index ff45f2c82..b2ce24687 100644 --- a/.llm/runs/plan-deploy-plugin--seed/context-pack.md +++ b/.llm/runs/plan-deploy-plugin--seed/context-pack.md @@ -4,30 +4,35 @@ Resumable summary. Read `supervisor.md` first (identity, pipeline, overrides), t ## State (2026-07-19) -**Corpus at revision r2** — generator stage complete AND Sol adversarial integrated. Branch -`plan/deploy-plugin` (worktree `/home/codex/repos/wt-deploy-plugin-seed`), baseline `290c68ef`, -all work pushed with explicit refspec (`git push origin HEAD:plan/deploy-plugin`). No PR, no -issue/label/milestone mutation (kickoff stop-lines). Owner authorized (in-turn, 2026-07-19): -Codex adversarial → integrate valid findings → Kimi K3 doc-story. +**Corpus at revision r3 — FULL KICKOFF PIPELINE COMPLETE.** Branch `plan/deploy-plugin` +(worktree `/home/codex/repos/wt-deploy-plugin-seed`), baseline `290c68ef`, all work pushed with +explicit refspec (`git push origin HEAD:plan/deploy-plugin`). No PR, no issue/label/milestone +mutation (kickoff stop-lines). Owner authorized (in-turn, 2026-07-19): Codex adversarial → +integrate valid findings → Kimi K3. Commit trail: `d7879e68` bootstrap → `fed30572` research corpus → `bcec7e53` DP-0..2 → `a178d31a` DP-3..5 → `96b6c47b` DP-6..8 → `f360deca` plan lock → `7facbd05` adversarial brief → -`9ed2eeab` Sol findings (Codex thread, id in `codex-thread-ids.md`) → r2 integration commit. +`9ed2eeab` Sol findings (Codex thread, id in `codex-thread-ids.md`) → `80882a33` r2 integration +→ `3d086488` Kimi brief → r3 integration commit (doc-story + triage + amendments). ## Pipeline position 1. ✅ Generator (Fable 5 · xhigh) — full seed corpus. -2. ✅ Sol xhigh constructive adversarial (`adversarial-sol.md`, SF-1…SF-16) — **all 16 accepted** - (`adversarial-sol-triage.md`), integrated as r2. -3. ▶ **Kimi K3 doc-driven story** — dispatch via `deno task agentic:opencode` (OpenCode + - OpenRouter; K3 slug passed explicitly — drift D-4; brief = `kimi-doc-story-brief.md`). - Deliverable: `doc-story-kimi.md` forecasting the public docs as if W3 shipped; DX seams the - docs cannot explain are reported as findings, not fixed in prose. -4. Then: generator integrates Kimi findings (r3 if needed) → owner decisions OF-1…OF-8 → - (owner extension, 2026-07-18 evening, recorded in supervisor memory) terminal deliverable = - PR on this branch + filed epic & sub-issues (all Backlog/Triage) + supersession closes — - **supervisor-coordinated AFTER the full pipeline; still forbidden to this session until - then**. +2. ✅ Sol xhigh constructive adversarial — SF-1…SF-16 all accepted, integrated as **r2**. +3. ✅ Kimi K3 doc-story (OpenCode + OpenRouter, `moonshotai/kimi-k3`, drift D-4/D-5) — + `doc-story-kimi.md` (docs IA + 4 forecast pages + KF-1…KF-13); all 13 accepted + (`doc-story-kimi-triage.md`), integrated as **r3**. Key corrections: no preinstalled target + (KF-1); `deploy-deno` declares no `emit`, flow = `plan → up` (KF-9); `baremetal` one target + with `windows|linux` variants (KF-3); declarations vs settings vs generated-descriptor homes + (KF-2); `--env` grammar (KF-8); `--prebuilt` manifest contract (KF-6); `cells apply` + + selector vocabulary (KF-11); `target remove` semantics (KF-12); preview catalog (KF-10); + verdict precedence (KF-13); grammar sketch (KF-5); stories on the locked `target add` flow + (KF-7). +4. ▶ NEXT (supervisor/owner-coordinated — NOT this session until directed): optional formal + PLAN-EVAL (open-model lane); owner ratification of OF-1…OF-8; then the owner-extended + terminal deliverable — PR on this branch + filed epic & 29 `DPB-n` sub-issues (all + Backlog/Triage) + supersession closes (#823/#824/#451/#453/#454/#455…) per the + netscript-pr taxonomy and the board sketch in `plan.md` §5. ## Artifact map diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md index 0c28d9407..37e423a5f 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md @@ -66,14 +66,25 @@ interface DeployTargetPort { canonical surface is **`netscript deploy `** with the eight-op set. The build/deploy split is CI-real: a build job runs `plan` + `emit` and hands the `EmittedArtifactManifest` (artifact digest, source revision, target variant, emitter version, - provenance) to a later deploy job running `up --prebuilt`. + provenance) to a later deploy job running `up --prebuilt`. (r3, KF-6) The hand-off contract is + concrete: `emit` writes to **`.deploy/[@]/`** (the shipped output convention) with + the manifest at **`.deploy/[@]/artifact-manifest.json`**; + `up --prebuilt ` takes the manifest path and verifies artifact digests + against it before any push. +- **CLI grammar sketch** (r3, KF-5 — locked at sketch depth; the full grammar is DPB-16 + acceptance scope): global `--env ` (KF-8, §6); `emit [--output ]` (defaults to the + contract path above); `secrets set|list|unset ` (env-variable names; values never in + argv — piped or prompted); `rollback [--to ]` (defaults to the previous deployment); + `down [--yes]` (confirmation required in a TTY). - **Legacy flat verbs** (r2, SF-9 — semantics are NOT equivalent to `up`/`down`): `build`, `install`, `start`, `stop`, `copy`, `upgrade`, `package-cli`, `uninstall` remain **first-class compatibility handlers owned by `deploy-baremetal`** through the next semver-major release, behind a `BaremetalCompatibilityCommands` adapter that preserves their current flags and side-effect boundaries (shipped `start`/`stop` operate on registered services without install/uninstall; `copy` syncs prebuilt artifacts without registration; `upgrade` is a - five-step transaction). Only `build → plan + emit`, `status`, and `logs` are direct aliases. + five-step transaction). Only `build → plan + emit`, `status`, and `logs` are direct aliases — + (r3, KF-4) **pinned to the `baremetal` target**: `build` ≡ `deploy baremetal plan` + `deploy + baremetal emit`; `status`/`logs` ≡ `deploy baremetal status`/`logs`. Help output may deprecate; **no minor-release removal date is claimed** until an equivalent canonical workflow and migration telemetry exist. Golden help/exit-code tests plus state-transition tests prove `stop` never uninstalls and `start` never registers. @@ -160,6 +171,10 @@ interface DeployCapabilityManifest { compiler** (UR-5): `unsupported` → build failure with the note; `partial` → warning that must be acknowledged in config; never a runtime surprise, never a silent downgrade (L-3). Sagas tri-state law unchanged. +- **Verdict-surface precedence** (r3, KF-13): newer live evidence wins. `deploy doctor`'s live + run supersedes a stale (`probedAt`-old) manifest; `plan` recompiles from the **installed** + manifests at invocation time and is authoritative for the deploy decision; + `deploy capabilities` renders published/installed manifest data and is informational. ## 5. Topology: cells are declared, never silently partitioned (**NEW**, SF-8) @@ -180,11 +195,17 @@ interface DeploymentTopologyPlan { } ``` -v1 rule: **cells are user-declared** in `deploy/targets.ts`. The compiler may return -machine-readable `suggestedCells` when a single-cell verdict fails, but it **rejects rather than -partitions silently**. Gates: single ownership of every service/consumer/schedule across cells; -explicit cross-cell transport; deterministic plan output. Until the topology slice lands, the -Cloudflare/AWS scaffold stories are narrowed to one compute variant per target (DP-8). +v1 rule: **cells are user-declared** in `deploy/targets.ts` (the user-owned **target +declarations** file — distinct from the config **target settings** member, r3 KF-2). The +compiler may return machine-readable `suggestedCells` when a single-cell verdict fails, but it +**rejects rather than partitions silently**. (r3, KF-11) The rejection is not a dead end: `plan` +writes `suggested-cells.json`, and **`deploy cells apply []`** materializes it into +`deploy/targets.ts` with the diff shown — an explicit command editing the user-owned file, never +an implicit rewrite. **Selector vocabulary is the logical-graph unit naming**: +`service:` / `app:` / `background:` (the appsettings unit categories). Gates: +single ownership of every service/consumer/schedule across cells; explicit cross-cell transport; +deterministic plan output. Until the topology slice lands, the Cloudflare/AWS scaffold stories +are narrowed to one compute variant per target (DP-8). `ResourceBinding` is unchanged in spirit — declarative name+kind transport, leaf semantics never interpreted by deploy — with requirements now expressed as `BindingRequirement` (namespaced @@ -204,7 +225,9 @@ interpreted by deploy — with requirements now expressed as `BindingRequirement `DEFAULT_DEPLOY_TARGETS` is **deleted as a core concept** (SF-1): during W1 its compatibility equivalent stays in the CLI composition root; from W3 the plugin/generated project registry supplies entries (as resolved `DeployTargetContribution` descriptors, DP-4 §3). Multi-target - by design, keyed `[@]`. + by design, keyed `[@]` — (r3, KF-8) invoked as + **`netscript deploy --env `** (the flag maps to the qualified registry + key; the config `environments` overlay supplies the values). - **Config — two-phase loader** (**NEW**, SF-10). Today the full config parses `deploy` through a static schema *before* the plugin list is even available, and unknown target keys are silently stripped — so "adapters contribute schemas at load time" needs a real bootstrap @@ -217,10 +240,12 @@ interpreted by deploy — with requirements now expressed as `BindingRequirement (a deliberate, documented behavior change from today's strip-by-design). `@netscript/config` keeps the project-loader seam and exports the legacy target types/schemas as a frozen delegating union for the compatibility window. Environments overlay - (`environments: Record>`) unchanged from r1. Key-mapping note - (quick win): config keys (`windows`, `linux`), registry keys (`windows-service`, - `linux-service`), and the CLI target (`baremetal`) are distinct vocabularies with one explicit - mapping table in `./config` — never treated as interchangeable. + (`environments: Record>`) unchanged from r1. (r3, KF-3 — the + variant mechanism resolves the r2 three-vocabulary note) The bare-metal lane has **one + user-facing name**: target **`baremetal`** with variants **`windows` | `linux`** — CLI, + registry, capabilities, and doctor all say `baremetal`; the legacy config keys `windows`/ + `linux` remain valid target-settings members mapped in by the frozen compat union and + documented as the historical spelling. ## 7. Gates and proof diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md index 61a952184..45819fbb7 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md @@ -48,8 +48,12 @@ carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidenc ## 2. `deploy-baremetal` (W2 extraction) — T1 +- **Lane naming (r3, KF-3):** one target key **`baremetal`**, variants **`windows` | `linux`** + (the SF-7 variant mechanism); legacy config keys `windows`/`linux` map in via the compat + union. - **Wraps:** Servy (Windows) / systemd (Linux) via the shipped OS-service port; the - `deno compile` build pipeline consumed from core `./build`. + `deno compile` build pipeline (owned here from W2; the adapter-neutral emitter graduates to + core later — SF-2). - **Ops:** full eight-op set including convention-backed `rollback` (activation retain + symlink/ dir-swap + health gate — already designed, `DEPLOY-BAREMETAL-PUBLIC-WIRING` debt closes by composing the ports in the plugin's composition root) and `secrets` (env-file reference, @@ -73,7 +77,12 @@ carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidenc scan is a named follow-up card). Target key stays `deno-deploy`. - **Ops:** plan (preflight + build config)/up/down/status/logs; `secrets` via `deno deploy env` (reference model — values never serialized into artifacts); `rollback` via - the platform's instant-rollback revision routing. + the platform's instant-rollback revision routing. (r3, KF-9) **`emit` is deliberately NOT + declared**: the platform builds from source (install/build → artifact → warmup), so there is + no meaningful local artifact materialization — the flagship target showcases the + declared-subset mechanism. Getting-started flow is `plan → up`; the CI build/deploy split + (`emit` + `up --prebuilt`) is demonstrated on targets where emission is real (container, + cloudflare, vercel). - **Manifest (the honesty showcase; r2, SF-7):** `tier: deno-native`; `process: bounded-window`; `@netscript/queue:consume → unsupported` at binding scope (the platform has no queues — note points to `externalized` MQ bindings); diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md index 7b59c5705..eba5a0541 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md @@ -39,12 +39,17 @@ interface DeployTargetContribution { } ``` -`deploy target add ` installs the adapter peer, writes one descriptor into the user-owned / -generated target registry file, and emits the target's scaffold assets. The composition root -resolves descriptors (validated safe loader subpaths), injects shared implementations (e.g. -`deploy-container`'s `ContainerBuildPort`), and populates the duplicate-rejecting registry. -Failure modes are first-class and tested: missing peer, invalid export, duplicate key, -uninstall, stale registry entry. +(r3, KF-2 — the two target surfaces are named and homed:) **target declarations** = +`deploy/targets.ts`, the user-owned leaf (cells, environments, declarative intent); **target +settings** = the `netscript.config.ts` `deploy.targets.` member (per-target configuration); +**descriptors** live in the **generated registry module** +(`.netscript/generated/deploy-registry.*`), regenerated by `target add`/`remove` and never +hand-edited. `deploy target add ` installs the adapter peer, writes the descriptor into +that generated module, adds the settings member, and emits the target's scaffold assets. The +composition root resolves descriptors (validated safe loader subpaths), injects shared +implementations (e.g. `deploy-container`'s `ContainerBuildPort`), and populates the +duplicate-rejecting registry. Failure modes are first-class and tested: missing peer, invalid +export, duplicate key, uninstall, stale registry entry. ## 2. Runtime manifest (`definePlugin`) @@ -144,6 +149,13 @@ dependencies. (`wrangler`, `aspire`, `docker`…), credentials env present (names only, never values), config member parses, capability verdict for the project graph. Output distinguishes `unsupported | unverified | adapter-not-installed | credential-unavailable` (r2 quick win). + Doctor also reports **orphaned declarations** (`deploy/targets.ts` entries whose target is no + longer registered — r3, KF-12). +- **Capability preview catalog** (r3, KF-10): the plugin bundles the **published manifests of + the official adapters** as static data; `deploy capabilities --preview` renders them + before `target add`, clearly labeled "published manifest — adapter not installed; verdicts + for your graph require `target add`". This gives the check-then-decide adoption loop without + pretending an uninstalled adapter was probed locally. ## 5. Host extensions (`@netscript/plugin` + CLI bootstrap) — r2, SF-4/SF-14: generic, data-driven @@ -191,9 +203,14 @@ when it lands (deploy status surfaces in the dev dashboard) and must not design - **Legacy flat verbs** (SF-9): first-class compatibility handlers owned by `deploy-baremetal` through the next semver-major (DP-2 §2) — routed through the shell, not aliased onto `up`/`down`. -- **New verbs** landing with the plugin: `deploy target add|remove ` (peer install + - descriptor + config member + scaffold assets), `deploy capabilities [] --json`, - `deploy doctor` (alias of `plugin doctor` scoped to deploy). +- **New verbs** landing with the plugin: `deploy target add ` (peer install + generated + descriptor + settings member + scaffold assets); `deploy target remove ` — (r3, KF-12) + removes the generated descriptor and the settings member (`--keep-config` opt-out), removes + scaffold assets only with `--purge-assets`, and **never edits the user-owned + `deploy/targets.ts`** (doctor flags orphaned declarations instead); + `deploy capabilities [] [--json|--preview]`; `deploy cells apply []` — (r3, + KF-11) materializes a `plan`-written `suggested-cells.json` into `deploy/targets.ts` with the + diff shown; `deploy doctor` (alias of `plugin doctor` scoped to deploy). ## 7. Acceptance (plugin Concept of Done, per doctrine 11 parity checklist) diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md index 388569471..e8e529fdc 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md @@ -55,8 +55,10 @@ W3 and beyond; legacy flat verbs keep their exact current semantics as compatibility handlers until the next semver-major (M-11, r2). 3. **Scaffolded projects**: existing projects keep working without the plugin (shim path); - `plugin install deploy` is additive. New scaffolds from W3 get the plugin preinstalled with - the `deno-deploy` default target (T1 default, DP-3 §3). + `plugin install deploy` is additive. (r3, KF-1 — contradiction resolved to the r2 answer) + New scaffolds from W3 get the plugin preinstalled with **no target preinstalled**; every + target is added explicitly (`deploy target add`), with `deno-deploy` documented as the + recommended first target (DP-3 §3, DP-8 Story 0). 4. **Aspire keeps its place**: local dev loop (`aspire start`) and the compose/k8s/azure publish lanes are unchanged in behavior — they change owner (adapter package), not shape. 5. **CI workflows**: previously scaffolded workflow files keep functioning (they call the same diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md index f882a7e86..a9b66bf93 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-8-scaffold-stories.md @@ -4,6 +4,9 @@ > Each story names: the command, what lands on disk, the leaf backings chosen (DP-7 §2), the dev > loop, the deploy path, and the honest caveats the capability manifest will render. App code is > identical across all four stories (L-4; DP-7 §3 invariant 1). +> (r3, KF-7) Every story runs on the **locked flow** — `netscript init` + `netscript deploy +> target add `. An `init --deploy ` shorthand remains an explicitly deferred +> open decision (plan §3); no story depends on it. ## Story 0 — The Deno-native default (baseline every story diffs against) @@ -20,10 +23,13 @@ netscript deploy target add deno-deploy # the one flow — targets are always cron → `Deno.cron()` lossless. - **Dev loop:** `aspire start` unchanged (M-13). **Deploy:** `netscript deploy deno-deploy plan` (preflight + capability verdict) → `up`. Rollback: platform instant revision routing. + (r3, KF-9) This target declares no `emit` (the platform builds from source — DP-3 §3); the + CI build/deploy split (`emit` + `up --prebuilt`) is taught on the container/cloudflare/vercel + stories where emission is real. - **Caveats rendered:** sagas `externalized`; queue `unsupported` in-platform; monorepo git integration gap (local-source deploys unaffected). -## Story 1 — Cloudflare-optimized (`netscript init my-app --deploy cloudflare`) +## Story 1 — Cloudflare-optimized (`netscript init my-app` + `deploy target add cloudflare`) - **Lands:** everything in Story 0's shape plus `wrangler.jsonc` (entry, `compatibility_date`, bindings blocks generated from the logical graph), a generated worker entry adapting @@ -49,7 +55,7 @@ netscript deploy target add deno-deploy # the one flow — targets are always (`wrangler deploy`); rollback `wrangler versions rollback`; secrets `wrangler secret` references. -## Story 2 — AWS-optimized (`--deploy aws`) +## Story 2 — AWS-optimized (`deploy target add aws`) - **Lands:** Dockerfile emitted by `deploy-container` (denoland/deno base + Lambda Web Adapter layer/binary), `deploy/targets.ts` `aws` member (region, function/service names, or @@ -67,7 +73,7 @@ netscript deploy target add deno-deploy # the one flow — targets are always - **Caveats rendered:** event/queue semantics not claimed until the leaf probe passes (adversarial F1 wording in the manifest note). -## Story 3 — Container/PaaS (`--deploy fly` | `koyeb` | `sevalla` | `coolify` | `dokploy`) +## Story 3 — Container/PaaS (`deploy target add fly|koyeb|sevalla|coolify|dokploy`) - **Lands:** generated Dockerfile (+ compose for local parity), `deploy/targets.ts` member with the platform client selection (self-hosted platforms: base URL + token env names), workflow @@ -79,7 +85,7 @@ netscript deploy target add deno-deploy # the one flow — targets are always - **Deploy:** `plan` (image build) → `up` (push + platform API create/deploy); rollback via platform revision/machine swap where offered. -## Story 4 — Vercel-optimized (`--deploy vercel`) +## Story 4 — Vercel-optimized (`deploy target add vercel`) - **Lands:** `.vercel/output` emission wired into `plan` (config.json, `functions/*.func` with `.vc-config.json`, `static/`), `deploy/targets.ts` `vercel` member (`runtime: 'node' | diff --git a/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi-triage.md b/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi-triage.md new file mode 100644 index 000000000..910e6398b --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi-triage.md @@ -0,0 +1,25 @@ +# Triage — Kimi K3 doc-story findings (KF-1…KF-13) + +Supervisor (Fable 5) triage of `doc-story-kimi.md` §DX findings. All thirteen **ACCEPTED**; fixes +land as corpus revision **r3**. The forecast pages themselves are kept verbatim as the stage +record — where a resolution changes a documented flow (KF-9), the corpus is authority and the +future real docs follow it; the divergence is noted here, not patched into the forecast. + +| Finding | Disposition | Resolution landed in | +| --- | --- | --- | +| KF-1 preinstalled-default-target contradiction (DP-6 §3 vs DP-8/DP-3) | **ACCEPT** — DP-6 §3 item 3 corrected to the r2-adjudicated answer: plugin preinstalled, **no target preinstalled** | DP-6 §3 | +| KF-2 two "targets" surfaces; descriptor home unpinned | **ACCEPT** — vocabulary fixed: `deploy/targets.ts` = **target declarations** (user-owned: cells, environments); `netscript.config.ts deploy.targets.` = **target settings**; `DeployTargetContribution` descriptors live in the **generated registry module** (`.netscript/generated/deploy-registry.*`, regenerated by `target add/remove`, never hand-edited) | DP-4 §1, DP-2 §5 | +| KF-3 three vocabularies for the bare-metal lane | **ACCEPT** — the SF-7 variant mechanism resolves it: one target **`baremetal`** with variants **`windows` \| `linux`**; CLI/registry/capabilities all say `baremetal`; legacy config keys `windows`/`linux` map in via the frozen compat union | DP-2 §6, DP-3 §2, DP-6 §3 | +| KF-4 legacy aliases don't name their target | **ACCEPT** — aliases are pinned to the baremetal target: `build` ≡ `deploy baremetal plan` + `emit`; `status`/`logs` ≡ `deploy baremetal status/logs` | DP-2 §2 | +| KF-5 op-level CLI grammar unspecified | **ACCEPT (sketch depth)** — minimal grammar locked now (`secrets set\|list\|unset `, `rollback [--to ]`, `emit [--output ]`, `down [--yes]`, `--env ` global); full grammar is DPB-16 acceptance scope | DP-2 §2 | +| KF-6 `--prebuilt` hand-off contract too thin | **ACCEPT** — contract defined: `emit` writes `.deploy/[@]/` with `artifact-manifest.json`; `up --prebuilt `; digest verified against the manifest before push | DP-2 §2/§3 | +| KF-7 `init --deploy` used by stories while still an open decision | **ACCEPT** — stories re-headed on the locked flow (`init` + `target add `); `--deploy` sugar stays an explicitly deferred open decision that no story depends on | DP-8 | +| KF-8 no environment invocation grammar | **ACCEPT** — `netscript deploy --env ` (maps to registry key `@`; config `environments` overlay supplies values) | DP-2 §6 | +| KF-9 `emit` uncorroborated on the flagship `deno-deploy` target | **ACCEPT (honest direction)** — `deploy-deno` **does not declare `emit`** (the platform builds from source; declared-subsets is the mechanism, and the flagship showcases it). Story-0/getting-started flow = `plan → up`; the build/deploy CI split is demonstrated on targets where `emit` is real (container, cloudflare, vercel). Forecast Page A's Step 3 is superseded by this — recorded here, forecast kept as the stage snapshot | DP-3 §3, DP-8 Story 0 | +| KF-10 no capability preview before install | **ACCEPT** — the plugin bundles a **static preview catalog** (published manifests of official adapters); `deploy capabilities --preview` renders it clearly labeled "published manifest — adapter not installed; graph verdicts require `target add`" | DP-4 §4 | +| KF-11 `suggestedCells` is a dead-end hand-off | **ACCEPT** — `plan` rejection writes `suggested-cells.json`; new verb **`deploy cells apply []`** materializes it into `deploy/targets.ts` (explicit command, diff shown, user-owned file). Selector vocabulary pinned to the logical-graph unit names (`service:`, `app:`, `background:`) — Kimi's illustrative syntax adopted as spec | DP-2 §5, DP-4 §6 | +| KF-12 `target remove` semantics unspecified | **ACCEPT** — defined: removes the generated-registry descriptor + the config settings member (`--keep-config` opt-out); scaffold assets removed only with `--purge-assets`; never edits user-owned `deploy/targets.ts` — doctor reports orphaned declarations | DP-4 §6 | +| KF-13 three verdict surfaces, no precedence | **ACCEPT** — precedence stated: newer live evidence wins. `doctor` (live run) supersedes a stale manifest; `plan` recompiles from installed manifests at invocation time and is authoritative for the deploy decision; `capabilities` renders published/installed manifest data (informational, `probedAt`-stamped) | DP-2 §4 | + +Board impact: no new children — KF-5/KF-10/KF-11/KF-12 land inside DPB-16/DPB-17 acceptance +scope (noted in plan §5 preamble); everything else is corpus-text correction. diff --git a/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi.md b/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi.md new file mode 100644 index 000000000..7e6cd36fa --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/doc-story-kimi.md @@ -0,0 +1,887 @@ +# Doc-story pass — deploy plugin family docs forecast + +> Stage 3 deliverable of `plan-deploy-plugin--seed` (doc-driven story). Forecasts the public +> deploy documentation as if the plugin family had shipped through pluginization, per the r2 +> canonical corpus (DP-0…DP-8 + plan.md). Every behavioral claim in the forecast pages traces to +> the corpus; seams the docs could not explain cleanly are reported as `KF-n` findings at the end. +> Forecast pages follow public-docs hygiene: no internal process, wave, issue-number, probe-id, or +> agent references. + +## Docs IA outline + +Proposed page tree for the deploy section (under Orchestration), replacing today's two-page +alpha-minimal surface. `★` = fully written below as a forecast page. + +- **Deploy** (section index, explanation-leaning) — what a deploy target is, the eight-operation + lifecycle (`plan`/`emit`/`up`/`down`/`status`/`logs`/`rollback`/`secrets`), capability verdicts + and tiers at a glance; routes readers to the right how-to. Absorbs the concept half of today's + `deploy.md`. +- how-to/**Deploy a NetScript workspace** ★ — the getting-started path: install the plugin, add + the `deno-deploy` target, `plan` → `emit` → `up`, read capability output, split build vs deploy + in CI with `up --prebuilt`. Replaces today's `deploy.md` (order 101, keeps `oldUrl`). +- how-to/**Deploy to Deno Deploy** — the Deno-native flagship target: config member, preflight, + `secrets`/`rollback` ops, and the honest platform gaps (no in-platform queues, sagas + externalized). Refresh of today's `deploy-deno-deploy.md`. +- how-to/**Deploy to Cloudflare Workers** ★ — the `workers` variant: emitted `wrangler.jsonc`, + isolate-profile caveats, and what `suggestedCells` looks like when an app outgrows the isolate. +- how-to/**Deploy to AWS** — the `lambda` variant (HTTP scope, Deno container image fronted by + Function URL/API GW), GitHub-OIDC CI auth, and the user-declared `fargate` second cell for + long-lived work. (Stub — lands with the AWS adapter.) +- how-to/**Deploy to Vercel** — Build Output API emission, Node-default runtime with the + community Deno runtime as opt-in, bounded-window caveats. (Stub — lands with the Vercel + adapter.) +- how-to/**Deploy containers to a PaaS** — the shared container path: generated Dockerfile + + thin platform clients (Fly.io, Koyeb, Sevalla, Coolify, Dokploy); the "everything just works" + tier for stateful apps. (Stub — lands with the container adapter.) +- how-to/**Deploy to bare metal (Windows/Linux services)** — `deno compile` artifacts, + Servy/systemd service lifecycle, activation + health-gated rollback conventions; where the + legacy flat verbs live. +- how-to/**Deploy with Aspire** — the `compose`/`docker`/`kubernetes`/`azure-*` targets, unchanged + in behavior, now owned by the Aspire adapter; evolves today's `deploy-local-aspire.md` and the + Aspire sections of `deploy.md`. +- how-to/**Migrate from the built-in deploy commands** ★ — the compat map: which verbs and config + keys survive verbatim, the one deliberate behavior change (unknown-target config error), and + where `build`/`start`/`stop`/`upgrade` now live. +- reference/**Deploy target matrix** — every shipped target × variant × tier × process model × + declared ops × sagas state × required tool, generated from the capability manifests. +- reference/**`deploy capabilities` and `deploy doctor`** ★ — verdict levels and scopes, the + manifest JSON surface, doctor diagnostic states, per-target permission profiles. +- reference/**CLI: deploy** — the full command group (eight ops, `target add/remove`, + `capabilities`, `doctor`, legacy compat verbs) folded into the CLI reference. + +--- + +## Forecast pages + +### Page A — how-to: Deploy a NetScript workspace + +````markdown +--- +layout: layouts/base.vto +title: Deploy a NetScript workspace +templateEngine: [vento, md] +order: 101 +oldUrl: /how-to/deploy/ +--- + +# Deploy a NetScript workspace + +**Goal:** take the workspace you scaffolded with `netscript init` and run it on a managed +platform — install the deploy plugin, add a target, check what that target can honestly run, and +ship it — using the same commands locally and in CI. + +Deployment in NetScript is built from **targets you add explicitly**. A target is an adapter for +one platform: Deno Deploy, Cloudflare Workers, AWS, a container PaaS, or your own bare metal. +Every target declares the operations it supports and a **capability manifest** — a +machine-readable statement of what the platform can and cannot run. The CLI checks your app's +requirements against that manifest when you plan, so an impossible deployment fails before +anything leaves your machine — never silently at runtime. + +{{ comp callout { type: "important", title: "What the plugin wires vs. what stays yours" } }} +Wired: the netscript deploy command group, per-target adapters, +capability checks at plan time, artifact emission, and starter CI workflows per target. +Yours: the cloud account, credentials, quotas, and regions. NetScript shells out +to each platform's own first-class tooling (deno deploy, wrangler, +aspire, docker) — it does not create credentials or hide the platform +from you. +{{ /comp }} + +## Before you start + +You need a healthy workspace and a platform account. + +```bash +deno task check # type-check apps, services, contracts +deno task lint +deno task test +``` + +New workspaces ship with the deploy plugin preinstalled. On an existing workspace, install it +once: + +```bash +netscript plugin install deploy +``` + +This walkthrough deploys to **Deno Deploy**, the Deno-native default target. Have your +organization slug and app name ready, and confirm the native CLI resolves: +`deno deploy --help`. + +## Step 1 — Add a target + +```bash +netscript deploy target add deno-deploy +``` + +No target is preinstalled — every target is added explicitly, and you can add several side by +side. `target add` installs the target's adapter, registers it, and writes its starter assets: + +{{ comp.apiTable({ caption: "What 'deploy target add deno-deploy' lands on disk", rows: [ + { name: "deploy/targets.ts", type: "declarations (you own this file)", desc: "The typed list of deploy targets for this workspace. target add appends; you edit it to declare environments or split an app into cells." }, + { name: "netscript.config.ts", type: "settings", desc: "A deploy.targets['deno-deploy'] member holding this target's settings (org, app, entrypoint…)." }, + { name: ".github/workflows/deploy-deno-deploy.yml", type: "CI starter", desc: "A starter workflow using a DENO_DEPLOY_TOKEN organization token." }, + { name: "deno.json", type: "platform config", desc: "The deploy section the native platform reads." } +] }) }} + +The config member keeps a repeatable push to one line; every field is optional and any flag wins +over config for that run: + +```ts +// netscript.config.ts +export default { + deploy: { + targets: { + "deno-deploy": { + org: "my-org", // Deno Deploy organization slug + app: "my-app", // application / project name + entrypoint: "main.ts", // default "main.ts" + prod: false, // default false; true pushes to production + envFile: ".env.production", + }, + }, + }, +}; +``` + +## Step 2 — Plan (preflight + capability check) + +```bash +netscript deploy deno-deploy plan +``` + +`plan` changes nothing — it touches neither your artifact directories nor the platform. It +resolves what you would deploy, runs the target's preflight checks, and compiles your app's +requirements against the target's capability manifest. The verdict table is the part to read: + +```text +target deno-deploy — tier: deno-native · process: bounded-window · sagas: externalized + + runtime:http-serve@1 lossless + runtime:cron@1 lossless Deno.cron() is discovered automatically + @netscript/kv:atomic@1 lossless binding kv:default — Deno KV backing, atomic ops conform + @netscript/kv:queues@1 unsupported Deno KV on this platform exposes no queue primitives + @netscript/queue:consume@1 unsupported no platform queue — bind an external MQ over env config +``` + +(output abridged) + +{{ comp callout { type: "note", title: "Reading capability verdicts" } }} +Every row is one capability with a level: lossless (works, with live-platform +evidence), partial (works with the stated caveat — must be acknowledged in +config), unsupported (demonstrated impossibility), or unverified +(not yet proven — see the reference for the distinction). Your plan fails only when your +app actually requires an unsupported capability; the rest is rendered so you know the +platform's shape. sagas: externalized means saga workers cannot run on this target — +run them on a long-lived target (containers, bare metal) or over an external transport. +See Reference: deploy capabilities +and deploy doctor. +{{ /comp }} + +## Step 3 — Emit the artifact + +```bash +netscript deploy deno-deploy emit +``` + +`emit` materializes the target's deploy artifacts and writes an **artifact manifest** describing +them: the artifact digest, the source revision, the target variant, and the emitter version. This +is the hand-off object between "build" and "deploy" — you will pass it to `up --prebuilt` in CI. + +## Step 4 — Up + +```bash +netscript deploy deno-deploy up +``` + +Plain `up` is the convenience form: it runs `plan` → `emit` → `up` in one command. Once deployed, +the remaining operations work as you'd expect: + +{{ comp.apiTable({ caption: "netscript deploy deno-deploy ", rows: [ + { name: "status", type: "read", desc: "Read deployment state." }, + { name: "logs", type: "read", desc: "Show deployment logs." }, + { name: "rollback", type: "restore", desc: "Instant revision routing back to a previous deployment (platform-native)." }, + { name: "secrets", type: "manage", desc: "Manage environment references. Values are never serialized into artifacts, plans, or logs — only references." }, + { name: "down", type: "delete", desc: "Remove the deployment." } +] }) }} + +Not every target declares every operation. The CLI never advertises an operation a target does +not declare, and calling one fails fast with an operation-unsupported error instead of a silent +no-op. + +## Split build and deploy in CI + +The `plan`/`emit`/`up` split exists so CI can build once and deploy the exact reviewed artifact. +A build job plans and emits; a later deploy job consumes the manifest — nothing is rebuilt, and +the manifest's digest and source revision prove what is being deployed: + +```yaml +# .github/workflows/deploy-deno-deploy.yml (abridged — target add writes the full starter) +jobs: + build: + steps: + - run: netscript deploy deno-deploy plan + - run: netscript deploy deno-deploy emit + - uses: actions/upload-artifact@v4 + with: + name: deploy-artifacts + path: + deploy: + needs: build + environment: production + steps: + - uses: actions/download-artifact@v4 + with: { name: deploy-artifacts } + - run: netscript deploy deno-deploy up --prebuilt + env: + DENO_DEPLOY_TOKEN: ${{ secrets.DENO_DEPLOY_TOKEN }} +``` + +{{ comp callout { type: "warning", title: "Honest limits" } }} +
    +
  • No deploy target is preinstalled — run deploy target add for each platform, and +commit the generated deploy/targets.ts and workflow files.
  • +
  • Capability rejections happen at plan time. If your graph requires something the +target cannot do — an owned queue consumer on Deno Deploy, say — the plan fails with the verdict +and its note, not with a runtime surprise.
  • +
  • Secrets are references. Values never appear in plans, artifact manifests, telemetry, command +output, or errors; only the environment-variable names are checked.
  • +
  • Cluster/cloud auth, quotas, and regions are operator prerequisites. The CLI shells to the +platform's own tooling; it does not create credentials.
  • +
+{{ /comp }} + +## Troubleshooting + +{{ comp.apiTable({ caption: "Common failures and what they mean", rows: [ + { name: "deploy group prints an install hint", type: "plugin", desc: "The deploy plugin is not installed in this workspace. Run netscript plugin install deploy." }, + { name: "DeployTargetAdapterMissingError", type: "config", desc: "Your config names a target whose adapter is not installed. Either netscript deploy target add <key> or remove the config member — unknown targets are an error, never silently ignored." }, + { name: "doctor: credential-unavailable", type: "auth", desc: "The environment variables the target expects (names only) are not set. Set them, or log in with the platform CLI." }, + { name: "deno deploy: command not found", type: "PATH", desc: "The native deno deploy CLI is not resolvable. NetScript shells out to it; confirm deno deploy --help works in the same shell." }, + { name: "plan fails on a capability", type: "verdict", desc: "Your app requires an unsupported capability for this target. Read the note; either change the backing, acknowledge a partial, or pick a target whose manifest allows it." } +] }) }} + +## Where to go next + +{{ comp.featureGrid({ items: [ + { + title: "Deploy to Cloudflare Workers", + body: "wrangler config emission, the isolate capability profile, and splitting an app into cells when it outgrows Workers.", + href: "/orchestration-runtime/how-to/deploy-cloudflare-workers/", + icon: "◆" + }, + { + title: "Reference: capabilities & doctor", + body: "Verdict levels, the manifest JSON surface, doctor diagnostic states, and per-target permission profiles.", + href: "/orchestration-runtime/reference/deploy-capabilities/", + icon: "▣" + }, + { + title: "Migrate from the built-in commands", + body: "Coming from the pre-plugin deploy commands? What survives, the one behavior change, and where the legacy verbs live.", + href: "/orchestration-runtime/how-to/deploy-migrate/", + icon: "◎" + }, + { + title: "Deploy target matrix", + body: "Every shipped target by tier, process model, declared operations, and required tooling.", + href: "/orchestration-runtime/reference/deploy-targets/", + icon: "▣" + } +] }) }} +```` + +### Page B — how-to: Deploy to Cloudflare Workers + +````markdown +--- +layout: layouts/base.vto +title: Deploy to Cloudflare Workers +templateEngine: [vento, md] +order: 110 +--- + +# Deploy to Cloudflare Workers + +**Goal:** ship a NetScript workspace to Cloudflare Workers with an emitted `wrangler.jsonc`, a +generated worker entry that adapts your app's Fetch handler, and capability checks that tell you — +at plan time — whether your app fits the isolate profile. + +Your application code does not change for Workers. Services are written against Web-standard +APIs (`Request`/`Response`, streams, crypto); the target emits a thin worker entry that delegates +to your app's fetch boundary. What changes is the **realization**: Workers run an isolate process +model, and the capability manifest is blunt about what that costs. + +## Before you start + +{{ comp.apiTable({ caption: "What you need before your first deploy", rows: [ + { name: "The deploy plugin + cloudflare target", type: "plugin", desc: "netscript plugin install deploy on existing workspaces (preinstalled on new ones), then netscript deploy target add cloudflare." }, + { name: "A Cloudflare account", type: "account", desc: "Account ID plus an API token with Workers edit scope. There is no OIDC flow on Cloudflare yet — CI uses token secrets (below)." }, + { name: "wrangler", type: "external CLI", desc: "The adapter wraps Cloudflare's wrangler CLI (resolved via npx). deploy doctor verifies it resolves." } +] }) }} + +## Step 1 — Add the target + +```bash +netscript deploy target add cloudflare +``` + +This installs the adapter and emits the Cloudflare-specific assets: + +{{ comp.apiTable({ caption: "What 'deploy target add cloudflare' lands on disk", rows: [ + { name: "wrangler.jsonc", type: "emitted config", desc: "Entry module, compatibility_date, and bindings blocks generated from your logical resource graph (KV namespaces, queues, D1…). Regenerated on plan/emit — edit the graph, not the file." }, + { name: "worker entry (generated)", type: "entry module", desc: "Adapts your app's fetch handler to the Workers module format. Your service code is untouched." }, + { name: "deploy/targets.ts", type: "declarations", desc: "Gains the cloudflare member; you own and edit this file (cells, environments)." }, + { name: ".github/workflows/deploy-cloudflare.yml", type: "CI starter", desc: "Uses cloudflare/wrangler-action with CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets — token-based, since Cloudflare has no OIDC. The scaffold README says so." } +] }) }} + +## Step 2 — Know the workers profile + +The `workers` variant's manifest reads, in short: **tier web-standard, process isolate**. The +rows that decide whether your app fits: + +{{ comp.apiTable({ caption: "Capability caveats — cloudflare (variant: workers)", rows: [ + { name: "runtime:static-assets", type: "lossless", desc: "Workers Static Assets are first-class." }, + { name: "runtime:long-running-process", type: "unsupported", desc: "Isolates are not long-lived processes. Background loops do not belong on this variant." }, + { name: "sagas", type: "rejected", desc: "Saga workers are durable long-lived processes; an isolate cannot host them. A project that uses sagas is rejected at plan time with a cell proposal (below)." }, + { name: "@netscript/kv:atomic", type: "unsupported", desc: "Workers KV has no compare-and-swap. Any consumer that depends on CAS semantics is rejected at plan time — use a different backing for that state." }, + { name: "@netscript/queue:consume", type: "unsupported (v1)", desc: "Cloudflare Queues are push-mode; owned listen()-loop consumption is leaf-backing territory and not claimed yet. The queue itself is still declared by name as a binding." } +] }) }} + +These are not fine print — `plan` compiles your graph against exactly these rows and fails the +build on a required `unsupported` capability. An acknowledged `partial` warns; an `unsupported` +requirement rejects. + +## Step 3 — Develop with fidelity + +```bash +aspire start # the full graph and backing services, unchanged +npx wrangler dev # the worker path, against the emitted wrangler.jsonc +``` + +{{ comp callout { type: "warning", title: "Miniflare is a simulator, not an oracle" } }} +wrangler dev runs your worker in a local simulation of the Workers runtime. It is +the right inner loop, but it is not the platform: before your first real deploy (and after any +binding change), run a smoke against the remote bindings. Treat a green local run as a strong +signal, not a proof. +{{ /comp }} + +## Step 4 — Deploy + +```bash +netscript deploy cloudflare plan # emit wrangler config + entry, compile the capability verdict +netscript deploy cloudflare up # wrangler deploy +``` + +{{ comp.apiTable({ caption: "netscript deploy cloudflare ", rows: [ + { name: "plan", type: "preflight", desc: "Emits wrangler.jsonc + the worker entry and compiles the verdict. Fails on a required unsupported capability." }, + { name: "up", type: "push", desc: "planemitwrangler deploy." }, + { name: "status / logs", type: "read", desc: "Deployment state and live logs (wrangler tail)." }, + { name: "rollback", type: "restore", desc: "wrangler versions rollback — platform-native versioned rollback." }, + { name: "secrets", type: "manage", desc: "References over wrangler secret. Values never appear in plans, artifacts, or output." }, + { name: "down", type: "delete", desc: "Delete the worker." } +] }) }} + +## When the app outgrows the isolate + +If your graph requires something the isolate profile cannot do — sagas, an exclusive database +writer, owned queue consumption — `plan` does not guess a split. It **rejects**, and returns a +machine-readable `suggestedCells` proposal: + +```jsonc +// abridged plan rejection +{ + "target": "cloudflare", + "variant": "workers", + "reasons": [ + { "kind": "sagas", "state": "rejected", + "note": "Saga workers are long-lived; the workers variant runs an isolate process model." }, + { "ref": "runtime:long-running-process@1", "level": "unsupported", "scope": "runtime" } + ], + "suggestedCells": [ + { "id": "edge", "target": "cloudflare", "variant": "workers", + "selectors": ["service:users", "app:dashboard"], "bindings": ["kv:cache"] }, + { "id": "jobs", "target": "cloudflare", "variant": "containers", + "selectors": ["background:workers", "background:sagas"], "bindings": ["queue:jobs", "db:primary"] } + ] +} +``` + +The compiler never partitions your app silently. You decide, then declare the cells yourself in +`deploy/targets.ts`: + +```ts +// deploy/targets.ts +export default defineDeployTargets({ + cloudflare: { + cells: [ + { id: "edge", variant: "workers", + selectors: ["service:users", "app:dashboard"], bindings: ["kv:cache"] }, + { id: "jobs", variant: "containers", + selectors: ["background:workers", "background:sagas"], bindings: ["queue:jobs", "db:primary"] }, + ], + }, +}); +``` + +The `containers` cell is a second, separately declared target sharing the generated container +image path from the [container/PaaS recipe](/orchestration-runtime/how-to/deploy-containers/) — +same image, long-lived, so sagas and queue consumers run there losslessly. Every service, +consumer, and schedule has exactly one owning cell; cross-cell communication is explicit. + +{{ comp callout { type: "warning", title: "Honest limits" } }} +
    +
  • One compute variant per target declaration. Mixed Workers+Containers apps declare cells +explicitly; nothing is auto-partitioned.
  • +
  • Workers KV has no CAS — @netscript/kv:atomic consumers are rejected at plan +time. Put CAS-dependent state on a backing that supports it.
  • +
  • Queue consumption is declared-by-name only in v1; an owned consumer loop is rejected until +the queue leaf backing lands.
  • +
  • CI auth is token-based (CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID). +There is no OIDC flow yet.
  • +
  • Miniflare fidelity is limited — smoke against remote bindings before first deploy.
  • +
+{{ /comp }} + +## Troubleshooting + +{{ comp.apiTable({ caption: "Common failures and what they mean", rows: [ + { name: "doctor: wrangler not found", type: "tool", desc: "The adapter shells out to wrangler (via npx). Confirm npx wrangler --version resolves." }, + { name: "plan rejects on kv:atomic", type: "verdict", desc: "A consumer needs compare-and-swap and Workers KV cannot provide it. Move that state to a CAS-capable backing, or to the containers cell." }, + { name: "plan rejects with suggestedCells", type: "topology", desc: "Your graph exceeds the isolate profile. Declare the proposed cells in deploy/targets.ts (or slim the graph) and re-plan." }, + { name: "401/403 from wrangler-action", type: "auth", desc: "Check CLOUDFLARE_API_TOKEN scope and CLOUDFLARE_ACCOUNT_ID. Token auth only — no OIDC." } +] }) }} + +## Where to go next + +{{ comp.featureGrid({ items: [ + { + title: "Deploy a NetScript workspace", + body: "The getting-started path: targets, plan/emit/up, capability output, CI split.", + href: "/orchestration-runtime/how-to/deploy/", + icon: "◆" + }, + { + title: "Reference: capabilities & doctor", + body: "Verdict levels and scopes, the manifest JSON surface, diagnostic states, permission profiles.", + href: "/orchestration-runtime/reference/deploy-capabilities/", + icon: "▣" + }, + { + title: "Deploy containers to a PaaS", + body: "The shared container image path the jobs cell uses.", + href: "/orchestration-runtime/how-to/deploy-containers/", + icon: "◎" + } +] }) }} +```` + +### Page C — reference: `deploy capabilities` and `deploy doctor` + +````markdown +--- +layout: layouts/base.vto +title: "Reference: deploy capabilities and deploy doctor" +templateEngine: [vento, md] +order: 130 +--- + +# Reference: `deploy capabilities` and `deploy doctor` + +The deploy plugin has no HTTP service and no dashboard — its machine-readable surface is the +**capability manifest**, exposed through two commands: `deploy capabilities` renders a target's +manifest; `deploy doctor` validates everything a configured target needs to actually run. + +## `netscript deploy capabilities [] [--json]` + +With a target argument, prints that target's manifest; without one, prints every configured +target's manifest independently. `--json` emits the manifest as JSON that validates against the +published manifest schema (including `schemaVersion`) — suitable for CI assertions. + +```jsonc +// netscript deploy capabilities cloudflare --json (excerpt) +{ + "schemaVersion": 1, + "target": "cloudflare", + "variant": "workers", // manifests are per target variant — never mode-collapsed + "tier": "web-standard", // deno-native | web-standard | node-compat + "process": "isolate", // long-lived | bounded-window | isolate + "sagas": "rejected", // supported | externalized | rejected + "verdicts": { + "runtime:http-serve@1": { "level": "lossless", "scope": "runtime" }, + "runtime:static-assets@1": { "level": "lossless", "scope": "runtime" }, + "runtime:long-running-process@1": { "level": "unsupported", "scope": "runtime", + "note": "Workers run an isolate process model, not a long-lived process." }, + "@netscript/kv:atomic@1": { "level": "unsupported", "scope": "binding", + "note": "Workers KV has no compare-and-swap; CAS-dependent consumers are rejected at plan time." } + }, + "toolVersions": { "wrangler": "" }, // upstream tool range the verdicts track + "probedAt": "2026-07-19" // when the live-platform evidence was collected +} +``` + +### Manifest fields + +{{ comp.apiTable({ caption: "DeployCapabilityManifest fields", rows: [ + { name: "schemaVersion", type: "number", desc: "Manifest schema version — assert on it when consuming --json." }, + { name: "target / variant", type: "string", desc: "Registry key and compute variant (workers, containers, lambda, fargate, compose, kubernetes…). Verdicts are per variant." }, + { name: "tier", type: "enum", desc: "deno-native (a Deno process or image), web-standard (web-API code on an isolate runtime), or node-compat (provider runtime is Node; the compat cost is declared)." }, + { name: "process", type: "enum", desc: "long-lived, bounded-window, or isolate — the process model the artifact runs under." }, + { name: "sagas", type: "tri-state", desc: "supported | externalized | rejected — whether saga workers can run on this variant." }, + { name: "verdicts", type: "map", desc: "Per-capability verdicts keyed by namespaced, versioned capability refs (below)." }, + { name: "toolVersions / probedAt", type: "metadata", desc: "The upstream tool range the manifest tracks, and when its live evidence was last collected." } +] }) }} + +### Verdict levels, scopes, and the evidence rule + +{{ comp.apiTable({ caption: "Capability verdict levels", rows: [ + { name: "lossless", type: "level", desc: "Works as designed. Requires live-platform evidence — an in-memory fake can prove the conformance suite runs, but it never certifies the provider." }, + { name: "partial", type: "level", desc: "Works with the stated caveat. Must be acknowledged in config; plan warns." }, + { name: "unsupported", type: "level", desc: "A demonstrated impossibility. If your app requires it, plan fails with the note — at build time, never at runtime." }, + { name: "unverified", type: "level", desc: "Not yet proven: no live conformance run, or live state could not be checked. Not the same as unsupported." } +] }) }} + +Each verdict also carries a **scope** saying what it describes: + +{{ comp.apiTable({ caption: "Verdict scopes", rows: [ + { name: "runtime", type: "scope", desc: "A trait of the platform runtime itself (HTTP serving, static assets, cron, long-running processes)." }, + { name: "adapter", type: "scope", desc: "A property of the deploy adapter (declared operations, emission behavior)." }, + { name: "binding", type: "scope", desc: "A semantic guarantee of a named logical resource (queue consume/ack semantics, KV atomicity). Composed from the installed leaf backing's manifest — a runtime manifest never claims queue/KV semantics by itself." } +] }) }} + +Verdicts may carry `note` (the honest caveat, surfaced by the CLI) and `evidence` (the +conformance cell that produced the verdict). A `lossless` row without its evidence is a bug — +report it. + +### Operations are declared subsets + +Every target declares the subset of the eight operations it implements. The CLI never advertises +an operation a target does not declare; calling one fails with +`DeployOperationUnsupportedError`. `rollback` is platform-native or convention-backed per target — +never a silent no-op. + +## `netscript deploy doctor` + +Validates each configured target end to end: + +1. the target's adapter package is installed, +2. its registered descriptor resolves, +3. the required external tool is on `PATH` (`wrangler`, `aspire`, `docker`, `deno`…), +4. the expected credential environment variables are present — **names only, never values**, +5. the target's config member parses against its schema, +6. the capability verdict for your project graph. + +### Diagnostic states + +{{ comp.apiTable({ caption: "Doctor diagnostic states", rows: [ + { name: "adapter-not-installed", type: "state", desc: "Config names a target whose adapter package is missing. Fix: netscript deploy target add <key>, or remove the config member." }, + { name: "credential-unavailable", type: "state", desc: "The credential env vars the target expects are not set, so live state cannot be verified. This is not a platform impossibility — set the variables and re-run." }, + { name: "unverified", type: "state", desc: "A capability is unproven (no live conformance run, or live state unreachable). Distinct from impossible." }, + { name: "unsupported", type: "state", desc: "A demonstrated impossibility for your graph on this target. Fix the backing or the target choice — re-running will not change it." } +] }) }} + +{{ comp callout { type: "note", title: "unverified ≠ unsupported" } }} +Doctor and capability output deliberately separate we could not prove it (unverified, +adapter-not-installed, credential-unavailable) from the platform cannot do it +(unsupported). Only the second fails a plan on a required capability. If a verdict looks stale, +re-run doctor with credentials available — probedAt on the manifest tells you when +the evidence was collected. +{{ /comp }} + +## Permission profiles + +The plugin's own baseline permissions are deliberately small (workspace read/write, registry +network access). Each target then declares the **exact** additional profile its tooling needs — +no target inherits another's permissions, and the plugin never aggregates a union of every +provider's permissions. `deploy doctor` prints the exact profile for your configured targets. + +{{ comp.apiTable({ caption: "Per-target permission profiles (shipped adapters)", rows: [ + { name: "deno-deploy", type: "permissions", desc: "--allow-run=deno, --allow-net (platform API), --allow-read." }, + { name: "cloudflare", type: "permissions", desc: "--allow-run=wrangler,npx, --allow-net." }, + { name: "container targets (fly, koyeb, sevalla, coolify, dokploy)", type: "permissions", desc: "--allow-run=docker,podman,flyctl, --allow-net per platform API." }, + { name: "baremetal", type: "permissions", desc: "--allow-run=servy,systemctl,deno, filesystem access on the install base." }, + { name: "aspire targets (compose, docker, kubernetes, azure-*)", type: "permissions", desc: "--allow-run=aspire,docker, read/write on the output directories." } +] }) }} + +Other adapters declare their own profiles; doctor reports the installed set exactly. + +## Where to go next + +{{ comp.featureGrid({ items: [ + { + title: "Deploy a NetScript workspace", + body: "The getting-started path that uses these verdicts.", + href: "/orchestration-runtime/how-to/deploy/", + icon: "◆" + }, + { + title: "Deploy target matrix", + body: "Every shipped target by tier, process model, declared operations, and required tooling.", + href: "/orchestration-runtime/reference/deploy-targets/", + icon: "▣" + }, + { + title: "Migrate from the built-in commands", + body: "The compatibility map for pre-plugin deploy usage.", + href: "/orchestration-runtime/how-to/deploy-migrate/", + icon: "◎" + } +] }) }} +```` + +### Page D — how-to: Migrate from the built-in deploy commands + +````markdown +--- +layout: layouts/base.vto +title: Migrate from the built-in deploy commands +templateEngine: [vento, md] +order: 120 +--- + +# Migrate from the built-in deploy commands + +**Goal:** if you used NetScript's deploy commands before the deploy plugin existed, know exactly +what changed and what did not. The short version: **your commands and your config keep working.** +Deployment is now owned by a plugin family — a core plus one adapter per platform — and the CLI +group you already type is the same group. + +## What changed + +- **The behavior owner is a plugin.** The `deploy` shell stays built-in (it owns `deploy + desktop`, shared help, and the install hint when the plugin is absent); the deploy plugin + contributes the behavior under it — the target operations, `target add/remove`, `capabilities`, + and `doctor`. Existing projects keep working without the plugin; installing it is additive. +- **Targets are explicit.** New workspaces ship with the plugin installed but **no deploy + target** — add each one with `netscript deploy target add `, which installs the adapter, + registers it, and emits its config member and starter assets. +- **New commands** (from the plugin): + +{{ comp.apiTable({ caption: "New deploy commands", rows: [ + { name: "deploy target add ", type: "new", desc: "Install a target's adapter, register it, write its config member and scaffold assets (workflow files, platform config)." }, + { name: "deploy target remove ", type: "new", desc: "Remove a target registration." }, + { name: "deploy capabilities [] [--json]", type: "new", desc: "Print the capability manifest — what the target can and cannot run for your graph." }, + { name: "deploy doctor", type: "new", desc: "Validate configured targets: adapter installed, tool on PATH, credential env names present, config parses, capability verdict." } +] }) }} + +## The one deliberate behavior change: unknown target keys are now an error + +Previously, an unrecognized key under `deploy.targets` was **silently ignored** at config load. +It now fails fast with `DeployTargetAdapterMissingError`: + +```ts +// netscript.config.ts +export default { + deploy: { + targets: { + "deno-deply": { org: "my-org", app: "my-app" }, // typo: previously ignored, now an error + }, + }, +}; +``` + +{{ comp callout { type: "important", title: "Surfacing misconfiguration is the point" } }} +A config that names a target with no installed adapter is almost always a typo or a missing +install — and silent stripping turned both into confusing no-ops. Fix either way: +netscript deploy target add <key> to install the adapter, or remove the config +member. Every key that parsed before still parses identically; only unknown keys changed +behavior. +{{ /comp }} + +## Verbs that keep working, unchanged + +Every documented `netscript deploy …` invocation works as before: + +- **Managed and Aspire-backed targets** — `deploy deno-deploy|compose|docker|kubernetes| + azure-aca|azure-app-service|azure-aks|cloud-run ` — same commands, same behavior. The + Aspire lanes are unchanged in behavior; they changed owner (an adapter package), not shape. +- **`deploy desktop`** — unchanged, built-in, and not part of the deploy plugin family. +- **Previously scaffolded CI workflows** keep functioning — they call the same verbs. + +## Where `build`, `start`, `stop`, `upgrade`… now live + +The legacy flat verbs remain **first-class compatibility handlers**, owned by the bare-metal +adapter and routed through the built-in shell. Their semantics are preserved exactly — they are +*not* aliases of `up`/`down`: + +{{ comp.apiTable({ caption: "Legacy flat verbs — status", rows: [ + { name: "build", type: "alias", desc: "Direct alias of plan + emit: compiles the deploy artifacts." }, + { name: "status / logs", type: "alias", desc: "Direct aliases of the canonical read ops." }, + { name: "install / uninstall", type: "compat handler", desc: "Register/remove the OS service, as before." }, + { name: "start / stop", type: "compat handler", desc: "Operate on an already-registered service. stop never uninstalls; start never registers." }, + { name: "copy", type: "compat handler", desc: "Syncs prebuilt artifacts to the host without registering anything." }, + { name: "upgrade", type: "compat handler", desc: "The same multi-step transaction as before, unchanged." }, + { name: "package-cli", type: "compat handler", desc: "Builds the operator binary, unchanged." } +] }) }} + +{{ comp callout { type: "note", title: "Deprecation, not removal" } }} +Help output marks the legacy verbs deprecated, but no removal date is claimed: +they stay first-class through at least the next semver-major, and only once an equivalent +canonical workflow and migration telemetry exist. State-transition tests prove +stop never uninstalls and start never registers. +{{ /comp }} + +## Config: keys survive; one vocabulary map to learn + +Existing config keys parse identically: `windows`, `linux`, `docker`, `compose`, `deno-deploy`, +`kubernetes`, `azure-*`, `cloud-run`, including the `environments` overlay. Legacy types stay +exported for the compatibility window. + +One naming seam to be aware of — the bare-metal lane has three vocabularies that are *not* +interchangeable: + +{{ comp.apiTable({ caption: "Bare-metal vocabulary map", rows: [ + { name: "Config keys", type: "netscript.config.ts", desc: "deploy.targets.windows / deploy.targets.linux." }, + { name: "Registry keys", type: "target registry", desc: "windows-service / linux-service — what deploy capabilities and doctor print." }, + { name: "CLI target", type: "command line", desc: "netscript deploy baremetal <op> — the verb target for the lane." } +] }) }} + +## CI workflows + +Previously generated workflow files keep running — they invoke the same verbs. New scaffolds get +per-target workflows from the adapters, OIDC-first where the platform supports it (AWS role +assumption, Deno Deploy organization tokens) and token-based where it does not (Cloudflare API +token + account ID). + +## Optional adoption checklist + +Nothing is required. When you want the new surface: + +```bash +netscript plugin install deploy # additive; existing commands keep working +netscript deploy doctor # validate configured targets and credentials +netscript deploy capabilities --json # machine-readable verdicts for CI assertions +netscript deploy target add # add another platform side by side +``` + +Then, at your own pace, move CI release jobs to the build/deploy split (`plan` + `emit` in the +build job, `up --prebuilt` in the deploy job) so what you deploy is exactly what you reviewed. + +## Where to go next + +{{ comp.featureGrid({ items: [ + { + title: "Deploy a NetScript workspace", + body: "The plugin-era getting-started path: target add, plan/emit/up, CI split.", + href: "/orchestration-runtime/how-to/deploy/", + icon: "◆" + }, + { + title: "Reference: capabilities & doctor", + body: "Verdict levels, manifest JSON, diagnostic states, permission profiles.", + href: "/orchestration-runtime/reference/deploy-capabilities/", + icon: "▣" + }, + { + title: "Deploy to bare metal", + body: "The lane that owns the legacy verbs: services, activation, rollback conventions.", + href: "/orchestration-runtime/how-to/deploy-baremetal/", + icon: "◎" + } +] }) }} +```` + +--- + +## DX findings + +Every seam the forecast docs could not explain cleanly. Format: claim · why it hurt the page · +corpus ref. + +**KF-1 — Contradiction: does a fresh scaffold ship a default deploy target?** +Hurt: Page A must answer "what do I have right after `netscript init`" in its first section, and +the corpus gives two answers — the compatibility contract says new scaffolds get the plugin +preinstalled *"with the `deno-deploy` default target"*, while the scaffold story and the adapter +card (the r2-adjudicated quick win) say *no target is preinstalled; targets are always added +explicitly*. Page A is written to the r2 answer, but the contradiction blocks any statement about +the out-of-box state until DP-6 is amended. +Ref: DP-6 §3 item 3 vs DP-8 Story 0 / DP-3 §3. + +**KF-2 — Two target surfaces with the same name.** +Hurt: `deploy/targets.ts` (typed target/cell *declarations* the user owns) and +`netscript.config.ts` → `deploy.targets.` (per-target *settings*) are both called "targets" +throughout the corpus. Pages A, B, and D each had to coin an awkward distinction ("declarations +vs. settings") to keep users from editing the wrong file; the `DeployTargetContribution` +descriptor's home (registry file vs. leaf file) is also not pinned, so the pages can't say +precisely what `target add` appends where. +Ref: DP-4 §1/§4, DP-8 Story 0, DP-2 §5. + +**KF-3 — Bare metal has three vocabularies for one lane.** +Hurt: config keys `windows`/`linux`, registry keys `windows-service`/`linux-service`, and CLI +target `baremetal` are "distinct vocabularies with one explicit mapping table" — but that means +the migration page needs a mapping *table* to answer "what do I type to deploy to my Linux box?" +(`deploy baremetal `), and no page can use one consistent name for the lane. +Ref: DP-2 §6 (key-mapping note), DP-6 M-5/M-11. + +**KF-4 — The legacy aliases don't name their target.** +Hurt: "`build → plan + emit`, `status`, and `logs` are direct aliases" — aliases of *which* +target? The shipped verbs are bare-metal-flavored and target-less; in the eight-op world every op +hangs off a target. Page D can document `build` as an alias but cannot write the canonical +replacement command (is it `deploy baremetal plan` + `deploy baremetal emit`?) without guessing. +Ref: DP-2 §2, DP-6 M-11. + +**KF-5 — The eight ops are locked; their CLI grammar is not.** +Hurt: no per-op flags or arguments exist anywhere in the corpus — `secrets` (set/list/rotate? +which sub-grammar?), `rollback` (revision selector? `--version`?), `emit` (output-dir flag?), +`down` (confirmation?). The reference page and both how-tos can show the op names but not a +single complete `secrets` or `rollback` invocation, which is exactly what command-first docs need. +Ref: DP-2 §2. + +**KF-6 — The `--prebuilt` hand-off contract is too thin to write the CI snippet.** +Hurt: Page A's CI split needs a concrete artifact hand-off, but the corpus never says where +`emit` writes, what the manifest file is called, or whether `--prebuilt` takes a path, a digest, +or a convention. The page ships `` placeholders — visible seams in +the flagship getting-started flow. +Ref: DP-2 §2/§3. + +**KF-7 — `init --deploy ` is used by the stories but still an open decision.** +Hurt: scaffold Stories 1–4 are titled around `netscript init my-app --deploy cloudflare`, yet the +plan lists "init flag vs post-init `target add` only" as an open decision not taken. Page B was +written around the locked flow (`target add`) and cannot document the provider-optimized init +variant the stories promise; the IA's "optimized scaffold" pages have no stable first command. +Ref: DP-8 Stories 1–4 headers vs plan.md §3 (open decisions). + +**KF-8 — Environment-qualified targets have no invocation grammar.** +Hurt: the registry is keyed `[@]` and config carries an `environments` +overlay, but no page can show how to deploy the staging environment of a target — is it +`deploy cloudflare@staging up`, a `--env` flag, or a config-only concept? Multi-environment docs +(the most common real-world case) are unwriteable. +Ref: DP-2 §6. + +**KF-9 — `emit` on the flagship target is uncorroborated.** +Hurt: the brief-mandated getting-started flow is `plan → emit → up` on `deno-deploy`, but the +emitter-format vocabulary lists `deno-binary | oci-image | wrangler-worker | +vercel-build-output | aspire-publish` — no Deno Deploy format — and the `deploy-deno` adapter +card's op list omits `emit`. Page A shows `deploy deno-deploy emit` producing a manifest it +cannot describe; either the card gains an emit story or the getting-started page changes shape. +Ref: DP-2 §3, DP-3 §3. + +**KF-10 — No capability preview before install.** +Hurt: verdicts come from installed adapters (and binding-scope verdicts from installed leaf +backings), so `deploy capabilities cloudflare` *before* `target add cloudflare` can only report +adapter-not-installed. The docs cannot offer the natural adoption loop — "check what a platform +would give you, then decide" — which is precisely the differentiator the capability story sells. +Ref: DP-2 §4, DP-4 §4. + +**KF-11 — `suggestedCells` is a dead-end hand-off.** +Hurt: plan rejections return machine-readable cell proposals, but nothing consumes them: the +user must hand-translate JSON into `deploy/targets.ts` cell declarations with correct selector +syntax (itself unspecified — Page B's `service:users` style is invented for illustration). No +`target add --cell`, no scaffold assist, no `plan --apply-suggestion`. The first Cloudflare +rejection is the moment the docs most need a next command, and there isn't one. +Ref: DP-2 §5, DP-8 Story 1. + +**KF-12 — `deploy target remove` semantics are unspecified.** +Hurt: the verb is named (peer install + descriptor + config member + scaffold assets on add; +"uninstall" and "stale registry entry" appear only as failure-mode test names), but its +subtractive behavior — peer package uninstall? config-member pruning? workflow/asset deletion? +`deploy/targets.ts` edits? — is nowhere defined. The migration page can list the command but +cannot document the off-ramp. +Ref: DP-4 §1/§6. + +**KF-13 — Three verdict surfaces, no stated precedence.** +Hurt: `plan` renders caveats, `capabilities` renders the manifest, and `doctor` re-renders a +capability verdict for the graph — three places showing overlapping truth with no canonical rule +for which is authoritative when they disagree (e.g. a stale `probedAt` manifest vs. a live doctor +run). The reference page had to invent the guidance ("re-run doctor; check `probedAt`") rather +than cite it. +Ref: DP-2 §2/§4, DP-4 §4/§6, DP-8 acceptance gates. diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md index a4b7421f1..bf500dada 100644 --- a/.llm/runs/plan-deploy-plugin--seed/drift.md +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -7,4 +7,5 @@ Append-only. Severity: `note` | `significant` | `architectural`. | D-1 | 2026-07-18 | note | `workflow/seed-run.md` stage A requires a draft PR as commit trail; kickoff stop-line forbids creating any PR. | Kickoff wins (owner-authored, verbatim, in force). Commit trail = branch `plan/deploy-plugin` pushed with explicit refspec. | | D-2 | 2026-07-18 | note | Seed-run stages F (adversarial) and G (PLAN-EVAL) are replaced by a kickoff-defined pipeline: supervisor-dispatched Sol-xhigh constructive adversarial + Kimi-K3 doc-story, then generator resume. Formal PLAN-EVAL timing is a supervisor decision downstream. | Recorded; this session generates and stops at `STAGE-COMPLETE: generator`. | | D-3 | 2026-07-19 | significant | Sol adversarial pass (SF-1…SF-16) overturned five r1 design seams: verb lifecycle (7-op `plan`-subsumes-emit → eight-op with pure `plan`), capability vocabulary (flat closed union → structural namespaced refs with scoped verdicts), host extensions (union-widening + deploy flag → data-driven contributions + tooling protocol variant), W1 shape (verbatim move → refactor-then-extract), and registry claims (closed-on-key "preserved" → duplicate rejection is NEW). | All integrated as corpus r2 (`adversarial-sol-triage.md`); board recut to 29 `DPB-n` children. The adversarial stage worked as designed — no escalation. | -| D-4 | 2026-07-19 | note | Kickoff names "Kimi K3" for stage 3; `.llm/tools/agentic/config/models.ts` only binds `visionEval: openrouter/moonshotai/kimi-k2.6`. Stage-3 dispatch passes the K3 OpenRouter slug explicitly to the OpenCode launcher (owner-directed model; config update left to a framework slice — this run cannot edit repo code). | Slug verified against the public OpenRouter model list before dispatch; recorded below in worklog. | +| D-4 | 2026-07-19 | note | Kickoff names "Kimi K3" for stage 3; `.llm/tools/agentic/config/models.ts` only binds `visionEval: openrouter/moonshotai/kimi-k2.6`. Stage-3 dispatch passes the K3 OpenRouter slug explicitly to the OpenCode launcher (owner-directed model; config update left to a framework slice — this run cannot edit repo code). | Slug `moonshotai/kimi-k3` verified against the public OpenRouter model list before dispatch; run completed on it. Follow-up: add a K3 binding to `config/models.ts` in a framework slice. | +| D-5 | 2026-07-19 | note | Kimi doc-story caught two genuine r2 corpus defects the adversarial pass missed: the DP-6 preinstalled-default-target contradiction (KF-1) and `emit` being uncorroborated on the flagship `deno-deploy` target (KF-9 — resolved by NOT declaring `emit` there; getting-started flow is `plan → up`). Forecast Page A's Step 3 documents the pre-correction flow and is kept verbatim as the stage snapshot; the corpus (r3) is authority. | Integrated (`doc-story-kimi-triage.md`); future public docs follow the corpus. | diff --git a/.llm/runs/plan-deploy-plugin--seed/plan.md b/.llm/runs/plan-deploy-plugin--seed/plan.md index e50ab96f3..2cd76d87a 100644 --- a/.llm/runs/plan-deploy-plugin--seed/plan.md +++ b/.llm/runs/plan-deploy-plugin--seed/plan.md @@ -1,10 +1,11 @@ # Plan — plan-deploy-plugin--seed -> **Draft — no GitHub mutation.** Generator-stage plan (Fable 5 · xhigh), **revision r2**: the -> Sol-xhigh constructive adversarial pass (`adversarial-sol.md`, SF-1…SF-16) is integrated — -> all sixteen findings accepted (`adversarial-sol-triage.md`). Pending: Kimi-K3 doc-driven story -> pass, generator integration, then (supervisor decision) formal PLAN-EVAL and owner -> ratification. Nothing below files, closes, or edits any issue/PR/milestone/label. +> **Draft — no GitHub mutation.** Generator-stage plan (Fable 5 · xhigh), **revision r3**: both +> downstream passes are integrated — Sol-xhigh adversarial (`adversarial-sol.md`, SF-1…SF-16, +> all accepted, r2) and Kimi-K3 doc-story (`doc-story-kimi.md`, KF-1…KF-13, all accepted, r3 — +> triage in `doc-story-kimi-triage.md`). Pending: (supervisor decision) formal PLAN-EVAL and +> owner ratification of OF-1…OF-8. Nothing below files, closes, or edits any +> issue/PR/milestone/label. ## 1. Scope statement @@ -79,6 +80,10 @@ milestoned, consistent status, GitHub-native sub-issues. **29 children** (< 30); files, an anti-scope boundary, and the smallest proof command; the expensive full runtime E2E remains a **wave exit gate**, not an intermediate loop. +(r3, KF-5/10/11/12) The doc-story resolutions add acceptance scope, not children: the op-level +CLI grammar, the capability **preview catalog**, `deploy cells apply`, and `target remove` +semantics land inside DPB-16/DPB-17's bodies. + | ID | Title (draft) | Wave | Pri | Delivery shape | Depends on | | --- | --- | --- | --- | --- | --- | | EPIC | Epic: Deploy plugin family (`type:umbrella`, `epic:deploy-plugin`, `area:cli` `area:plugin`) | — | p1 | umbrella; epic-acceptance gates from DP-8 + conformance matrix; each wave exit includes its docs refresh | — | @@ -164,6 +169,11 @@ AWS event semantics (DPB-29). `adversarial-sol-triage.md`; every attack item from r1 §10 produced an adopted amendment (capability structure, mount-children CLI axis, plan/emit split, W1 recut, manifest honesty). -**Kimi K3 doc-story — pending.** Write the public docs as if W3 shipped (install → `target add` -→ `plan`/`emit`/`up` per story, capability output, doctor); surface every DX seam the docs -cannot explain — those are design bugs to report back, not to fix in docs. +**Kimi K3 doc-story — DONE (r3).** `doc-story-kimi.md`: docs IA + four forecast pages (kept +verbatim as the stage snapshot) + KF-1…KF-13 — all accepted (`doc-story-kimi-triage.md`). +Notable corpus corrections: the DP-6 default-target contradiction; `deno-deploy` declares no +`emit` (getting-started flow = `plan → up`; the CI split is taught where emission is real); +`baremetal` unified as one target with `windows|linux` variants; declarations/settings/ +descriptor vocabulary pinned; `--env` invocation grammar; `--prebuilt` manifest contract; +`cells apply`, `target remove`, and the preview catalog specified. Forecast Page A's Step 3 +(emit on deno-deploy) is superseded by the KF-9 resolution — the corpus is authority. diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 6e20101b0..5d9a50164 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -128,3 +128,47 @@ STAGE-COMPLETE: generator commit (this one). All pushed `HEAD:plan/deploy-plugin`. STAGE-COMPLETE: adversarial-integration + +## 2026-07-19 — Stage 3: Kimi K3 doc-driven story + integration (r3) + +- Dispatch: brief `kimi-doc-story-brief.md` (commit `3d086488`); launched on the OpenCode + + OpenRouter lane (`deno task agentic:opencode`, model `openrouter/moonshotai/kimi-k3` — slug + verified against the public OpenRouter model list, drift D-4 — variant high). First attempt + was killed by the 10-minute shell timeout mid-read; relaunched detached (nohup + pidfile + + file/exit monitor). Run completed `DONE` (~30 min), read the full r2 corpus + both docs-voice + reference pages, wrote and self-polished the single deliverable; no git actions (per brief). +- Result: `doc-story-kimi.md` (887 lines) — docs IA outline (13-page tree), four fully-written + forecast pages (getting-started, Cloudflare Workers, capabilities/doctor reference, + migration), and 13 DX findings KF-1…KF-13. +- Triage: `doc-story-kimi-triage.md` — **all 13 ACCEPTED**; two scoped (KF-5 grammar at sketch + depth; KF-9 resolved in the honest direction — `deploy-deno` declares no `emit`). +- Integration (r3): + - DP-2: `--prebuilt` manifest contract (`.deploy/[@]/artifact-manifest.json`, + digest-verified); CLI grammar sketch (`secrets set|list|unset`, `rollback [--to]`, + `emit [--output]`, `down [--yes]`, global `--env`); legacy aliases pinned to `baremetal`; + verdict-surface precedence (live doctor > plan compile > capabilities render); + declarations/settings vocabulary; `suggested-cells.json` + `deploy cells apply` + selector + vocabulary (`service:|app:|background:`); `--env` invocation grammar; baremetal unified as + one target with `windows|linux` variants. + - DP-3: baremetal lane-naming bullet; `deploy-deno` `emit`-not-declared note (flagship + showcases declared subsets; CI split taught where emission is real). + - DP-4: descriptor home = generated registry module (`.netscript/generated/deploy-registry.*`); + capability preview catalog (`capabilities --preview`, published-manifest data, + honestly labeled); `target remove` semantics (descriptor + settings member; assets only with + `--purge-assets`; never edits `deploy/targets.ts` — doctor flags orphans); `cells apply` + verb; doctor orphaned-declarations check. + - DP-6: §3 item 3 contradiction fixed (KF-1 — no preinstalled target). + - DP-8: stories re-headed on the locked `target add` flow (KF-7); Story-0 emit note. + - plan.md: r3 header; §5 preamble (KF scope into DPB-16/17 — board stays 29 children); §10 + Kimi marked done with the correction list. +- Commit trail this stage: `3d086488` brief → r3 integration commit (doc-story + triage + + amendments). All pushed `HEAD:plan/deploy-plugin`. + +## Pipeline status + +All three kickoff stages complete: generator → Sol adversarial (r2) → Kimi doc-story (r3). +Remaining, supervisor/owner-coordinated (NOT this session, per stop-lines): formal PLAN-EVAL if +desired; owner ratification of OF-1…OF-8; then the owner-extended terminal deliverable (PR on +this branch + filed epic/sub-issues in Backlog/Triage + supersession closes). + +STAGE-COMPLETE: doc-story-integration From 9ceb4a946a79fa255ddf775ac0c670664b6a1574 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 07:54:30 +0200 Subject: [PATCH 12/20] plan(deploy-plugin): RFC consolidation for the lane draft PR rfc.md presents the ratified direction, laws, public API design, wave plan, 29-child placeholder board, migration/supersession map, and owner forks OF-1..OF-8 in the #822 RFC format. Drift D-6 records the owner's in-turn stop-line lift for the draft PR (board filing still withheld). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .llm/runs/plan-deploy-plugin--seed/drift.md | 1 + .llm/runs/plan-deploy-plugin--seed/rfc.md | 325 ++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/rfc.md diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md index bf500dada..f040ac4ee 100644 --- a/.llm/runs/plan-deploy-plugin--seed/drift.md +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -9,3 +9,4 @@ Append-only. Severity: `note` | `significant` | `architectural`. | D-3 | 2026-07-19 | significant | Sol adversarial pass (SF-1…SF-16) overturned five r1 design seams: verb lifecycle (7-op `plan`-subsumes-emit → eight-op with pure `plan`), capability vocabulary (flat closed union → structural namespaced refs with scoped verdicts), host extensions (union-widening + deploy flag → data-driven contributions + tooling protocol variant), W1 shape (verbatim move → refactor-then-extract), and registry claims (closed-on-key "preserved" → duplicate rejection is NEW). | All integrated as corpus r2 (`adversarial-sol-triage.md`); board recut to 29 `DPB-n` children. The adversarial stage worked as designed — no escalation. | | D-4 | 2026-07-19 | note | Kickoff names "Kimi K3" for stage 3; `.llm/tools/agentic/config/models.ts` only binds `visionEval: openrouter/moonshotai/kimi-k2.6`. Stage-3 dispatch passes the K3 OpenRouter slug explicitly to the OpenCode launcher (owner-directed model; config update left to a framework slice — this run cannot edit repo code). | Slug `moonshotai/kimi-k3` verified against the public OpenRouter model list before dispatch; run completed on it. Follow-up: add a K3 binding to `config/models.ts` in a framework slice. | | D-5 | 2026-07-19 | note | Kimi doc-story caught two genuine r2 corpus defects the adversarial pass missed: the DP-6 preinstalled-default-target contradiction (KF-1) and `emit` being uncorroborated on the flagship `deno-deploy` target (KF-9 — resolved by NOT declaring `emit` there; getting-started flow is `plan → up`). Forecast Page A's Step 3 documents the pre-correction flow and is kept verbatim as the stage snapshot; the corpus (r3) is authority. | Integrated (`doc-story-kimi-triage.md`); future public docs follow the corpus. | +| D-6 | 2026-07-19 | note | Kickoff stop-line "no PRs" partially lifted by owner directive (in-turn via /remote-control, 2026-07-19): open a DRAFT RFC PR (`rfc.md`, #822 format, Refs-only, colon labels, Backlog/Triage milestone) as the lane deliverable. Board filing (epic/sub-issues) and any merge remain forbidden pending owner ratification. | Executed: `rfc.md` committed; draft PR opened per the netscript-pr skill. | diff --git a/.llm/runs/plan-deploy-plugin--seed/rfc.md b/.llm/runs/plan-deploy-plugin--seed/rfc.md new file mode 100644 index 000000000..4c50709a5 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/rfc.md @@ -0,0 +1,325 @@ +# RFC — NetScript Deploy Plugin Family: `plugin-deploy` core + per-cloud adapters, Deno-native first + +| | | +| --- | --- | +| **Status** | **Adversarially hardened draft (r3)** — direction ratified by owner 2026-07-18; package/API design pending owner arbitration of forks OF-1…OF-8 (§8); board **not filed** (numbers are placeholders) | +| **Tracking** | Refs #820 (single-deployment charter) · #327 (Deployment epic) · #823 (Unified epic — re-scope proposal §7) · #824 (unified-runtime seed — **superseded by this RFC**, §7) · #825 (Aspire packaging NuGet — unaffected) · #871 (enterprise-auth board — the composition + board pattern this family mirrors) | +| **Pipeline** | Fable 5 · xhigh generator → GPT-5.6 Sol · xhigh constructive adversarial (**16/16 findings accepted**, r2) → Kimi K3 doc-driven story (**13/13 DX findings accepted**, r3). Triage records in the run dir | +| **Evidence base** | Prior seed run `plan-unified-runtime--seed` (nitro-vs-own analysis, both adversarial rounds); live provider surfaces 2026-07-18 (Cloudflare/Vercel/AWS/Fly/Deno Deploy/thin PaaS/Nitro v3, primary sources); shipped deploy layer inventory; auth-plugin composition anatomy; doctrine A7 + `DEPLOY-ARCHETYPE-7-CORE-SEED` debt; full corpus in `.llm/runs/plan-deploy-plugin--seed/` (this PR) | + +--- + +## Abstract + +NetScript's deploy story is rebuilt as a **plugin family** — exactly the way auth composes: + +``` +plugins/deploy @netscript/plugin-deploy (thin A5 plugin) +packages/plugin-deploy-core @netscript/plugin-deploy-core (A2 core: ports, registry, + capabilities, conventions) +packages/deploy-aspire | deploy-baremetal | deploy-deno (A2 adapters — extractions) +packages/deploy-container (+ fly/koyeb/sevalla/coolify/dokploy) (A2 adapter — shared OCI path) +packages/deploy-cloudflare | deploy-vercel | deploy-aws (A2 adapters — probe-gated) +``` + +``` +netscript plugin install deploy +netscript deploy target add deno-deploy # targets are explicit; several compose side by side +netscript deploy deno-deploy plan # pure: capability verdict, nothing mutated +netscript deploy deno-deploy up # ship +``` + +One goal frame governs every target — **"Deno native first, then Node compat where needed"** — +made operational as three declared tiers (Deno-native process / Web-standard isolate / +Node-compat), and one credibility mechanism makes the cloud story honest: every target ships a +**capability manifest** whose verdicts are compiled against the app's requirements at plan time — +build-time rejection with an explanation, never a runtime surprise, never a silent downgrade. +Provider-optimized scaffolds (Cloudflare-first seams, AWS suites, Vercel marketplace, the +container PaaS tier) then make provider-first real without ever forking application code. + +## 1. Motivation + +The prior unified-runtime seed (#824) ended in an adversarial verdict that killed its own +architecture: a Nitro-hosted "giant deploy package" was rejected as a god-object with a +dependency-cycle risk, unproven provider-family claims, and a board whose canonical bodies could +not survive the pivot. The owner ratified the redirect (2026-07-18): no giant package — use +NetScript at what it is best at, **composability**. A deploy **plugin** with a core and provider +adapters, modeled on the auth family, redefines the whole deploy story: + +- the plugin contributes at **every layer** — CLI, scaffolding, streams/telemetry, doctor, and + (when the frontend contribution axis lands) UI; +- per-cloud adapters make provider-first scaffolding possible (a Cloudflare-optimized project + ships Workers/DO/KV-first seams from day one, same for AWS suites, Vercel); +- it "defeats" naive cloud-agnosticism while making the credible version real: strict + NetScript-owned ports + honest per-target capability verdicts + explicit provider choices. + +Doctrine had already reserved the seat: Archetype 7 models deploy as an A2 core + A6 router +composite, its F-DEPLOY gates are seeded awaiting the packages, and the shipped CLI kernel +already contains the target port, registry, and activation/secrets/rollback/health conventions +as target-agnostic modules (`DEPLOY-ARCHETYPE-7-CORE-SEED`). **This family is an extraction and +pluginization, not an invention.** + +## 2. The ratified decision and its rationale + +**Decision.** Deploy ships as the package family above, composed like auth: a provider-agnostic +core owning ports + registry + conventions + the capability compiler; thin per-provider adapters +wrapping each platform's first-class tooling; a thin plugin that wires, declares, contributes, +and re-exports. Laws carried forward from the prior run's surviving ground: + +| # | Law | +| --- | --- | +| L-1 | Per target, a provider-native wrapper wins over any generic emitter iff it passes the same conformance suite, exposes every required native surface, and costs less to maintain. Nothing third-party enters the composition contract or leaf ports; whatever emits, emits behind a NetScript-owned port | +| L-2 | Leaf ports (`KvStore`, `MessageQueue`, saga/worker/trigger/stream ports) stay authoritative; provider primitives are capability-scoped backings — each a feasibility question, not a mapping exercise | +| L-3 | Capability manifest + build-time rejection: `unsupported` fails the plan with the note; `partial` warns and must be acknowledged; sagas are `supported \| externalized \| rejected` per target | +| L-4 | One logical composition root; no application-created loopback; app code never forks per provider | +| L-5 | Artifacts drive services through the shipped `ServiceShutdownCoordinator` lifecycle | +| L-6 | The Docker-image long tail (Fly, Koyeb, Sevalla, Coolify, Dokploy, CF Containers) is one shared container path with thin platform clients | +| L-7 | Probe before ownership claims: Cloudflare Workers, Vercel, and AWS adapters are gated on live conformance probes; AWS claims HTTP scope only until event-semantics probes pass | + +**Wrap map (selective wrapping, owner point 4):** wrangler (invoked as a tool) + emitted +`wrangler.jsonc`; Vercel **Build Output API v3** (`.vercel/output` → `vercel deploy +--prebuilt`); the built-in **`deno deploy`** CLI (Deno-native flagship); Fly **Machines REST +API** + the four PaaS REST APIs over one owned OCI path; **AWS Lambda Web Adapter** (HTTP +sidecar — scope-limited honestly) + optional **Pulumi Automation API** for IaC; **Aspire-native** +for local/self-host/azure/k8s lanes (`aspire publish|deploy|destroy` — Aspire remains +composer/executor, never the contract). **Rejected:** Serverless Framework v4 (license-gated). +**Nitro:** no dependency — presets are welded to Nitro's build and its Deno preset is Node-built; +kept as a reference corpus with a named L-1-gated re-entry as an optional emitter package if a +target ever proves cheaper that way. + +## 3. Public API design (with implementation detail) + +### 3.1 The eight-operation target port (core) + +The shipped kernel port moves to `plugin-deploy-core` and is sharpened — `plan` is **pure**, +`emit` materializes, `up --prebuilt` consumes: + +```ts +type DeployOperation = + | 'plan' | 'emit' | 'up' | 'down' | 'status' | 'logs' | 'rollback' | 'secrets'; + +interface DeployTargetPort { + readonly key: DeployTargetKey; + readonly operations: readonly DeployOperation[]; // declared subset — never advertise more + readonly capabilities: DeployCapabilityManifest; // backend-truthful (§3.2) + plan(ctx): Promise; // pure: topology + capability verdict, + // serializable DeploymentPlan + emit(ctx): Promise; // content-addressed artifacts + provenance + up(ctx): Promise; // plain up = plan → emit → up; + // up --prebuilt consumes a build + down(ctx): Promise; + status(ctx): Promise; + logs(ctx): Promise; + rollback(ctx): Promise; // platform-native or convention-backed + secrets(ctx): Promise; // references + rotation; values never serialized +} +``` + +- CI hand-off contract: `emit` writes `.deploy/[@]/` with + `artifact-manifest.json` (artifact digest, source revision, target variant, emitter version); + `up --prebuilt ` digest-verifies before pushing. +- Unsupported ops throw `DeployOperationUnsupportedError` (the auth + `OperationUnsupportedError` pattern); the flagship `deno-deploy` target deliberately declares + **no `emit`** (the platform builds from source) — the declared-subset mechanism on display. +- Environment grammar: `netscript deploy --env ` (registry key + `@`; config `environments` overlay). +- Emission is a separate `ArtifactEmitterPort` (formats: `deno-binary`, `oci-image`, + `wrangler-worker`, `vercel-build-output`, `aspire-publish`) so lifecycle consumers never + depend on build machinery; `ContainerBuildPort` is the core-owned OCI specialization that + `deploy-container` implements and composition roots **inject** into the cloudflare/aws lanes. + +### 3.2 Capability contracts — honest agnosticism, structurally + +```ts +interface CapabilityRef { namespace: string; name: string; major: number } // 'runtime' | '@netscript/kv' | … +interface BindingRequirement { binding: string; capability: CapabilityRef } +interface WorkloadConstraint { kind: 'singleton' | 'long-running' | 'co-locate' | 'offline' } + +interface CapabilityVerdict { + level: 'lossless' | 'partial' | 'unsupported' | 'unverified'; + scope: 'runtime' | 'adapter' | 'binding'; + evidence?: string; // conformance-suite cell / probe id — a lossless row without one is a bug + note?: string; // the honest caveat, rendered by the CLI +} + +interface DeployCapabilityManifest { + schemaVersion: number; + target: DeployTargetKey; + variant: string; // 'workers' | 'containers' | 'lambda' | 'fargate' | … + tier: 'deno-native' | 'web-standard' | 'node-compat'; + process: 'long-lived' | 'bounded-window' | 'isolate'; + verdicts: ReadonlyMap; + sagas: 'supported' | 'externalized' | 'rejected'; + toolVersions?: Record; probedAt?: string; +} +``` + +Core owns only these **structures** plus a small `runtime:*` trait vocabulary; leaf packages +contribute namespaced descriptors **through** them — core imports no leaf package, no provider +SDK. Manifests are per target **variant** (Workers ≠ Containers; Lambda ≠ Fargate). `lossless` +requires a live-platform conformance cell; unproven/not-installed/credential-missing states are +`unverified`-family diagnostics, never provider impossibility. Precedence when surfaces +disagree: live `doctor` > `plan` (recompiles from installed manifests) > `capabilities` +(rendered data). `compileCapabilityVerdict` is the build-time rejection compiler (L-3). + +### 3.3 Topology cells — never silently partitioned + +```ts +interface DeploymentCell { + id: string; selectors: readonly string[]; // 'service:' | 'app:' | 'background:' + target: DeployTargetKey; variant: string; bindings: readonly string[]; +} +interface DeploymentTopologyPlan { cells: DeploymentCell[]; transports: CrossCellTransport[] } +``` + +Cells are **user-declared** in `deploy/targets.ts`. When an app exceeds a variant's profile +(sagas on an isolate, CAS on Workers KV), `plan` **rejects** and writes machine-readable +`suggested-cells.json`; `deploy cells apply` materializes it into the user-owned file with the +diff shown. Every service/consumer/schedule has exactly one owning cell; cross-cell transport is +explicit. + +### 3.4 The plugin, its manifest triad, and three host extensions + +Three surfaces are named and homed: **target declarations** (`deploy/targets.ts`, user-owned), +**target settings** (`netscript.config.ts` → `deploy.targets.`), and **descriptors** +(generated registry module, regenerated by `target add/remove`): + +```ts +interface DeployTargetContribution { + key: string; + targetLoader: SafePackageExport; // e.g. { pkg: '@netscript/deploy-deno', export: './target' } + schemaLoader: SafePackageExport; // the target's config member schema + permissions: DeployPermissionProfile; // exact per-target tool/net/fs profile +} +``` + +The plugin depends **only on core** and composes resolved descriptors — adapters are peer +install choices (`deploy target add `), never static imports. New verbs: +`target add|remove`, `capabilities [] [--json|--preview]` (a bundled published-manifest +preview catalog serves check-then-decide before install), `cells apply`, `doctor` (states: +`unsupported | unverified | adapter-not-installed | credential-unavailable`; reports per-target +permission profiles and orphaned declarations). No resident HTTP service in v1; the plugin also +contributes a `deploy-events` durable stream (versioned audit envelope), telemetry spans with +secret redaction, and a tiny `deploy` runtime-config topic. + +Host (`@netscript/plugin` + CLI) gains three **generic** extensions: (1) CLI **mount-children** +contributions (`{mount, id, loader, export}`) + async bootstrap — the built-in `deploy` shell +stays host-owned (reserved mount, `desktop`, help, install hint) and contributions can never +shadow a top-level command; duplicate `(mount,id)` fails before parsing; (2) **doctor checks as +data** (`{id, loader}` registry — replacing the hard-coded literal union); (3) an +installer-protocol `officialSource` variant `{ sourceKind: 'tooling' }` + +`capabilities.contributionAxes` — no deploy-specific field enters the generic protocol. + +### 3.5 Config: two-phase loader + +Adapter-owned target schemas require a real bootstrap contract: (1) bootstrap-parse project +identity + `plugins` + `deploy.targets` unstripped → (2) resolve plugin/adapter `schemaLoader`s +→ (3) compose the target schema registry → (4) full parse. An unknown target key becomes +`DeployTargetAdapterMissingError` — the **one deliberate behavior change** (today unknown keys +are silently stripped). `@netscript/config` keeps the loader seam + a frozen delegating legacy +union for the window. The bare-metal lane unifies as one target `baremetal` with +`windows | linux` **variants**; legacy config keys map in unchanged. + +## 4. What ships when (plan) + +| Wave | Content | Exit proof | +| --- | --- | --- | +| **W1** | Core extraction as ordered refactor-then-extract sub-slices: contracts behind compat re-exports; pure conventions with constants; **empty** duplicate-rejecting registry (defaults externalized to the CLI composition root); host-owned `deploy` shell split preserving `desktop`; capability+topology contracts + compiler; two-phase config loader. F-DEPLOY-1/2 flip `reviewed→gated` | `e2e:cli` green after every sub-slice | +| **W2** | `deploy-aspire`, `deploy-baremetal` (receives the build pipeline + the legacy-verb compat handlers), `deploy-deno` extracted; adapter-side config members; compatibility gate (state-transition + help goldens + unknown-target paths) | per-package A2 gates; manifests published | +| **W3** | Host extensions (3 slices) → `plugins/deploy` (manifest triad, descriptor composition, doctor, verify-plugin) → plugin CLI children → scaffolder + Story-0 `scaffold.runtime` E2E → streams/telemetry | `plugin install deploy` → `target add deno-deploy` → `plan` on a generated workspace | +| **W4** | `deploy-container` (OCI build/push + Dockerfile emission) + thin PaaS clients + container scaffold story | live smoke ≥1 managed + ≥1 self-hosted platform | +| **W5** | Probe-gated clouds: CF-PROBE → `deploy-cloudflare` (workers variant) + story; Vercel probe → `deploy-vercel` + story; AWS-PROBE-HTTP → `deploy-aws` (lambda variant, HTTP scope) + story | each adapter ships only behind its passing probe (L-7) | + +Milestone proposal (owner fork OF-6): W1–W3 → `0.0.1-beta.13`, W4–W5 → beta.14/stable; the epic +supersedes #824 and re-scopes #823's deploy half. Legacy flat verbs +(`build/install/start/stop/copy/upgrade/package-cli/uninstall`) stay **first-class compat +handlers** with their exact shipped semantics through the next semver-major (their semantics are +not `up`/`down`-equivalent); only `build → plan+emit`, `status`, `logs` alias — pinned to the +`baremetal` target. + +## 5. Proposed board (placeholders — filed only after owner ratification) + +One umbrella epic (`type:umbrella`, `epic:deploy-plugin`) + **29 children `DPB-1…DPB-29`**, in +the enterprise-auth body template (`Part of #EPIC` → scoping paragraph with anti-scope boundary +→ `- [ ] gate:` acceptance predicates → `Dependencies:` + `Delivery shape:`) with its defects +fixed (single id scheme, every child milestoned, GitHub-native sub-issues). Summary of the DAG: + +- **W1 (6):** DPB-1 contracts move (p0) · DPB-2 conventions · DPB-3 empty registry + compat + composition root (p0) · DPB-4 shell split + router rewire · DPB-5 capability+topology + contracts + compiler + conformance harness (p0) · DPB-6 two-phase config loader (p0) +- **W2 (5):** DPB-7 baremetal · DPB-8 aspire · DPB-9 deno · DPB-10 config members · DPB-11 + compatibility gate +- **Host (3):** DPB-12 mount-children contract · DPB-13 async bootstrap + collisions · DPB-14 + doctor-as-data + tooling protocol variant +- **W3 (5):** DPB-15 plugin · DPB-16 CLI children (grammar, `cells apply`, `target remove`, + preview catalog) · DPB-17 scaffolder · DPB-18 Story-0 E2E · DPB-19 streams/telemetry +- **W4 (3):** DPB-20 OCI core · DPB-21 PaaS client tranche · DPB-22 container story +- **W5 (5):** DPB-23 CF-PROBE · DPB-24 deploy-cloudflare + Story 1 · DPB-25 Vercel probe + + adapter + Story 4 · DPB-26 AWS-PROBE-HTTP · DPB-27 deploy-aws + Story 2 +- **Rolling/backlog (2):** DPB-28 docs (target-matrix reference + per-target how-tos replace the + alpha-minimal page) · DPB-29 deferred RFC: AWS event semantics + leaf backing catalog + graduation (p2) + +Full table with per-child dependencies: `plan.md` §5 (this PR). Issue numbers are assigned at +the supervisor-coordinated filing step — **not by this PR**. + +## 6. Migration and supersession map + +**Migration (behavior-preserving; full item map M-1…M-18 in `DP-6`):** the shipped 8-op port, +registry, conventions, and compile pipeline are extractions from `packages/cli`; config keys and +every documented verb survive; Aspire lanes change owner, not behavior; scaffolded workflows +keep functioning; desktop packaging stays outside this family (epic #830 boundary). Debt +retired: `DEPLOY-ARCHETYPE-7-CORE-SEED` (conditional on the externalized composition root), +`DEPLOY-SECRETS-ROLLBACK-CORE`, `DEPLOY-BAREMETAL-PUBLIC-WIRING`, the deploy slice of +`config-plugin-specific-schema-debt`, the container half of `cli-deploy-artifacts-missing` +(NetScript finally emits a Dockerfile — as adapter output), `cli-deploy-linux-integration-untested` +(as a live-probe cell). + +**Supersession proposals (owner arbitration at filing; no closes by this PR):** + +| Issue | Proposal | +| --- | --- | +| #824 unified-runtime seed | **Superseded by this RFC** — close at filing with a successor pointer to the deploy-plugin epic | +| #823 Unified epic | Re-scope: its deploy half is owned by this family; its single-runtime product framing is re-stated against the plugin architecture (owner decision at filing) | +| #451 SDK transport surface | Orthogonal to deploy — KEEP; re-homing decision stays with #823's re-scope | +| #453 desktop realization | KEEP — consumed by epic #830 (desktop graph), not this family | +| #454 single-process realization | Deploy aspect absorbed by the topology/cell + capability model (§3.3); close-or-fold proposal at filing with pointer | +| #455 offline-sync | KEEP — leaf/database capability (`offline` workload constraint names the seam); not deploy's to close | +| #327 Deployment epic | Body updated at filing to point at the new family as its architecture | + +## 7. Security & operational posture (summary) + +Secrets are references end-to-end — values never in plans, artifact manifests, telemetry, stream +events, argv, or errors (negatively tested). Per-target least-privilege permission profiles, +reported exactly by doctor; no aggregate all-provider permission claim. CI auth OIDC-first where +platforms support it (AWS role-assume, Deno Deploy org tokens), token-based where not +(Cloudflare — documented). Health-gated activation with retain/rollback conventions; audit +stream whose sink can never fail a deploy. + +## 8. Open forks for owner arbitration + +| Fork | Question | Recommendation | Alternative | +| --- | --- | --- | --- | +| OF-1 | Core package name | `@netscript/plugin-deploy-core` (auth parity) | `@netscript/deploy-core` (kickoff literal) | +| OF-2 | Thin PaaS packaging | Subpaths of `deploy-container`; graduate on SDK/auth/lifecycle divergence | One package per platform now | +| OF-3 | CLI delivery | Host-owned reserved `deploy` shell; plugin contributes children (never shadows) | Fully plugin-owned group (rejected by adversarial SF-4) | +| OF-4 | v1 service surface | None — CLI + manifest is the machine surface; service seam deferred rework-safe | Ship a deploy HTTP service + contracts/v1 now | +| OF-5 | Legacy verbs | First-class compat handlers until next semver-major; only build/status/logs alias | Two-minor-release alias-and-remove (rejected by SF-9) | +| OF-6 | Board/milestone relation | New `epic:deploy-plugin` supersedes #824, re-scopes #823's deploy half; W1–W3 → beta.13, W4–W5 → beta.14/stable | Fold under #823 as-is; or single milestone | +| OF-7 | Nitro | No dependency; reference corpus + L-1-gated re-entry as optional emitter | Commission a `deploy-nitro` emitter in the first waves | +| OF-8 | AWS first-wave scope | HTTP-only until AWS-PROBE-EVENTS passes | Commit the full event surface in W5 | + +Open decisions deferred rework-safe: cloud-run re-home timing (W4); `init --deploy ` +sugar (no story depends on it); deploy dashboard service; secrets rotation overlap-window card. + +--- + +**Provenance.** This PR lands the full planning record under +`.llm/runs/plan-deploy-plugin--seed/`: six-surface research corpus (`research/`), canonical +design docs `DP-0…DP-8` (r3), `plan.md` (locked decisions LD-1…12, the 29-child board table, +risk register, gates), the Sol adversarial trail (`adversarial-brief.md`, `adversarial-sol.md`, +`adversarial-sol-triage.md` — 16/16 accepted), and the Kimi doc-story trail +(`kimi-doc-story-brief.md`, `doc-story-kimi.md` — docs IA + four forecast pages, +`doc-story-kimi-triage.md` — 13/13 accepted; where a forecast page predates a correction, the +corpus is authority). Board numbers here are placeholders — filing is a later +supervisor-coordinated step. Refs #820 #327 #823 #824 #825 — no closing keywords. From 642d3e3be9f9197f75593055db2bbcac4d72af7d Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 07:58:06 +0200 Subject: [PATCH 13/20] plan(deploy-plugin): record RFC draft PR #891 in worklog Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .llm/runs/plan-deploy-plugin--seed/worklog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 5d9a50164..bc16ddf1a 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -172,3 +172,14 @@ desired; owner ratification of OF-1…OF-8; then the owner-extended terminal del this branch + filed epic/sub-issues in Backlog/Triage + supersession closes). STAGE-COMPLETE: doc-story-integration + +## 2026-07-19 — Lane deliverable: RFC draft PR + +- Owner directive (in-turn, /remote-control): open the DRAFT RFC PR now; board filing stays a + later supervisor-coordinated step (drift D-6). +- `rfc.md` committed (`9ceb4a94`) — #822-format consolidation of the r3 corpus. +- **Draft PR #891 OPEN**: https://github.com/rickylabs/netscript/pull/891 — + `plan/deploy-plugin` → `main`, body = the RFC (Refs #820 #327 #823 #824 #825 #871, no closing + keywords); labels `rfc` `type:docs` `status:plan` `priority:p1` `area:deploy` `area:plugins` + `ci:skip-e2e` `ci:skip-scaffold`; milestone `Backlog / Triage`. Board numbers remain + DPB-placeholders; no epic/sub-issues filed; nothing merged. From 933f8e4df7d8109e3add052976b87fe66e7103f5 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 09:54:06 +0200 Subject: [PATCH 14/20] =?UTF-8?q?plan(deploy-plugin):=20Aspire=20pipeline?= =?UTF-8?q?=20composition=20pass=20(r4)=20=E2=80=94=20DP-9=20keep-or-deleg?= =?UTF-8?q?ate=20per=20seam?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner-directed pre-filing review: every pipeline seam examined against Aspire's shipped deployment stack (TS tab). Delegate on Aspire-managed targets: publish emission, deploy/ destroy, --list-steps dry-run, --environment selection + state cache, Parameters__ secrets convention. Keep: capability/topology verdict, artifact-manifest + up --prebuilt (aspire deploy does not consume published assets), status/logs/rollback, the TS env-overlay file (appsettings overlays are C#-only), workflow generation, the Deno OCI path (no upstream generic-executable publish helper; image builder C#-only). New: netscript-capability-check registered via the TS-available builder.pipeline.addStep. Radius (aspire#18696 merged, #18759 open) tracked as a future 'radius' target key on deploy-aspire (DPB-29). Family architecture, ports, and 29-child board unchanged. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../design/canonical/DP-2-deploy-core.md | 8 +- .../design/canonical/DP-3-adapter-cards.md | 13 +- .../design/canonical/DP-4-plugin-and-host.md | 6 +- .../canonical/DP-5-selective-wrapping.md | 2 +- .../canonical/DP-9-aspire-composition.md | 123 ++++++++++++++++++ .llm/runs/plan-deploy-plugin--seed/drift.md | 1 + .llm/runs/plan-deploy-plugin--seed/plan.md | 5 +- .llm/runs/plan-deploy-plugin--seed/rfc.md | 37 ++++++ .llm/runs/plan-deploy-plugin--seed/worklog.md | 24 ++++ 9 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 .llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md index 37e423a5f..a456ba4ba 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md @@ -227,7 +227,13 @@ interpreted by deploy — with requirements now expressed as `BindingRequirement supplies entries (as resolved `DeployTargetContribution` descriptors, DP-4 §3). Multi-target by design, keyed `[@]` — (r3, KF-8) invoked as **`netscript deploy --env `** (the flag maps to the qualified registry - key; the config `environments` overlay supplies the values). + key; the config `environments` overlay supplies the values). (r4, DP-9) On Aspire-managed + targets `--env` **passes through as `aspire --environment `** (case-insensitive; deploy/ + publish default aligned to Aspire's `production`); per-env provisioning state delegates to + Aspire's deployment state cache; the overlay file remains ours (`appsettings.{env}.json` is + C#-only — a documented TS gap, not duplication). Aspire-lane ops compose further per DP-9 §2: + `plan` surfaces `aspire deploy --list-steps`; `secrets` adopts the + `addParameter({secret:true})` + `Parameters__*` convention; `down` = `aspire destroy`. - **Config — two-phase loader** (**NEW**, SF-10). Today the full config parses `deploy` through a static schema *before* the plugin list is even available, and unknown target keys are silently stripped — so "adapters contribute schemas at load time" needs a real bootstrap diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md index 45819fbb7..b76ff8107 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md @@ -33,7 +33,18 @@ carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidenc `azure-app-service`, `azure-aks`: plan/up/down (+status where `aspire` reports it). AppHost platform-marker validation preserved. `rollback`: convention-backed for compose (previous emitted dir + dir-swap); platform-native where Azure provides it — else absent from - `operations`. + `operations`. (r4, DP-9) **Delegation deepened:** `down` prefers **`aspire destroy`** (the + pipeline teardown command); `plan` surfaces `aspire deploy --list-steps` as its pipeline-step + section; per-env provisioning state delegates to Aspire's deployment state cache + (`~/.aspire/deployments/{AppHostSha}/{env}.json`, `--clear-cache` surfaced, plaintext-secret + CI caution kept); `secrets` rides the `Parameters__*` convention. Binding is **CLI + + application-level pipeline steps only** (the callback-annotation surface is mid-migration + upstream and C#-only — DP-9 §4). +- **Radius watch (r4, DP-9 §3):** once microsoft/aspire#18759 (TS AppHost projection of the + merged #18696 `Aspire.Hosting.Radius`) ships in the pinned CLI, **`radius` joins this card as + a target key** — same `publish`(→`app.bicep`)/`deploy`(→`rad deploy`) verbs, control-plane + credentials (no cloud secrets in CI), capability rows from the environment's bound Recipes + (binding scope). Tracked in DPB-29; not claimed for v1. - **Manifest sketch (per variant: `compose`, `docker`, `kubernetes`, `azure-*`):** `process: long-lived`; sagas `supported` (long-lived containers). (r2, SF-7) Queue and exclusive-writer semantics are **`scope: 'binding'` verdicts composed from installed leaf diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md index eba5a0541..55c20faf0 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md @@ -143,7 +143,11 @@ dependencies. - **Scaffolder**: emits the userland `deploy/` leaf — `deploy/targets.ts` (typed target + cell definitions the user owns and edits — the named inversion of auth's never-rewritten barrel) plus per-target assets on `target add` (wrangler.jsonc, fly.toml, workflows…; - scaffold-stories doc). Golden tests per emitter (R-PLUGIN-PARITY). + scaffold-stories doc). Golden tests per emitter (R-PLUGIN-PARITY). (r4, DP-9 §2) On + Aspire-managed projects the generated AppHost helpers register a + **`netscript-capability-check` pipeline step** (`builder.pipeline.addStep(..., + { requiredBy: ['deploy'] })` — TS-available) so the capability compiler also gates a raw + `aspire deploy` invoked without the NetScript CLI. - **Doctor** (`deploy-target` check, contributed as data — §5): validates each configured target: adapter peer installed, descriptor loader resolves, required tool on PATH (`wrangler`, `aspire`, `docker`…), credentials env present (names only, never values), config diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md index 94dbb0765..9d9a544fa 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-5-selective-wrapping.md @@ -20,7 +20,7 @@ capability compiler, ports) — "own the semantics; rent the volatile provider t | Surface | Decision | Instrument | Rationale | | --- | --- | --- | --- | | Deploy semantics: ports, registry, capability compiler, conventions (activation/secrets/rollback/health/otel), binding transport | **Own, enterprise-grade** | `plugin-deploy-core` | This IS the product; no upstream owns these semantics (L-1). Already built target-agnostic in the CLI kernel — extraction, not invention. | -| Local/dev + self-host orchestration | **Aspire-native** | `@netscript/aspire` substrate + `deploy-aspire` shelling `aspire publish/deploy/destroy` | Aspire is where polyglot AppHost composition, dashboard, OTLP, and azure/k8s emission already work; reimplementing its publish pipeline fails the rule. Aspire stays the *composer/executor* — never enters the composition contract (same L-1 line as Nitro). | +| Local/dev + self-host orchestration | **Aspire-native** | `@netscript/aspire` substrate + `deploy-aspire` shelling `aspire publish/deploy/destroy/do` | Aspire is where polyglot AppHost composition, dashboard, OTLP, and azure/k8s emission already work; reimplementing its publish pipeline fails the rule. Aspire stays the *composer/executor* — never enters the composition contract (same L-1 line as Nitro). (r4) Delegation depth per seam codified in **DP-9** (emit/up/down/env-selection/state delegate; manifest, status/logs/rollback, overlay file, workflow generation, Deno OCI path stay ours); TS-available `builder.pipeline.addStep` is the sanctioned extensibility surface; **Radius** tracked as a future `deploy-aspire` target key (#18696 merged, #18759 open). | | Deno Deploy | **Wrap** `deno deploy` CLI + `deno.json` deploy section | `deploy-deno` | First-party, Deno-native, token+org auth; wrapping deployctl-era surfaces is dead (Classic sunset 2026-07-20). | | Cloudflare Workers | **Wrap** `wrangler` (invoke) + emit `wrangler.jsonc` + Web-standard entry | `deploy-cloudflare` (CF-PROBE) | Provider-owned Node tooling *callable from* Deno (adversarial F2 framing — honest, not "Deno-native"); the alternative (raw CF REST script upload) forfeits bindings/config/versions management wrangler owns. | | Cloudflare Containers / Fly / koyeb / sevalla / coolify / dokploy / (cloud-run) | **Wrap platform REST APIs** over one owned container path | `deploy-container` + subpath clients | All are image+env platforms (L-6); the owned piece is Dockerfile/OCI emission + the generic platform port; each client is a thin JSON mapping. | diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md new file mode 100644 index 000000000..31ce8568a --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md @@ -0,0 +1,123 @@ +# DP-9 — Aspire pipeline composition: keep-or-delegate per seam + +> **Draft — no GitHub mutation.** Canonical design doc of `plan-deploy-plugin--seed`, **r4** — +> the owner-directed pre-filing composition pass (PR #891 review comment, 2026-07-19). The +> family architecture (DP-0…DP-8, plan.md) stands as ratified; this addendum decides, per +> deployment-pipeline seam, whether the plugin **delegates to Aspire's shipped deployment +> stack** or **owns the seam** — and why. Evidence: aspire.dev deployment docs (TypeScript tab, +> `?aspire-lang=typescript`) + microsoft/aspire#18696 (merged) / #18759 (open); doctrine steer: +> Aspire is a first-class citizen; the non-Aspire fallback is deferred and must not shape the +> core. + +## 0. Ground truth that binds this addendum + +1. **Two Aspire verbs, one rule** — `aspire publish` = "a one-way handoff out of Aspire" + (target artifacts, parameters unresolved); `aspire deploy` = "Aspire stays in control … + resolve parameter values, and apply the deployment in one operation". Crucially: + **"`aspire deploy` does not consume previously published assets."** Aspire has no + internal publish-artifact → deploy hand-off; its documented CI shape is registry-tagged + images (`aspire do push`) + emitted files carried by upload-artifact + an external applier. +2. **The pipeline model is the extensibility surface** — a step DAG with well-known steps + (`Build`, `Push`, `Publish`, `Deploy` + prereqs), driven by `aspire do `, discoverable + without executing via `--list-steps`. **Aspire 13.0 replaced the callback-annotation system** + (`PublishingCallbackAnnotation`/`DeployingCallbackAnnotation` are listed as *removed* on the + pipelines page even though the custom-deployments page still teaches them) — do **not** + design against the annotation names. +3. **TS-availability split** — language-agnostic CLI (publish/deploy/do/destroy, + `--environment`, `--output-path`, `--list-steps`, `--clear-cache`), parameters/secrets + (`addParameter({secret:true})`, `Parameters__*`), compute-environment registration, and + **application-level `builder.pipeline.addStep(name, fn, {requiredBy/dependsOn})`** are all + available from a TypeScript AppHost. **C#-only today:** custom resource types, resource-level + `WithPipelineStepFactory`/`PipelineStep`, `IResourceContainerImageBuilder`, + `PipelineActivityReporter`, `appsettings.{env}.json` overlays ("Custom resource types and + callback-based deployment extensibility aren't yet available in the TypeScript AppHost + SDK"). +4. **No Deno publish helper** — the JS-apps deployment page covers Vite/Next/Svelte/Node + helpers with auto-generated Dockerfiles; a generic executable (how NetScript's Deno services + register) has **no documented publish path**. This gap is real and is exactly what + `deploy-container` fills. +5. **Deployment state caching** — `~/.aspire/deployments/{AppHostSha}/{environment}.json` + stores *prompted provisioning/parameter values* per app × environment; `--clear-cache` + deletes it; it may contain secrets in plaintext (CI caching needs access control). It is a + provisioning-answers memory — **not** a build-artifact cache and **not** rollback history. + +## 1. The delegation rule + +**When the project graph declares an Aspire compute environment, the pipeline mechanics belong +to Aspire; NetScript owns what Aspire does not have** — the capability/topology verdict, the +artifact-manifest hand-off contract, deploy-time `status`/`logs`/`rollback`, the environments +*overlay file* (a documented TS gap), workflow *generation*, and the Deno container path. The +eight-op port is unchanged; what changes is **how the Aspire-lane ops are realized** (delegation +depth), codified per seam below. + +## 2. Keep-or-delegate, per seam + +| Seam (RFC) | Verdict | Realization | +| --- | --- | --- | +| `emit` (Aspire-managed targets) | **DELEGATE** mechanics, **KEEP** the manifest | `aspire publish --output-path` emits the per-environment artifacts (compose+`.env`, k8s, Bicep, Helm); `EmittedArtifactManifest` wraps them — file list + digests + registry image tags. Aspire has no manifest contract; ours formalizes the hand-off its CI example leaves to upload-artifact conventions | +| `up` / `down` (Aspire lane) | **DELEGATE** | `aspire deploy` / **`aspire destroy`** (the pipeline teardown command — replaces the r3 card's compose-down-only story where applicable) | +| `plan` | **COMPOSE** | The capability/topology verdict is ours (Aspire has nothing comparable); on Aspire targets `plan` additionally surfaces `aspire deploy --list-steps` (the built-in no-execution dry-run) as the pipeline-step section of the plan output. `plan` stays pure | +| `up --prebuilt` | **KEEP** | Aspire explicitly does not consume published assets in `deploy`; our manifest + digest-verified applier IS the missing contract. On Aspire targets `--prebuilt` = pre-pushed registry tags (`aspire do push`) + the emitted files applied by the platform applier — the documented Aspire CI shape, formalized | +| `status` / `logs` / `rollback` | **KEEP** | No deploy-time Aspire equivalent (the dashboard is dev-time telemetry); conventions + platform-native mechanisms per adapter card, unchanged | +| `secrets` | **COMPOSE** | On Aspire targets the reference model adopts Aspire's parameter convention — `addParameter({secret:true})` + `Parameters__` env injection — instead of inventing a second channel; rotation/redaction lifecycle stays ours (Aspire only prompts/injects) | +| `--env` / environments overlay | **COMPOSE** | Selection **delegates**: NetScript `--env ` passes through as `aspire --environment ` (case-insensitive; align the default to Aspire's `production` on deploy/publish). The **overlay file stays ours**: `appsettings.{env}.json` overlays are C#-only — the RFC's `environments` config overlay fills a documented TS gap, it does not duplicate | +| Deployment state | **DELEGATE** (Aspire lane) | Per-env provisioning state lives in Aspire's cache (`{AppHostSha}/{env}.json`); the adapter surfaces `--clear-cache` and keeps the shipped CI guidance (never persist without access control — plaintext secrets). Orthogonal to our artifact manifest (build artifact ≠ prompted state) | +| OCI build (`deploy-container`) | **KEEP, converge later** | `ContainerBuildPort` stays: `IResourceContainerImageBuilder` is C#-only and no Deno/generic-executable publish helper exists — we fill the gap. Convergence path named: on Aspire-managed graphs prefer `aspire do build`/`aspire do push` as the build/push backend where the resource is one Aspire can build; revisit if upstream ships a Deno publish helper | +| CI workflows | **KEEP** (generator), **MODEL** on Aspire | Aspire documents an example workflow, it ships no generator. Our per-target workflow generation stays — and the Aspire-lane workflow adopts the documented shape verbatim: `aspire do push` (registry params via `Parameters__*` secrets) → `aspire publish --output-path` → upload-artifact → applier | +| Pipeline extensibility | **NEW integration point** | The TS-available `builder.pipeline.addStep(...)` lets the **generated NetScript AppHost helpers register deploy-plugin steps inside Aspire's own pipeline** — first candidate: a `netscript-capability-check` step `requiredBy: ["deploy"]`, so the capability compiler (L-3) also gates a raw `aspire deploy` invoked without the NetScript CLI. Resource-level factories/image-builder access would require a C#-side hosting integration (the #825 `NetScript.Aspire.Packaging` NuGet is the established vehicle) — **not v1**, recorded as the escape hatch | + +Net effect on the r3 corpus: **no port, package, or board-shape change.** The Aspire adapter +card deepens its delegation; two core notes (secrets convention pass-through, `--env` mapping) +and one plugin note (AppHost pipeline-step registration) are added; the rest of the family +(deno/container/cloudflare/vercel/aws lanes) is untouched — those platforms are precisely where +Aspire's pipeline does not reach. + +## 3. Radius — the tracked convergence (enterprise CI/CD) + +- **microsoft/aspire#18696 (merged 2026-07-10):** `Aspire.Hosting.Radius` makes Radius a + first-class **compute environment** — a peer of Kubernetes/DockerCompose/ACA, registered via + `AddRadiusEnvironment`, run-mode inert, with the full verbs: `aspire publish` emits a native + `app.bicep`; `aspire deploy` transparently runs `rad deploy`. In-code Azure/AWS providers with + typed credential modes sourced from secret parameters (never inlined). Follow-ups add recipe + parameters, secret stores, resource groups. +- **microsoft/aspire#18759 (open):** the same surface **projected into the TypeScript AppHost** + via ATS — cloud-provider callbacks and typed Bicep-infrastructure customization from TS. +- **Radius model** (docs.radapp.io): server-side control plane — Applications, Resource Types, + **Recipes** (Bicep/Terraform, per-Environment, swappable), Resource Groups; `rad deploy` + executes recipes via the deployment engine; **credentials live in the Radius control plane** + (`rad credential register azure|aws`), not the developer's shell. + +**Position:** Radius enters the family as a **`deploy-aspire` target key (`radius`)** — not a +new adapter package — because upstream already routes it through the exact verbs the adapter +wraps. Gate: #18759 merged + released in the pinned Aspire CLI + the TS surface stable. Its +capability manifest rows come from what the bound Recipes provide (binding-scope, per L-2). The +control-plane credential model is a **strength** for the enterprise story (no cloud secrets in +CI at all) and slots into the `secrets`-op reference model unchanged. Tracking home: the +DPB-29 deferred-RFC card (scope widened) + a watch item on the aspire-adapter card. + +## 4. Honest constraints (carried into docs and cards) + +1. TS AppHost extensibility is application-level `pipeline.addStep` only; anything + resource-level or the programmatic image builder needs the C#/ATS route (#825 vehicle) — + never claimed for v1. +2. Aspire's own docs currently disagree about the callback annotations (13.0 pipeline migration + vs the custom-deployments page); the adapter binds to the **CLI + pipeline-step surface + only**, which is stable across both readings. +3. Deployment state cache may hold plaintext secrets — the CI guidance (access-controlled + cache or re-prompt via `Parameters__*` env) ships with the Aspire-lane workflow template. +4. `aspire publish/deploy` default environment is **production** — the adapter aligns + NetScript's default rather than fighting it. + +## 5. Corpus amendments carried by this addendum (r4) + +- **DP-2 §2/§6:** `--env` pass-through mapping + default alignment; `secrets` adopts the + Aspire parameter convention on Aspire-managed targets; `plan` composes `--list-steps` there. +- **DP-3 §1 (deploy-aspire card):** `down` via `aspire destroy`; state-cache delegation + + `--clear-cache`; pipeline-step integration; Radius watch row (#18696/#18759). +- **DP-4 §4:** scaffolder's generated AppHost helpers register the `netscript-capability-check` + pipeline step. +- **DP-5 map:** Aspire row extended (pipeline steps + Radius); no other wrap change. +- **plan.md:** DPB-8 acceptance gains the pipeline-step + destroy + state-cache items; DPB-29 + scope gains the Radius graduation RFC; risk register: upstream doc/API churn on the pipeline + surface (mitigated by CLI-only binding). +- **rfc.md / PR #891:** Addendum A summarizing this document. diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md index f040ac4ee..f9badcc8c 100644 --- a/.llm/runs/plan-deploy-plugin--seed/drift.md +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -10,3 +10,4 @@ Append-only. Severity: `note` | `significant` | `architectural`. | D-4 | 2026-07-19 | note | Kickoff names "Kimi K3" for stage 3; `.llm/tools/agentic/config/models.ts` only binds `visionEval: openrouter/moonshotai/kimi-k2.6`. Stage-3 dispatch passes the K3 OpenRouter slug explicitly to the OpenCode launcher (owner-directed model; config update left to a framework slice — this run cannot edit repo code). | Slug `moonshotai/kimi-k3` verified against the public OpenRouter model list before dispatch; run completed on it. Follow-up: add a K3 binding to `config/models.ts` in a framework slice. | | D-5 | 2026-07-19 | note | Kimi doc-story caught two genuine r2 corpus defects the adversarial pass missed: the DP-6 preinstalled-default-target contradiction (KF-1) and `emit` being uncorroborated on the flagship `deno-deploy` target (KF-9 — resolved by NOT declaring `emit` there; getting-started flow is `plan → up`). Forecast Page A's Step 3 documents the pre-correction flow and is kept verbatim as the stage snapshot; the corpus (r3) is authority. | Integrated (`doc-story-kimi-triage.md`); future public docs follow the corpus. | | D-6 | 2026-07-19 | note | Kickoff stop-line "no PRs" partially lifted by owner directive (in-turn via /remote-control, 2026-07-19): open a DRAFT RFC PR (`rfc.md`, #822 format, Refs-only, colon labels, Backlog/Triage milestone) as the lane deliverable. Board filing (epic/sub-issues) and any merge remain forbidden pending owner ratification. | Executed: `rfc.md` committed; draft PR opened per the netscript-pr skill. | +| D-7 | 2026-07-19 | note | Owner pre-filing review pass (PR #891 comment): Aspire-composition analysis required before epic dispatch. Result (r4, DP-9): Aspire-lane seams delegate to the language-agnostic CLI (`publish/deploy/destroy/do`, `--list-steps`, `--environment`, state cache); NetScript keeps the manifest/--prebuilt contract, status/logs/rollback, the TS env-overlay file, workflow generation, and the Deno OCI path; new `builder.pipeline.addStep` capability-check integration; Radius tracked as a future `deploy-aspire` target key (aspire#18696/#18759). No port/package/board-shape change. | Integrated as r4; Addendum A on rfc.md + PR body. | diff --git a/.llm/runs/plan-deploy-plugin--seed/plan.md b/.llm/runs/plan-deploy-plugin--seed/plan.md index 2cd76d87a..2a0934f39 100644 --- a/.llm/runs/plan-deploy-plugin--seed/plan.md +++ b/.llm/runs/plan-deploy-plugin--seed/plan.md @@ -94,7 +94,7 @@ semantics land inside DPB-16/DPB-17's bodies. | DPB-5 | Capability + topology contracts (`CapabilityRef`/verdicts/variants; `DeploymentCell`/`suggestedCells`) + rejection compiler + conformance harness | W1 | **p0** | core contract, backend-truthful | DPB-1 | | DPB-6 | Two-phase config loader + base schema re-home + frozen legacy union | W1 | **p0** | config bootstrap contract (unknown target ⇒ error) | DPB-1 | | DPB-7 | Extract `deploy-baremetal` (build behavior, Servy/systemd, `BaremetalCompatibilityCommands`, systemd live probe) | W2 | p1 | adapter over core port + compat handlers | DPB-2, DPB-3 | -| DPB-8 | Extract `deploy-aspire` | W2 | p1 | adapter delegating to aspire CLI | DPB-2, DPB-3 | +| DPB-8 | Extract `deploy-aspire` (r4: + `aspire destroy` for down, `--list-steps` in plan, state-cache delegation + `--clear-cache`, `Parameters__*` secrets convention, `netscript-capability-check` pipeline step; Radius watch per DP-9) | W2 | p1 | adapter delegating to the aspire CLI + TS pipeline steps | DPB-2, DPB-3 | | DPB-9 | Extract `deploy-deno` (+ honest manifest rows; transitive-scan follow-up) | W2 | p1 | adapter wrapping `deno deploy` | DPB-3, DPB-5 | | DPB-10 | Adapter-side config member schemas over the schema registry (config debt retirement) | W2 | p1 | config re-home | DPB-6, DPB-7–DPB-9 | | DPB-11 | Legacy/config compatibility gate: state-transition + help goldens + unknown-target error paths | W2 | p1 | first-class compat verification (SF-9/SF-10) | DPB-7, DPB-6 | @@ -115,7 +115,7 @@ semantics land inside DPB-16/DPB-17's bodies. | DPB-26 | AWS-PROBE-HTTP (live LWA conformance) | W5 | p1 | probe card, findings-only | DPB-20 | | DPB-27 | `deploy-aws` (lambda variant, HTTP scope) + Story 2 | W5 | p1 | probe-gated adapter, HTTP-only | DPB-26 | | DPB-28 | Docs: target-matrix reference + per-target how-tos replace the alpha-minimal page | W3–W5 | p1 | docs consolidation (wave exits carry interim refreshes) | rolling | -| DPB-29 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation | backlog | p2 | RFC-first, leaf co-owned | DPB-27 | +| DPB-29 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation + **Radius target graduation** (`radius` key on `deploy-aspire`, gated on microsoft/aspire#18759 shipping in the pinned CLI — DP-9 §3) | backlog | p2 | RFC-first, leaf/upstream co-owned | DPB-27 | (Each future body: `Part of #EPIC` → scoping paragraph with anti-scope boundary → `- [ ] gate:` acceptance → `Dependencies:` + `Delivery shape:` — per board-parity conventions.) @@ -129,6 +129,7 @@ semantics land inside DPB-16/DPB-17's bodies. | Host-axis creep (`cli-command` opens a contribution firehose) | med | Closed collision rules; one axis, typed registry; doctrine 11 promotion test governs future axes | | Probe failures strand W5 adapters | med | Probes are findings-first cards; adapters ship only behind passing probes (L-7); W1–W4 value is independent of W5 | | Provider surface drift (wrangler/Vercel/Deno Deploy move fast) | med | Wrap at CLI process boundaries; compatibility dates pinned in emitted configs; manifest notes carry dates; conformance suite re-run per upgrade | +| Aspire pipeline-surface churn (13.0 callback→pipeline migration mid-flight; docs internally inconsistent) | med | (r4, DP-9 §4) Bind ONLY to the language-agnostic CLI (`publish/deploy/destroy/do`, `--list-steps`, `--environment`, `--clear-cache`) + the TS-available `builder.pipeline.addStep`; never the callback annotations or C#-only surfaces; resource-level needs route via the #825 NuGet vehicle if ever required | | Live-probe CI cost/credentials | med | Probe lanes are manually-triggered/secret-gated workflows, not default CI; in-memory suite covers default CI | | JSR surface growth (6+ new packages) | med | OF-2 subpath folding; F-5 export budgets; publish dry-run + jsr-audit per package before any release wave | | Leaf-backing dependency (catalog rows need leaf adapters that don't exist) | med | Catalog rows degrade to `external` honestly (DP-7 §2); leaf cards tracked separately (DPB-29) | diff --git a/.llm/runs/plan-deploy-plugin--seed/rfc.md b/.llm/runs/plan-deploy-plugin--seed/rfc.md index 4c50709a5..056f81d12 100644 --- a/.llm/runs/plan-deploy-plugin--seed/rfc.md +++ b/.llm/runs/plan-deploy-plugin--seed/rfc.md @@ -314,6 +314,43 @@ sugar (no story depends on it); deploy dashboard service; secrets rotation overl --- +## Addendum A — Aspire pipeline composition (owner review pass, 2026-07-19; corpus r4) + +Per the PR review comment, every deployment-pipeline seam was re-examined against Aspire's +shipped deployment stack (docs read on the TypeScript tab) — full analysis in +`design/canonical/DP-9-aspire-composition.md`. The family architecture is unchanged; the +Aspire-lane realization deepens its delegation: + +**Delegate (Aspire-managed targets):** `emit` mechanics → `aspire publish --output-path`; +`up`/`down` → `aspire deploy`/`aspire destroy`; `plan`'s pipeline-step section → +`aspire deploy --list-steps` (built-in no-execution dry-run); environment *selection* → +`--environment` (+ default aligned to Aspire's `production`); per-env provisioning state → +Aspire's deployment state cache (`--clear-cache` surfaced; plaintext-secret CI caution kept); +`secrets` injection → the `addParameter({secret:true})` + `Parameters__*` convention. + +**Keep (Aspire lacks it, or it's C#-only today):** the capability/topology verdict; the +`artifact-manifest.json` + `up --prebuilt` contract (Aspire states verbatim that +"`aspire deploy` does not consume previously published assets" — our manifest formalizes the +hand-off its CI example leaves to upload-artifact conventions); deploy-time +`status`/`logs`/`rollback`; the **environments overlay file** (`appsettings.{env}.json` is +C#-only — a documented TS gap we fill, not duplication); **workflow generation** (Aspire ships +an example, not a generator — ours adopts its documented `aspire do push` → `aspire publish` → +upload-artifact shape); and the **Deno OCI path** (`IResourceContainerImageBuilder` is C#-only +and no generic-executable publish helper exists — `deploy-container` fills a real gap, with +`aspire do build|push` named as the convergence backend). + +**New integration point:** the TS-available `builder.pipeline.addStep(...)` lets the generated +NetScript AppHost register a `netscript-capability-check` step `requiredBy: ["deploy"]` — the +capability compiler then gates even a raw `aspire deploy`. Binding is CLI + application-level +pipeline steps only: the callback-annotation surface is mid-migration upstream (13.0 pipeline +system) and C#-only; resource-level needs would route via the #825 NuGet vehicle (not v1). + +**Radius:** microsoft/aspire#18696 (merged — Radius as a first-class compute environment: +`publish`→`app.bicep`, `deploy`→`rad deploy`, control-plane credentials) + #18759 (open — the +full TypeScript AppHost projection) are tracked as a future **`radius` target key on +`deploy-aspire`** — not a new adapter package — gated on #18759 shipping in the pinned CLI +(DPB-29 scope; DP-9 §3). + **Provenance.** This PR lands the full planning record under `.llm/runs/plan-deploy-plugin--seed/`: six-surface research corpus (`research/`), canonical design docs `DP-0…DP-8` (r3), `plan.md` (locked decisions LD-1…12, the 29-child board table, diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index bc16ddf1a..927cdeb1b 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -183,3 +183,27 @@ STAGE-COMPLETE: doc-story-integration keywords); labels `rfc` `type:docs` `status:plan` `priority:p1` `area:deploy` `area:plugins` `ci:skip-e2e` `ci:skip-scaffold`; milestone `Backlog / Triage`. Board numbers remain DPB-placeholders; no epic/sub-issues filed; nothing merged. + +## 2026-07-19 — Owner review pass: Aspire pipeline composition (r4) + +- Owner review (via /remote-control + screenshot) transcribed as the structured PR #891 comment + (https://github.com/rickylabs/netscript/pull/891#issuecomment-5014908464): architecture stands; + one pass on the deployment-pipeline layer vs Aspire's shipped stack; Aspire-first doctrine + (non-Aspire fallback deferred); track Radius (microsoft/aspire#18696 merged, #18759 open). +- Research: Opus agent harvested aspire.dev deployment docs (TypeScript tab — Deploy with + Aspire, Environments, Pipeline model + custom pipelines, Deployment state caching, CI/CD + lifecycle, JavaScript apps) + docs.radapp.io. Load-bearing facts: `aspire deploy` does NOT + consume published assets; 13.0 replaced callback annotations with the pipeline system (docs + internally inconsistent); TS gets application-level `builder.pipeline.addStep` only; + `appsettings.{env}.json` overlays are C#-only; state cache = prompted-values memory keyed + AppHostSha×env; no Deno/generic-executable publish helper; CI example = `aspire do push` + + `aspire publish` + upload-artifact with an external applier. +- **DP-9-aspire-composition.md** (r4) written: per-seam keep-or-delegate table; the + `netscript-capability-check` pipeline-step integration; Radius position (`radius` target key + on deploy-aspire, gated on #18759); honest constraints (CLI-only binding, doc inconsistency, + plaintext-secret cache caution, production default alignment). +- Amendments: DP-2 §6 (--env→--environment pass-through, secrets Parameters__ convention, + plan --list-steps, aspire destroy); DP-3 §1 (delegation deepened + Radius watch); DP-4 §4 + (capability-check pipeline step in scaffolded AppHost); DP-5 (aspire row extended); plan.md + (DPB-8/DPB-29 scope, new pipeline-churn risk row); rfc.md Addendum A. Family architecture, + ports, and board shape unchanged. From d7b382625e4f4c46603f4a80e85d766bdf251351 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 09:57:45 +0200 Subject: [PATCH 15/20] plan(deploy-plugin): round-2 adversarial brief (Sol xhigh, r4 soundness) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../adversarial-brief-r4.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-brief-r4.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-brief-r4.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-brief-r4.md new file mode 100644 index 000000000..5323c685a --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-brief-r4.md @@ -0,0 +1,70 @@ +use harness + +# Brief — Round 2: adversarial/collaborative pass over corpus r4 (Sol · xhigh, same thread) + +You reviewed this run's r1 corpus (SF-1…SF-16). Since then, three amendment waves landed — this +round verifies the WHOLE architecture is still sound after them. Same constructive stance: +findings + concrete adoptable amendments, not demolition. + +## What changed since your review (read in this order) + +1. `adversarial-sol-triage.md` — all 16 of your findings ACCEPTED; integrated as **r2**. +2. `doc-story-kimi.md` + `doc-story-kimi-triage.md` — Kimi doc-story pass; 13 DX findings + (KF-1…13) ACCEPTED; integrated as **r3** (highlights: no preinstalled target; `deploy-deno` + declares NO `emit` — flow `plan → up`; `baremetal` one target with `windows|linux` variants; + `--env` grammar; `.deploy/[@]/artifact-manifest.json` `--prebuilt` contract; + `cells apply` + selector vocabulary; `target remove` semantics; capability preview catalog; + verdict-surface precedence; op grammar sketch). +3. **`design/canonical/DP-9-aspire-composition.md`** + r4 amendments (DP-2 §2/§6, DP-3 §1, + DP-4 §4, DP-5, plan.md DPB-8/DPB-29 + risk row, `rfc.md` Addendum A) — the owner-directed + Aspire-composition pass: per-seam keep-or-delegate, the `netscript-capability-check` + pipeline-step integration, Radius as a future `deploy-aspire` target key. +4. `rfc.md` — the RFC now mirrored as draft PR #891's body (do NOT touch the PR). + +## Attack surface (round 2) + +1. **DP-9 delegation verdicts** — attack each keep/delegate call. Examples worth pressure: + is `plan` still *pure* while surfacing `aspire deploy --list-steps`? Does the + `--env → aspire --environment` pass-through with the production default conflict with the + config `environments` overlay semantics or the r3 `--env` registry-key mapping? Does + adopting `addParameter({secret:true})` + `Parameters__*` for `secrets` on Aspire targets + stay consistent with the secrets-reference/redaction law (values in Aspire's state cache are + plaintext — is the caveat placement sufficient)? Is `up --prebuilt` on Aspire-managed + targets concrete — given DP-9 §0's ground truth that `aspire deploy` does not consume + published assets, WHO is the applier per target (compose vs k8s vs azure) and does the card + say so? +2. **Cross-revision consistency sweep** — three amendment waves touched DP-0…DP-9, plan.md, + rfc.md. Hunt contradictions and staleness: op-count naming, `emit` claims vs the deno card, + preinstalled-target statements, baremetal naming (config keys vs variants vs registry keys), + `DPB-n` references, DP-N §x cross-references that moved, anything the amendments orphaned. + `rfc.md` is a summary — flag any place it now misstates the corpus it summarizes. +3. **The `netscript-capability-check` pipeline step** (DP-4 §4, DP-9 §2) — registration point, + behavior when the deploy plugin is absent from the project, double-gating semantics + (NetScript CLI plan AND the aspire pipeline step both run the compiler — divergence risk?), + failure UX inside `aspire deploy`. +4. **Radius position** (DP-9 §3) — is "a `radius` target key on `deploy-aspire`" the right + shape vs a separate variant/adapter? Is the gating condition (#18759 shipped in the pinned + CLI + TS surface stable) well-formed and testable? +5. **Anything else** the three waves broke or weakened — board DAG (29 children), gate + selection, migration map, owner-fork table accuracy. + +Verification: corpus + repo ground truth first-hand (as in round 1). The Aspire facts in DP-9 +§0 carry citations from same-day doc reads — treat them as given unless internally inconsistent +or contradicted by something you can check; if your tooling can fetch aspire.dev, add +`?aspire-lang=typescript`. + +## Output contract (strict) + +1. Write `.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4.md`: H1 + one-paragraph + overall verdict (is r4 sound to file and implement against?), findings `SG-1…SG-n` with + `[BLOCKER|MAJOR|MINOR|ENHANCE]`, one-line claim, evidence (corpus/repo refs), concrete + suggested amendment; then `## Quick wins`. +2. Commit ONLY that file (message: + `plan(deploy-plugin): Sol r4 adversarial findings — aspire composition + consistency sweep`) + and push with `git push origin HEAD:plan/deploy-plugin`. +3. End your final response with exactly `DONE` (or `BLOCKED: `). + +## Stop-lines (unchanged) + +Findings only — no edits to any other file; no GitHub mutations (no PR/issue/label/milestone +touches); no `packages/`/`plugins/` source edits; no other agents; this branch only. From 38ccc2c55687cb6d12143068b94739645a3a17eb Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 10:05:30 +0200 Subject: [PATCH 16/20] =?UTF-8?q?plan(deploy-plugin):=20Sol=20r4=20adversa?= =?UTF-8?q?rial=20findings=20=E2=80=94=20aspire=20composition=20+=20consis?= =?UTF-8?q?tency=20sweep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adversarial-sol-r4.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4.md new file mode 100644 index 000000000..e9b535708 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4.md @@ -0,0 +1,86 @@ +# Constructive adversarial review — corpus r4 + +The r4 corpus is still a strong architecture to build on, but it is **not yet sound to file or implement unchanged**. The Aspire delegation rule is directionally right and the earlier r2/r3 repairs remain intact; the remaining defects cluster at the new composition boundary. In particular, the design promises a portable prebuilt hand-off without naming its platform appliers, permits Aspire's plaintext state cache to undercut the family secret law, and assigns the raw-`aspire deploy` capability gate to a board card that cannot yet access its compiler or generated-AppHost integration point. These are bounded amendments rather than reasons to revisit the family topology: resolve the blockers below, make environment identity and compatibility behavior explicit, and r4 becomes fileable. + +## Findings + +### SG-1 [BLOCKER] `up --prebuilt` has no executable per-target contract for Aspire outputs. + +**Claim:** DP-9 correctly observes that `aspire deploy` cannot consume published assets, but “the platform applier” is a placeholder: neither the adapter card nor the board names the executable, input shape, ownership, permissions, or rollback/status pairing for compose, Kubernetes, and each Azure target. + +**Evidence:** DP-9 §0.1 and §2 (`up --prebuilt`); DP-3 §1 lines 32–58; DP-2 §2 lines 67–73; plan.md DPB-8. The shipped compose adapter already demonstrates that this is target-specific—`compose` applies an emitted file with `docker compose`, while the `docker` lane calls `aspire deploy` (`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:100`); the latter cannot satisfy the new prebuilt contract. + +**Suggested amendment:** Add an Aspire applier matrix to DP-9 §2 and DP-3 §1, and make it DPB-8 acceptance scope: `compose → docker compose -f up -d`; `kubernetes → kubectl apply -f ` (or one explicitly selected Helm path); each `azure-* → a named Azure-native command/API consuming the emitted Bicep/manifest and pre-pushed immutable image tags`. Require every row to declare `ApplierPort` implementation owner, accepted manifest entries, required tools/permissions, idempotency key, status/log/rollback mechanism, and a digest-before-first-mutation conformance test. Until a row exists, that variant must omit `up --prebuilt` and its CI split must not be advertised. + +### SG-2 [BLOCKER] Aspire secret composition violates the corpus's non-persistence posture unless secret parameters are excluded from deployment-state caching by construction. + +**Claim:** A warning that the Aspire cache “may” contain plaintext secrets is not an adequate realization of a reference/redaction convention whose tests otherwise prove values never escape secret channels. A generated CI workflow can accidentally persist the cache, and a local deploy leaves secret material under a predictable home-directory path. + +**Evidence:** DP-9 §0.5, §2 (`secrets`, `Deployment state`), and §4.3; DP-2 §2 lines 76–77 and §7 lines 267–268; DP-3 §1 lines 38–40; RFC Addendum A lines 327–329. The shipped request already distinguishes `clearCache` as “clear and avoid saving deployment state” (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts:58`), so the design has a usable policy seam. + +**Suggested amendment:** Replace the Aspire secret/state wording with: “Secret parameters are supplied only through process environment (`Parameters__`) or an interactive prompt whose value is never persisted by NetScript. Every NetScript-driven Aspire operation that can resolve secret parameters runs in no-save/clear-cache mode; Aspire state persistence is allowed only for a filtered allow-list of non-secret provisioning answers. If the pinned CLI cannot prove that separation, the adapter disables state persistence entirely and doctor fails closed when an existing cache contains keys mapped to secret parameters.” Add local and CI tests proving a sentinel secret is absent from the deployment cache, artifact tree, manifest, argv, logs, telemetry, and errors. + +### SG-3 [BLOCKER] The capability pipeline step is assigned before its compiler, plugin, and generated-AppHost registration path can coexist. + +**Claim:** DPB-8 is a W2 adapter extraction depending only on DPB-2/3, yet r4 adds a step that calls the DPB-5 capability compiler and is emitted through the W3 plugin scaffolder. The current AppHost is generated by the CLI and has no deploy-specific registration seam, so DPB-8 cannot deliver or prove the feature in its stated DAG position. + +**Evidence:** DP-4 §4 lines 143–150 assigns registration to the plugin scaffolder; plan.md lines 94–107 assigns the compiler to DPB-5, the step to DPB-8, the plugin to DPB-15, and scaffolding to DPB-17, while DPB-8 depends only on DPB-2/3. Repo generation writes `apphost.mts` and helpers from the CLI pipeline (`packages/cli/src/kernel/application/scaffold/render-ts-apphost.ts:237`); the entry template only creates the builder and calls `createNetScriptAppHost` (`packages/cli/src/kernel/assets/aspire/helpers/apphost.ts.template:6`). + +**Suggested amendment:** Remove pipeline-step delivery from DPB-8 and add it to DPB-17 (or create one additional child while remaining under 30) with dependencies on DPB-5, DPB-8, and DPB-15. DPB-8 should expose only an adapter-neutral `runCapabilityCheck` callable. The generated helper must register the Aspire step only when the deploy plugin and at least one Aspire target descriptor are resolved; without the plugin it emits no import and no step, so `aspire start/deploy` remains valid. + +### SG-4 [MAJOR] Running the compiler twice creates two potentially different deployment verdicts and an unexplained raw-Aspire failure experience. + +**Claim:** `netscript deploy … plan` and `netscript-capability-check` independently recompiling the graph can disagree because installed manifests, overlays, generated registries, or live evidence changed between invocations. The corpus does not define which input snapshot the Aspire step uses, its structured failure contract, or how a user recovers from a failed `aspire deploy`. + +**Evidence:** DP-9 §2 (`plan`, `Pipeline extensibility`); DP-4 §4 lines 147–150; DP-2 §4 gives plan authoritative precedence, while r4 introduces a second deploy gate without revising that rule. DP-9 §0.2 establishes that the step is part of Aspire's DAG, so its error is experienced through Aspire rather than NetScript's command renderer. + +**Suggested amendment:** Define one pure compiler entrypoint and one versioned `CapabilityCheckInput` snapshot (graph digest, effective environment, registry digest, manifest ids/versions/probe dates). NetScript plan embeds that snapshot/digest; the Aspire step invokes the same entrypoint and either verifies the supplied snapshot or recompiles and reports that it superseded it. Standardize failure output as one concise summary plus the exact recovery command (`netscript deploy plan --env `), a diagnostics path, and stable nonzero error code. Add parity tests asserting identical verdict JSON for NetScript CLI and raw Aspire entrypoints over the same snapshot. + +### SG-5 [MAJOR] Omitted Aspire environments acquire two identities and two artifact paths. + +**Claim:** r3 defines environment identity as `@` and `.deploy/[@]`, while r4 makes omitted `--env` mean Aspire `production`. That leaves the same effective deployment addressable as both bare `` and `@production`, with different overlay selection, registry collision behavior, manifest paths, and state-cache keys. + +**Evidence:** DP-2 §2 lines 69–73 and §6 lines 228–234; DP-9 §2 (`--env / environments overlay`) and §4.4; RFC §3.1 lines 118–125 and Addendum A lines 326–328. + +**Suggested amendment:** Add a single `resolveDeploymentEnvironment(target, requestedEnv)` rule before registry/config/artifact resolution: for Aspire targets, omitted means canonical effective environment `production`; registry lookup, overlay merge, artifact path, manifest field, cache key, events, and Aspire `--environment` all use that same normalized value. Define overlay order as base target settings → `environments.production` → CLI flags. Reject simultaneous bare and `@production` registrations as aliases of one identity. Non-Aspire adapters retain their explicitly declared default rather than inheriting Aspire's default. + +### SG-6 [MAJOR] The Aspire adapter card contradicts r4 about `emit`, `secrets`, and the operation subsets. + +**Claim:** DP-9 delegates `emit` for Aspire-managed targets and composes `secrets`, but DP-3's authoritative target/ops row omits `emit` and `secrets` for every Aspire target; it also says the extraction moves shipped behavior “unchanged” although r4 deliberately changes plan/down/state behavior. + +**Evidence:** DP-3 §1 lines 28–42 versus DP-9 §2; DP-2 §2 lines 91–96. Repo ground truth advertises `emit` today for compose/docker and cloud targets (`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:63`; `packages/cli/src/kernel/adapters/aspire/aspire-cloud-deploy-target.ts:124`). + +**Suggested amendment:** Replace DP-3 §1's ops paragraph with an explicit target × operation table. At minimum, declare `emit` wherever `aspire publish` produces a supported manifest, and declare `secrets` only where set/list/unset semantics—not merely environment injection—are implemented. Mark `status/logs/rollback` per target rather than “where Aspire reports it.” Replace “shipped behavior moved … unchanged” with “extracted behind compatibility behavior first; r4 changes activate only behind the plugin-era canonical verbs and the migration rules below.” Generate `operations` and docs from this table. + +### SG-7 [MAJOR] The compatibility contract incorrectly says Aspire behavior is unchanged. + +**Claim:** Pure plan replaces today's artifact-writing Aspire plan; down may change from `docker compose down` to `aspire destroy`; environment defaults, state caching, and secret injection also change. These are user-visible semantics, not merely a package-owner move, so DP-6 and the RFC currently promise compatibility the design does not preserve. + +**Evidence:** DP-2 §2 lines 36–39 acknowledges the shipped Aspire plan writes artifacts; DP-6 §3 item 4 says Aspire lanes are “unchanged in behavior”; RFC §6 lines 268–271 repeats that claim. Current source confirms `plan()` calls `aspire publish` for compose (`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:90`) and `down()` calls Docker Compose (`packages/cli/src/kernel/adapters/aspire/aspire-compose-deploy-target.ts:123`). + +**Suggested amendment:** Amend DP-6 §3 and RFC §6 to enumerate the plugin-era changes: legacy Aspire invocations retain their current publish-on-plan and teardown behavior through the compatibility shim; canonical `plan` is pure and canonical `emit` publishes; canonical `down` uses the target's declared teardown executor; omitted Aspire environment normalizes to `production`; cache/secret policy follows SG-2. Require golden argv, artifact-side-effect, and exit-code tests for both legacy and canonical paths through the compatibility window. + +### SG-8 [MAJOR] Radius graduation is not yet a testable target contract. + +**Claim:** “#18759 merged + released in the pinned CLI + TS surface stable” depends on issue state and an undefined stability judgment. Treating Radius as one static target key also risks lying about capabilities that DP-9 correctly says vary by bound Recipes and environment. + +**Evidence:** DP-9 §3; DP-3 §1 lines 43–47; plan.md DPB-29. The corpus's own capability law requires variant- and binding-scoped evidence rather than provider-wide claims (DP-2 §4; DP-3 §0). + +**Suggested amendment:** Keep `radius` inside `deploy-aspire`, but define it as an Aspire compute-environment variant selected by a descriptor—not an unconditional static capability profile. Replace the gate with machine-verifiable predicates: pinned Aspire version is at least the first recorded release containing the TS API; `deno check` of a minimal `addRadiusEnvironment` fixture passes; `aspire publish --environment ` emits `app.bicep`; `aspire deploy --list-steps` exposes the expected Radius deployment step; and a fixture proves recipe-derived binding verdicts. Publish only runtime/process traits statically; discover Recipe/resource-group capabilities per environment and mark them `unverified` until probed. + +### SG-9 [MINOR] “Plan stays pure” needs an explicit subprocess-side-effect boundary for `--list-steps`. + +**Claim:** Calling an external AppHost command can restore dependencies, evaluate user AppHost code, and mutate tool caches even when it does not execute deployment steps. The current purity statement only names artifact directories and providers, while the RFC's “nothing mutated” phrasing is broader and therefore unsafe. + +**Evidence:** DP-2 §2 lines 49–51 defines purity relative to artifact directories/providers; DP-9 §2 calls `--list-steps` a no-execution dry run; RFC line 28 says “nothing mutated.” + +**Suggested amendment:** Define plan purity as “no workspace-owned deployment artifacts and no provider mutation”; permit documented toolchain cache/restore effects in an isolated subprocess. Capture `--list-steps` stdout/stderr/exit status in diagnostics, use non-interactive mode, and fail with a distinct `AspirePipelineInspectionError` rather than converting inspection failure into a capability rejection. Replace RFC “nothing mutated” with the scoped definition. + +## Quick wins + +- Fix the shipped port comment from “uniform 7-op” to “eight-op or declared subset” when DPB-1 moves it (`packages/cli/src/kernel/domain/deploy/deploy-target-port.ts:2`); include the matching doctrine/gate wording update in DPB-1 acceptance so the stale count is not copied forward. +- Add `emit`, `--environment`, `--clear-cache`, the platform applier binaries, and secret-state paths to the Aspire adapter permission/doctor matrix; DP-3 §1 currently lists only `aspire,docker` and output directories. +- In RFC Addendum A, say the generated helper registers the step **conditionally** and name its owning DPB card after SG-3 is applied. +- Update RFC provenance from “DP-0…DP-8 (r3)” to “DP-0…DP-9 (r4)” (`rfc.md:356`). +- Expand RFC's DPB-29 board summary to mention Radius; the full plan table has it, but RFC §5 lines 259–261 still summarizes only AWS events and leaf backing graduation. +- Remove the duplicated risk-table header in `plan.md` lines 123–126 before filing. From 75523e60c8f629f11a1adb98d7fa45aa540d5dc4 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 10:10:16 +0200 Subject: [PATCH 17/20] =?UTF-8?q?plan(deploy-plugin):=20integrate=20Sol=20?= =?UTF-8?q?round-2=20(r5)=20=E2=80=94=20all=209=20findings=20accepted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SG-1 prebuilt applier matrix (no row, no op); SG-2 no-save secret/state policy with fail-closed doctor; SG-3 pipeline step re-homed to DPB-17 with conditional registration; SG-4 single compiler entrypoint + CapabilityCheckInput snapshot with parity tests; SG-5 resolveDeploymentEnvironment normalization (alias registrations rejected); SG-6 aspire target-by-op table as the generated operations source; SG-7 honest compatibility enumeration (legacy via shim, canonical changes deliberate); SG-8 predicate-gated Radius variant with per-environment unverified recipe capabilities; SG-9 scoped plan purity + AspirePipelineInspectionError. Quick wins applied; family topology, ports, and the 29-child board unchanged. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../adversarial-sol-r4-triage.md | 26 +++++++ .../design/canonical/DP-2-deploy-core.md | 23 +++++- .../design/canonical/DP-3-adapter-cards.md | 36 ++++++--- .../design/canonical/DP-4-plugin-and-host.md | 9 ++- .../design/canonical/DP-6-migration-map.md | 10 ++- .../canonical/DP-9-aspire-composition.md | 74 +++++++++++++++++-- .llm/runs/plan-deploy-plugin--seed/drift.md | 1 + .llm/runs/plan-deploy-plugin--seed/plan.md | 8 +- .llm/runs/plan-deploy-plugin--seed/rfc.md | 8 +- .llm/runs/plan-deploy-plugin--seed/worklog.md | 22 ++++++ 10 files changed, 183 insertions(+), 34 deletions(-) create mode 100644 .llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4-triage.md diff --git a/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4-triage.md b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4-triage.md new file mode 100644 index 000000000..bb0e17520 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/adversarial-sol-r4-triage.md @@ -0,0 +1,26 @@ +# Triage — Sol round-2 findings (SG-1…SG-9, corpus r4) + +Supervisor triage of `adversarial-sol-r4.md` (commit `38ccc2c5`). Verdict adopted: architecture +sound, **not fileable unchanged** — defects cluster at the new Aspire composition boundary. All +nine findings **ACCEPTED**; fixes land as corpus **r5** (this commit series). Family topology, +ports, and the 29-child board count are unchanged. + +| Finding | Sev | Disposition | Landed in | +| --- | --- | --- | --- | +| SG-1 `up --prebuilt` has no per-target applier contract for Aspire outputs | BLOCKER | **ACCEPT** — Aspire **applier matrix** added (`compose → docker compose -f … up -d`; `kubernetes → kubectl apply -f …`; `azure-* → named Azure-native command over emitted Bicep + pre-pushed immutable tags`), each row declaring `ApplierPort` owner, accepted manifest entries, tools/permissions, idempotency key, status/log/rollback pairing, digest-before-first-mutation test; **a variant without a row omits `up --prebuilt`** and must not advertise the CI split | DP-9 §2, DP-3 §1, plan DPB-8 | +| SG-2 Aspire plaintext state cache undercuts the secret law | BLOCKER | **ACCEPT** — secret parameters only via process env (`Parameters__*`) or never-persisted prompt; **every secret-resolving Aspire operation runs no-save/clear-cache**; state persistence only for an allow-list of non-secret provisioning answers; if the pinned CLI cannot prove the separation, persistence is disabled and **doctor fails closed** on a cache containing secret-mapped keys; sentinel-secret absence tests (cache, artifacts, manifest, argv, logs, telemetry, errors) | DP-9 §2/§4, DP-3 §1, DP-2 §7 | +| SG-3 Pipeline step assigned before its dependencies exist | BLOCKER | **ACCEPT** — step delivery moved **DPB-8 → DPB-17** (deps DPB-5, DPB-8, DPB-15); DPB-8 exposes only an adapter-neutral `runCapabilityCheck` callable; the generated helper registers the step **conditionally** (deploy plugin present + ≥1 Aspire target descriptor resolved; otherwise no import, no step — raw `aspire start/deploy` stays valid) | plan DPB-8/DPB-17, DP-4 §4, DP-9 §2 | +| SG-4 Double-compile divergence + raw-Aspire failure UX | MAJOR | **ACCEPT** — one pure compiler entrypoint + versioned **`CapabilityCheckInput` snapshot** (graph digest, effective env, registry digest, manifest ids/versions/probe dates); plan embeds it; the Aspire step verifies-or-supersedes it; standardized failure: one-line summary + recovery command (`netscript deploy plan --env `) + diagnostics path + stable exit code; parity tests (identical verdict JSON via CLI and raw Aspire) | DP-2 §4, DP-9 §2, DP-4 §4 | +| SG-5 Omitted Aspire env yields two identities (`` vs `@production`) | MAJOR | **ACCEPT** — single `resolveDeploymentEnvironment(target, requestedEnv)` normalization before registry/overlay/artifact resolution; one normalized value everywhere (registry key, overlay merge order base→`environments.`→flags, artifact path, manifest field, cache key, events, `--environment`); bare + `@production` registrations rejected as aliases; non-Aspire adapters keep their own declared defaults | DP-2 §6, DP-9 §2 | +| SG-6 Aspire card contradicts r4 (`emit`/`secrets` missing; "unchanged" wording) | MAJOR | **ACCEPT** — DP-3 §1 ops paragraph replaced by an explicit **target × operation table** (emit wherever `aspire publish` yields a supported manifest — repo ground truth already advertises it; secrets only where set/list/unset semantics exist, not mere env injection); "moved unchanged" → "extracted behind compatibility behavior first; r4/r5 changes activate behind the plugin-era canonical verbs"; `operations` + docs generated from the table | DP-3 §1 | +| SG-7 Compatibility contract overstates "Aspire unchanged" | MAJOR | **ACCEPT** — DP-6 §3 + RFC §6 enumerate the plugin-era changes (legacy invocations keep publish-on-plan/compose-down via the compat shim; canonical plan pure / emit publishes / down uses the declared teardown executor; env normalization; SG-2 cache policy); golden argv/side-effect/exit-code tests on both paths | DP-6 §3, rfc.md §6 | +| SG-8 Radius gate untestable; static capability risk | MAJOR | **ACCEPT** — `radius` = descriptor-selected Aspire compute-environment **variant**; gate replaced with machine-verifiable predicates (pinned CLI ≥ first release with the TS API; `deno check` of a minimal `addRadiusEnvironment` fixture; `aspire publish` emits `app.bicep`; `--list-steps` shows the Radius step; recipe-derived binding-verdict fixture); only runtime/process traits static — Recipe/resource-group capabilities per-environment, `unverified` until probed | DP-9 §3, DP-3 §1, plan DPB-29 | +| SG-9 Plan "purity" under `--list-steps` needs a boundary | MINOR | **ACCEPT** — purity defined as "no workspace-owned deployment artifacts, no provider mutation"; documented toolchain cache/restore effects permitted in an isolated non-interactive subprocess; inspection output captured to diagnostics; failure = `AspirePipelineInspectionError`, never a capability rejection; RFC "nothing mutated" scoped | DP-2 §2, DP-9 §2, rfc.md | + +**Quick wins:** applied — DPB-1 acceptance gains the shipped-port comment fix ("uniform 7-op" → +eight-op/declared-subset, incl. doctrine/gate wording); DP-3 §1 permission/doctor matrix extended +(applier binaries, `--environment`, `--clear-cache`, secret-state paths); RFC Addendum A says the +helper registers the step **conditionally** and names DPB-17; rfc.md provenance corrected to +`DP-0…DP-9 (r5)`; rfc.md §5 DPB-29 summary now mentions Radius. **Not reproducible:** the +"duplicated risk-table header (plan.md 123–126)" — HEAD has exactly one header at line 125; +treated as a stale line-number read, no change needed. diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md index a456ba4ba..7b8de07cc 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-2-deploy-core.md @@ -46,9 +46,13 @@ interface DeployTargetPort { readonly key: DeployTargetKey; readonly operations: readonly DeployOperation[]; // declared subset (F-DEPLOY-1) readonly capabilities: DeployCapabilityManifest; // §4 — NEW field, backend-truthful - plan(ctx): Promise; // PURE wrt artifact dirs and providers: resolves - // topology, validates capabilities, returns a - // serializable DeploymentPlan (+ diagnostics) + plan(ctx): Promise; // PURE (r5, SG-9): writes no workspace-owned + // deployment artifacts, mutates no provider; + // isolated toolchain-inspection subprocesses + // (e.g. --list-steps) are permitted and captured + // to diagnostics. Returns a serializable + // DeploymentPlan embedding its + // CapabilityCheckInput snapshot (SG-4) emit(ctx): Promise; // materializes content-addressed artifacts; does // not deploy/push unless the format models a // publish phase explicitly @@ -175,6 +179,19 @@ interface DeployCapabilityManifest { run supersedes a stale (`probedAt`-old) manifest; `plan` recompiles from the **installed** manifests at invocation time and is authoritative for the deploy decision; `deploy capabilities` renders published/installed manifest data and is informational. +- **One compiler entrypoint, one snapshot** (r5, SG-4): `compileCapabilityVerdict` is a pure + function over a versioned `CapabilityCheckInput` snapshot (graph digest, effective + environment, generated-registry digest, manifest ids/versions/probe dates). Every consumer — + `plan`, scaffold-time selection, the `netscript-capability-check` Aspire pipeline step — + invokes this one entrypoint; a consumer holding a prior snapshot either verifies it or + recompiles and reports supersession. Parity tests assert identical verdict JSON across + consumers for the same snapshot. +- **Environment normalization** (r5, SG-5): `resolveDeploymentEnvironment(target, requestedEnv)` + runs before registry/overlay/artifact resolution and yields ONE value used everywhere + (registry key, overlay merge order base → `environments.` → flags, artifact path, + manifest field, cache key, events, provider pass-through). Aspire targets normalize omitted + env to `production`; bare and `@production` registrations are rejected as aliases; non-Aspire + adapters keep their own declared defaults. ## 5. Topology: cells are declared, never silently partitioned (**NEW**, SF-8) diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md index b76ff8107..f16e00c23 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-3-adapter-cards.md @@ -29,17 +29,25 @@ carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidenc `aspire-compose-deploy-target.ts` / `aspire-cloud-deploy-target.ts` unchanged; `docker compose` shelling for the self-host lane. `@netscript/aspire` (SDK-neutral composition) stays a separate substrate package — this adapter is the *executor*, that package is the *composer*. -- **Targets/ops:** `compose`, `docker`: plan/up/down/status/logs; `kubernetes`, `azure-aca`, - `azure-app-service`, `azure-aks`: plan/up/down (+status where `aspire` reports it). AppHost - platform-marker validation preserved. `rollback`: convention-backed for compose (previous - emitted dir + dir-swap); platform-native where Azure provides it — else absent from - `operations`. (r4, DP-9) **Delegation deepened:** `down` prefers **`aspire destroy`** (the - pipeline teardown command); `plan` surfaces `aspire deploy --list-steps` as its pipeline-step - section; per-env provisioning state delegates to Aspire's deployment state cache - (`~/.aspire/deployments/{AppHostSha}/{env}.json`, `--clear-cache` surfaced, plaintext-secret - CI caution kept); `secrets` rides the `Parameters__*` convention. Binding is **CLI + - application-level pipeline steps only** (the callback-annotation surface is mid-migration - upstream and C#-only — DP-9 §4). +- **Target × operation table (r5, SG-6 — `operations` and docs are GENERATED from this table; + a blank cell means the op is absent from `operations`):** + + | Variant | plan | emit | up | up --prebuilt | down | status | logs | rollback | secrets | + | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | + | `compose` | pure + `--list-steps` | `aspire publish` | `aspire deploy` | applier: `docker compose -f … up -d` | `aspire destroy` (compat shim keeps `docker compose down`) | `docker compose ps` | `docker compose logs` | convention (retained emitted dir + dir-swap) | `Parameters__*` injection only — no set/list/unset ⇒ **not declared** until implemented | + | `docker` | pure + `--list-steps` | `aspire publish` | `aspire deploy` | — (no applier row yet) | `aspire destroy` | — | — | — | — | + | `kubernetes` | pure + `--list-steps` | `aspire publish` (manifests/Helm chart) | `aspire deploy` | applier: `kubectl apply -f …` | `aspire destroy` | — | — | — | — | + | `azure-aca` / `azure-app-service` / `azure-aks` | pure + `--list-steps` | `aspire publish` (Bicep) | `aspire deploy` | — (row absent until an Azure-native applier is proven — DP-9 §2a.1) | `aspire destroy` | — | — | platform-native where Azure provides it | — | + + AppHost platform-marker validation preserved. (r5, SG-6/SG-7) **Extraction wording + corrected:** shipped behavior is *extracted behind compatibility behavior first* — legacy + invocations keep today's semantics (publish-on-plan, compose-down) through the compat shim; + the r4/r5 changes (pure plan, `aspire destroy`, env normalization, no-save secret policy) + activate **only behind the plugin-era canonical verbs**. Per-env state delegates to Aspire's + deployment state cache under the **SG-2 policy**: secret-resolving operations run + no-save/clear-cache; persistence only for the non-secret allow-list; doctor fails closed on a + cache containing secret-mapped keys. Binding is **CLI + application-level pipeline steps + only** (the callback-annotation surface is mid-migration upstream and C#-only — DP-9 §4). - **Radius watch (r4, DP-9 §3):** once microsoft/aspire#18759 (TS AppHost projection of the merged #18696 `Aspire.Hosting.Radius`) ships in the pinned CLI, **`radius` joins this card as a target key** — same `publish`(→`app.bicep`)/`deploy`(→`rad deploy`) verbs, control-plane @@ -55,7 +63,11 @@ carry `schemaVersion`, adapter + upstream tool versions, probe date, and evidenc - **Scaffold hooks:** owns the `deploy-compose-ghcr.yml` workflow template; keeps emitting nothing hand-authored (aspire publish output stays the artifact) until W4's container path offers generated Dockerfiles as an alternative. -- **Permissions:** `--allow-run=aspire,docker`, `--allow-read/write` on output dirs. +- **Permissions / doctor matrix (r5 quick win):** `--allow-run=aspire,docker,kubectl` (applier + binaries per declared row), `--allow-read/write` on output dirs **and** the Aspire state path + (`~/.aspire/deployments/`) for the no-save/clear-cache policy; doctor checks `--environment` + normalization, `--clear-cache` availability, applier binaries for declared `--prebuilt` rows, + and the secret-state cache scan (SG-2). ## 2. `deploy-baremetal` (W2 extraction) — T1 diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md index 55c20faf0..2b62d4eb2 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-4-plugin-and-host.md @@ -143,11 +143,16 @@ dependencies. - **Scaffolder**: emits the userland `deploy/` leaf — `deploy/targets.ts` (typed target + cell definitions the user owns and edits — the named inversion of auth's never-rewritten barrel) plus per-target assets on `target add` (wrangler.jsonc, fly.toml, workflows…; - scaffold-stories doc). Golden tests per emitter (R-PLUGIN-PARITY). (r4, DP-9 §2) On + scaffold-stories doc). Golden tests per emitter (R-PLUGIN-PARITY). (r4/r5, DP-9 §2/§2a) On Aspire-managed projects the generated AppHost helpers register a **`netscript-capability-check` pipeline step** (`builder.pipeline.addStep(..., { requiredBy: ['deploy'] })` — TS-available) so the capability compiler also gates a raw - `aspire deploy` invoked without the NetScript CLI. + `aspire deploy` invoked without the NetScript CLI. Registration is **conditional** (SG-3): + only when the deploy plugin is present AND ≥1 Aspire target descriptor resolves — otherwise + no import, no step. The step calls the single compiler entrypoint over the + `CapabilityCheckInput` snapshot (SG-4) and reports failures with the recovery command + + diagnostics path. Delivery: DPB-17 (deps DPB-5/DPB-8/DPB-15) — DPB-8 exposes only the + adapter-neutral `runCapabilityCheck`. - **Doctor** (`deploy-target` check, contributed as data — §5): validates each configured target: adapter peer installed, descriptor loader resolves, required tool on PATH (`wrangler`, `aspire`, `docker`…), credentials env present (names only, never values), config diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md index e8e529fdc..f6557cfbe 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-6-migration-map.md @@ -59,8 +59,14 @@ New scaffolds from W3 get the plugin preinstalled with **no target preinstalled**; every target is added explicitly (`deploy target add`), with `deno-deploy` documented as the recommended first target (DP-3 §3, DP-8 Story 0). -4. **Aspire keeps its place**: local dev loop (`aspire start`) and the compose/k8s/azure publish - lanes are unchanged in behavior — they change owner (adapter package), not shape. +4. **Aspire: legacy paths unchanged, canonical paths deliberately changed** (r5, SG-7 — the + earlier "unchanged in behavior" claim was too strong): the local dev loop (`aspire start`) + is untouched, and **legacy invocations keep today's exact semantics through the compat + shim** (publish-on-plan for compose, `docker compose down` teardown). The plugin-era + **canonical** verbs change deliberately: `plan` is pure (artifacts move to `emit`), `down` + uses the variant's declared teardown executor (`aspire destroy`), omitted `--env` normalizes + to `production`, and the SG-2 no-save secret/state policy applies. Golden argv, + artifact-side-effect, and exit-code tests cover BOTH paths through the compatibility window. 5. **CI workflows**: previously scaffolded workflow files keep functioning (they call the same verbs); new scaffolds get per-target workflows from the adapters (OIDC-first where the platform supports it — AWS role-assume, Deno Deploy org tokens; token-based where not — diff --git a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md index 31ce8568a..100ce94b0 100644 --- a/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md +++ b/.llm/runs/plan-deploy-plugin--seed/design/canonical/DP-9-aspire-composition.md @@ -72,6 +72,56 @@ and one plugin note (AppHost pipeline-step registration) are added; the rest of (deno/container/cloudflare/vercel/aws lanes) is untouched — those platforms are precisely where Aspire's pipeline does not reach. +## 2a. r5 hardening of the composition boundary (Sol round-2, SG-1…SG-9 — all accepted) + +1. **Applier matrix — `up --prebuilt` is defined per variant or not at all (SG-1).** "The + platform applier" is no longer a placeholder. Each Aspire variant that declares + `up --prebuilt` carries a row: applier executable, accepted manifest entries, `ApplierPort` + implementation owner, required tools/permissions, idempotency key, and its status/log/ + rollback pairing — with a digest-before-first-mutation conformance test. + - `compose` → `docker compose -f up -d` (pre-pushed immutable image tags) + - `kubernetes` → `kubectl apply -f ` (one explicitly selected path; + Helm alternative is a separate declared row if ever added) + - `azure-*` → a named Azure-native command/API consuming the emitted Bicep + pre-pushed tags + (row authored with the adapter; absent until proven) + **A variant without a row omits `up --prebuilt` from `operations` and must not advertise the + CI build/deploy split.** +2. **Secrets never persist — by construction, not by warning (SG-2).** Secret parameters are + supplied only through process environment (`Parameters__`) or an interactive prompt + whose value NetScript never persists. **Every NetScript-driven Aspire operation that can + resolve secret parameters runs in no-save/clear-cache mode**; Aspire state persistence is + allowed only for a filtered allow-list of non-secret provisioning answers. If the pinned CLI + cannot prove that separation, the adapter disables state persistence entirely — and `doctor` + **fails closed** when an existing cache contains keys mapped to secret parameters. Sentinel + tests prove a secret value is absent from the deployment cache, artifact tree, manifest, + argv, logs, telemetry, and errors. +3. **One compiler, one snapshot (SG-4).** The capability compiler has a single pure entrypoint + over a versioned **`CapabilityCheckInput`** snapshot (graph digest, effective environment, + generated-registry digest, manifest ids/versions/probe dates). `plan` embeds the snapshot + + digest in its output; the `netscript-capability-check` pipeline step invokes the same + entrypoint and either **verifies** the supplied snapshot or **recompiles and reports that it + superseded it** — never a silently different verdict. Failure inside `aspire deploy` is one + concise summary + the exact recovery command (`netscript deploy plan --env `) + + a diagnostics path + a stable nonzero exit code. Parity tests assert identical verdict + JSON through the NetScript CLI and the raw Aspire entrypoint over the same snapshot. +4. **One environment identity (SG-5).** `resolveDeploymentEnvironment(target, requestedEnv)` + normalizes BEFORE registry/config/artifact resolution: for Aspire targets an omitted `--env` + normalizes to `production`, and that one value keys everything — registry lookup, overlay + merge (base target settings → `environments.` → CLI flags), artifact path, manifest + field, state-cache key, stream events, and the `--environment` pass-through. Registering + both bare `` and `@production` is rejected as aliases of one identity. + Non-Aspire adapters keep their own explicitly declared defaults. +5. **Plan purity, precisely (SG-9).** Purity means **no workspace-owned deployment artifacts + and no provider mutation**. Documented toolchain cache/restore effects of inspecting the + AppHost (`--list-steps`) are permitted inside an isolated, non-interactive subprocess whose + stdout/stderr/exit status are captured to diagnostics; an inspection failure raises + `AspirePipelineInspectionError` and is never converted into a capability rejection. +6. **Step delivery re-homed (SG-3).** The pipeline step ships with the **scaffolder card** + (DPB-17; deps DPB-5 + DPB-8 + DPB-15) — DPB-8 exposes only an adapter-neutral + `runCapabilityCheck` callable. The generated helper registers the step **conditionally**: + deploy plugin present AND ≥1 Aspire target descriptor resolved; otherwise it emits no import + and no step, so raw `aspire start/deploy` remains valid on any project. + ## 3. Radius — the tracked convergence (enterprise CI/CD) - **microsoft/aspire#18696 (merged 2026-07-10):** `Aspire.Hosting.Radius` makes Radius a @@ -87,13 +137,23 @@ Aspire's pipeline does not reach. executes recipes via the deployment engine; **credentials live in the Radius control plane** (`rad credential register azure|aws`), not the developer's shell. -**Position:** Radius enters the family as a **`deploy-aspire` target key (`radius`)** — not a -new adapter package — because upstream already routes it through the exact verbs the adapter -wraps. Gate: #18759 merged + released in the pinned Aspire CLI + the TS surface stable. Its -capability manifest rows come from what the bound Recipes provide (binding-scope, per L-2). The -control-plane credential model is a **strength** for the enterprise story (no cloud secrets in -CI at all) and slots into the `secrets`-op reference model unchanged. Tracking home: the -DPB-29 deferred-RFC card (scope widened) + a watch item on the aspire-adapter card. +**Position (r5, SG-8):** Radius enters the family **inside `deploy-aspire`** as a +**descriptor-selected Aspire compute-environment variant** — not a new adapter package and not +an unconditional static capability profile. The graduation gate is **machine-verifiable +predicates**, not issue-state prose: + +1. the pinned Aspire CLI version is ≥ the first recorded release containing the Radius TS API; +2. `deno check` of a minimal `addRadiusEnvironment` AppHost fixture passes; +3. `aspire publish --environment ` emits `app.bicep`; +4. `aspire deploy --list-steps` exposes the expected Radius deployment step; +5. a fixture demonstrates recipe-derived binding verdicts. + +Only runtime/process traits are published statically; **Recipe/resource-group capabilities are +discovered per environment and reported `unverified` until probed** (the capability law, DP-2 +§4, applied — a static provider-wide claim would lie). The control-plane credential model is a +**strength** for the enterprise story (no cloud secrets in CI at all) and slots into the +`secrets`-op reference model unchanged. Tracking home: the DPB-29 deferred-RFC card (scope +widened) + a watch item on the aspire-adapter card. ## 4. Honest constraints (carried into docs and cards) diff --git a/.llm/runs/plan-deploy-plugin--seed/drift.md b/.llm/runs/plan-deploy-plugin--seed/drift.md index f9badcc8c..143e78bba 100644 --- a/.llm/runs/plan-deploy-plugin--seed/drift.md +++ b/.llm/runs/plan-deploy-plugin--seed/drift.md @@ -11,3 +11,4 @@ Append-only. Severity: `note` | `significant` | `architectural`. | D-5 | 2026-07-19 | note | Kimi doc-story caught two genuine r2 corpus defects the adversarial pass missed: the DP-6 preinstalled-default-target contradiction (KF-1) and `emit` being uncorroborated on the flagship `deno-deploy` target (KF-9 — resolved by NOT declaring `emit` there; getting-started flow is `plan → up`). Forecast Page A's Step 3 documents the pre-correction flow and is kept verbatim as the stage snapshot; the corpus (r3) is authority. | Integrated (`doc-story-kimi-triage.md`); future public docs follow the corpus. | | D-6 | 2026-07-19 | note | Kickoff stop-line "no PRs" partially lifted by owner directive (in-turn via /remote-control, 2026-07-19): open a DRAFT RFC PR (`rfc.md`, #822 format, Refs-only, colon labels, Backlog/Triage milestone) as the lane deliverable. Board filing (epic/sub-issues) and any merge remain forbidden pending owner ratification. | Executed: `rfc.md` committed; draft PR opened per the netscript-pr skill. | | D-7 | 2026-07-19 | note | Owner pre-filing review pass (PR #891 comment): Aspire-composition analysis required before epic dispatch. Result (r4, DP-9): Aspire-lane seams delegate to the language-agnostic CLI (`publish/deploy/destroy/do`, `--list-steps`, `--environment`, state cache); NetScript keeps the manifest/--prebuilt contract, status/logs/rollback, the TS env-overlay file, workflow generation, and the Deno OCI path; new `builder.pipeline.addStep` capability-check integration; Radius tracked as a future `deploy-aspire` target key (aspire#18696/#18759). No port/package/board-shape change. | Integrated as r4; Addendum A on rfc.md + PR body. | +| D-8 | 2026-07-19 | note | Sol round-2 (SG-1…SG-9, all accepted): the r4 Aspire composition boundary needed hardening — prebuilt applier matrix, no-save secret/state policy, single-compiler snapshot parity, environment-identity normalization, conditional step delivery re-homed to the scaffolder card, predicate-gated Radius, scoped plan purity, honest Aspire compatibility wording. No topology/port/board-count change. | Integrated as r5; PR #891 body synced. | diff --git a/.llm/runs/plan-deploy-plugin--seed/plan.md b/.llm/runs/plan-deploy-plugin--seed/plan.md index 2a0934f39..e7a729935 100644 --- a/.llm/runs/plan-deploy-plugin--seed/plan.md +++ b/.llm/runs/plan-deploy-plugin--seed/plan.md @@ -87,14 +87,14 @@ semantics land inside DPB-16/DPB-17's bodies. | ID | Title (draft) | Wave | Pri | Delivery shape | Depends on | | --- | --- | --- | --- | --- | --- | | EPIC | Epic: Deploy plugin family (`type:umbrella`, `epic:deploy-plugin`, `area:cli` `area:plugin`) | — | p1 | umbrella; epic-acceptance gates from DP-8 + conformance matrix; each wave exit includes its docs refresh | — | -| DPB-1 | Move deploy port/result/error contracts to core behind compatibility re-exports | W1 | **p0** | contracts move, no behavior change | — | +| DPB-1 | Move deploy port/result/error contracts to core behind compatibility re-exports (r5 quick win: fix the shipped "uniform 7-op" port comment → eight-op/declared-subset, incl. matching doctrine/gate wording, so the stale count is not copied forward) | W1 | **p0** | contracts move, no behavior change | — | | DPB-2 | Move pure conventions (activation/secrets/rollback/otel/health-gate) with their constants | W1 | p1 | conventions move; `runtime-overrides.ts` stays leaf-side | DPB-1 | | DPB-3 | Empty duplicate-rejecting core registry + CLI compatibility composition root | W1 | p0 | NEW rejection behavior + externalized defaults | DPB-1 | | DPB-4 | Host-owned `deploy` shell split (desktop preserved) + router rewired over contracts | W1 | p1 | thin-router refactor, no verb changes | DPB-1, DPB-3 | | DPB-5 | Capability + topology contracts (`CapabilityRef`/verdicts/variants; `DeploymentCell`/`suggestedCells`) + rejection compiler + conformance harness | W1 | **p0** | core contract, backend-truthful | DPB-1 | | DPB-6 | Two-phase config loader + base schema re-home + frozen legacy union | W1 | **p0** | config bootstrap contract (unknown target ⇒ error) | DPB-1 | | DPB-7 | Extract `deploy-baremetal` (build behavior, Servy/systemd, `BaremetalCompatibilityCommands`, systemd live probe) | W2 | p1 | adapter over core port + compat handlers | DPB-2, DPB-3 | -| DPB-8 | Extract `deploy-aspire` (r4: + `aspire destroy` for down, `--list-steps` in plan, state-cache delegation + `--clear-cache`, `Parameters__*` secrets convention, `netscript-capability-check` pipeline step; Radius watch per DP-9) | W2 | p1 | adapter delegating to the aspire CLI + TS pipeline steps | DPB-2, DPB-3 | +| DPB-8 | Extract `deploy-aspire` (r4/r5: `aspire destroy` for down, `--list-steps` in plan, state-cache delegation under the SG-2 no-save secret policy, `Parameters__*` convention, the target×op table as the generated `operations` source, the **applier matrix** for declared `--prebuilt` rows, adapter-neutral `runCapabilityCheck`; Radius watch per DP-9 §3) — the pipeline STEP itself ships with DPB-17 (SG-3) | W2 | p1 | adapter delegating to the aspire CLI | DPB-2, DPB-3 | | DPB-9 | Extract `deploy-deno` (+ honest manifest rows; transitive-scan follow-up) | W2 | p1 | adapter wrapping `deno deploy` | DPB-3, DPB-5 | | DPB-10 | Adapter-side config member schemas over the schema registry (config debt retirement) | W2 | p1 | config re-home | DPB-6, DPB-7–DPB-9 | | DPB-11 | Legacy/config compatibility gate: state-transition + help goldens + unknown-target error paths | W2 | p1 | first-class compat verification (SF-9/SF-10) | DPB-7, DPB-6 | @@ -103,7 +103,7 @@ semantics land inside DPB-16/DPB-17's bodies. | DPB-14 | Host: doctor-check contributions as data + installer-protocol `tooling` variant + `contributionAxes` | pre-W3 | p1 | protocol generalization (auth migrates compatibly) | — | | DPB-15 | `plugins/deploy`: manifest triad, descriptor composition root, verify-plugin expectations | W3 | p1 | thin A5 plugin (depends on extracted adapters + schema loader) | DPB-5, DPB-9, DPB-10, DPB-12, DPB-14 | | DPB-16 | Plugin CLI children under the `deploy` mount: `target add/remove`, `capabilities --json`, eight-op router | W3 | p1 | plugin CLI contribution | DPB-13, DPB-15 | -| DPB-17 | Scaffolder: `deploy/targets.ts` leaf + Story-0 assets + golden tests | W3 | p1 | plugin scaffold contribution | DPB-15 | +| DPB-17 | Scaffolder: `deploy/targets.ts` leaf + Story-0 assets + golden tests + (r5, SG-3) the **conditional `netscript-capability-check` pipeline-step registration** in the generated AppHost helpers (snapshot-verified via the single compiler entrypoint, SG-4) | W3 | p1 | plugin scaffold contribution | DPB-5, DPB-8, DPB-15 | | DPB-18 | Story-0 `scaffold.runtime` E2E case (`install deploy` → `target add deno-deploy` → `plan`) | W3 | p1 | e2e registration | DPB-16, DPB-17 | | DPB-19 | `deploy-events` stream + telemetry + runtime-config topic | W3 | p2 | plugin contributions | DPB-15 | | DPB-20 | `deploy-container`: OCI build/push, `ContainerBuildPort` impl, Dockerfile emission | W4 | p1 | new adapter (injectable shared path) | DPB-5 | @@ -115,7 +115,7 @@ semantics land inside DPB-16/DPB-17's bodies. | DPB-26 | AWS-PROBE-HTTP (live LWA conformance) | W5 | p1 | probe card, findings-only | DPB-20 | | DPB-27 | `deploy-aws` (lambda variant, HTTP scope) + Story 2 | W5 | p1 | probe-gated adapter, HTTP-only | DPB-26 | | DPB-28 | Docs: target-matrix reference + per-target how-tos replace the alpha-minimal page | W3–W5 | p1 | docs consolidation (wave exits carry interim refreshes) | rolling | -| DPB-29 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation + **Radius target graduation** (`radius` key on `deploy-aspire`, gated on microsoft/aspire#18759 shipping in the pinned CLI — DP-9 §3) | backlog | p2 | RFC-first, leaf/upstream co-owned | DPB-27 | +| DPB-29 | Deferred RFC: AWS-PROBE-EVENTS + leaf backing catalog graduation + **Radius target graduation** (r5, SG-8: descriptor-selected Aspire compute-environment variant; gate = machine-verifiable predicates — pinned CLI ≥ first TS-API release, `deno check` fixture, `app.bicep` emission, Radius step in `--list-steps`, recipe-derived binding-verdict fixture; per-environment Recipe capabilities `unverified` until probed — DP-9 §3) | backlog | p2 | RFC-first, leaf/upstream co-owned | DPB-27 | (Each future body: `Part of #EPIC` → scoping paragraph with anti-scope boundary → `- [ ] gate:` acceptance → `Dependencies:` + `Delivery shape:` — per board-parity conventions.) diff --git a/.llm/runs/plan-deploy-plugin--seed/rfc.md b/.llm/runs/plan-deploy-plugin--seed/rfc.md index 056f81d12..ef6c74f63 100644 --- a/.llm/runs/plan-deploy-plugin--seed/rfc.md +++ b/.llm/runs/plan-deploy-plugin--seed/rfc.md @@ -2,7 +2,7 @@ | | | | --- | --- | -| **Status** | **Adversarially hardened draft (r3)** — direction ratified by owner 2026-07-18; package/API design pending owner arbitration of forks OF-1…OF-8 (§8); board **not filed** (numbers are placeholders) | +| **Status** | **Adversarially hardened draft (r5)** — direction ratified by owner 2026-07-18; Aspire-composition pass (Addendum A) + Sol round-2 verdicts (SG-1…SG-9, all accepted) integrated; package/API design pending owner arbitration of forks OF-1…OF-8 (§8); board **not filed** (numbers are placeholders) | | **Tracking** | Refs #820 (single-deployment charter) · #327 (Deployment epic) · #823 (Unified epic — re-scope proposal §7) · #824 (unified-runtime seed — **superseded by this RFC**, §7) · #825 (Aspire packaging NuGet — unaffected) · #871 (enterprise-auth board — the composition + board pattern this family mirrors) | | **Pipeline** | Fable 5 · xhigh generator → GPT-5.6 Sol · xhigh constructive adversarial (**16/16 findings accepted**, r2) → Kimi K3 doc-driven story (**13/13 DX findings accepted**, r3). Triage records in the run dir | | **Evidence base** | Prior seed run `plan-unified-runtime--seed` (nitro-vs-own analysis, both adversarial rounds); live provider surfaces 2026-07-18 (Cloudflare/Vercel/AWS/Fly/Deno Deploy/thin PaaS/Nitro v3, primary sources); shipped deploy layer inventory; auth-plugin composition anatomy; doctrine A7 + `DEPLOY-ARCHETYPE-7-CORE-SEED` debt; full corpus in `.llm/runs/plan-deploy-plugin--seed/` (this PR) | @@ -25,7 +25,8 @@ packages/deploy-cloudflare | deploy-vercel | deploy-aws (A2 adapters ``` netscript plugin install deploy netscript deploy target add deno-deploy # targets are explicit; several compose side by side -netscript deploy deno-deploy plan # pure: capability verdict, nothing mutated +netscript deploy deno-deploy plan # pure: capability verdict — no workspace deploy + # artifacts written, no provider mutation netscript deploy deno-deploy up # ship ``` @@ -354,8 +355,7 @@ full TypeScript AppHost projection) are tracked as a future **`radius` target ke **Provenance.** This PR lands the full planning record under `.llm/runs/plan-deploy-plugin--seed/`: six-surface research corpus (`research/`), canonical design docs `DP-0…DP-8` (r3), `plan.md` (locked decisions LD-1…12, the 29-child board table, -risk register, gates), the Sol adversarial trail (`adversarial-brief.md`, `adversarial-sol.md`, -`adversarial-sol-triage.md` — 16/16 accepted), and the Kimi doc-story trail +risk register, gates), the Sol adversarial trail (rounds 1+2: `adversarial-brief.md`, `adversarial-sol.md`, `adversarial-sol-triage.md` — 16/16; `adversarial-brief-r4.md`, `adversarial-sol-r4.md`, `adversarial-sol-r4-triage.md` — 9/9), and the Kimi doc-story trail (`kimi-doc-story-brief.md`, `doc-story-kimi.md` — docs IA + four forecast pages, `doc-story-kimi-triage.md` — 13/13 accepted; where a forecast page predates a correction, the corpus is authority). Board numbers here are placeholders — filing is a later diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 927cdeb1b..704bdffa2 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -207,3 +207,25 @@ STAGE-COMPLETE: doc-story-integration (capability-check pipeline step in scaffolded AppHost); DP-5 (aspire row extended); plan.md (DPB-8/DPB-29 scope, new pipeline-churn risk row); rfc.md Addendum A. Family architecture, ports, and board shape unchanged. + +## 2026-07-19 — Round 2 adversarial (Sol xhigh, same thread) + integration (r5) + +- Owner directive: adversarial/collaborative pass against r4 to confirm whole-architecture + soundness. Steered the existing Codex thread (one-sender rule; first steer killed by the + 2-min shell cap — `codex exec resume` streams the turn in-process; re-issued detached + nohup+monitor, same fix as the Kimi run). +- Result: `adversarial-sol-r4.md` (commit `38ccc2c5`) — verdict "strong architecture, not + fileable unchanged; defects cluster at the new composition boundary"; SG-1…SG-9 + (3 BLOCKER / 5 MAJOR / 1 MINOR) + quick wins. Triage: `adversarial-sol-r4-triage.md` — + **all 9 ACCEPTED** (one quick win not reproducible at HEAD: the plan.md duplicate risk + header). +- Integration (r5): DP-9 §2a hardening (per-variant `--prebuilt` applier matrix — no row, no + op; SG-2 no-save secret/state policy with fail-closed doctor; single compiler entrypoint + + `CapabilityCheckInput` snapshot with CLI/step parity; `resolveDeploymentEnvironment` + normalization; scoped plan purity + `AspirePipelineInspectionError`; step delivery re-homed) + + DP-9 §3 Radius predicates; DP-3 §1 target×op table (generated `operations` source) + + extraction wording + permission/doctor matrix; DP-2 §2/§4 purity + snapshot + normalization; + DP-4 §4 conditional step registration; DP-6 §3 honest compat enumeration (legacy via shim, + canonical changes deliberate); plan.md DPB-1/8/17/29 recut (step → DPB-17 with deps + 5/8/15); rfc.md r5 (status, abstract purity wording, §5/§6, Addendum A round-2 paragraph, + provenance). Family topology, ports, and 29-child board count unchanged. From c7c68270ff75e330ed377221ee7a3889c39f1ff1 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 11:17:32 +0200 Subject: [PATCH 18/20] plan(deploy-plugin): one-shot filing manifest (executes on PLAN-EVAL PASS) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../filing-manifest.md | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/filing-manifest.md diff --git a/.llm/runs/plan-deploy-plugin--seed/filing-manifest.md b/.llm/runs/plan-deploy-plugin--seed/filing-manifest.md new file mode 100644 index 000000000..791013158 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/filing-manifest.md @@ -0,0 +1,197 @@ +# Filing manifest — deploy-plugin board (one-shot; executes only on PLAN-EVAL PASS) + +> Authority: owner directive 2026-07-19 ("dispatch & on pass create the epic, sub issues and +> prioritize them on milestones; allowed to create further betas"). Corpus r5 is the source; +> `plan.md` §5 is the DAG. After filing, **GitHub is the single source of truth**; run docs carry +> tags under that authority rule. Dependencies are expressed as `DPB-n` ids (titles carry the +> same ids — single-scheme rule). + +## Pre-steps + +- **Labels:** ensure `epic:deploy-plugin` exists (color `5319e7`, description "Deploy plugin + family epic (#891 RFC)"). Parity follow-up: add to `.github/labels.yml` in a framework slice + (recorded in FILING-LOG). +- **Milestones:** ensure `0.0.1-beta.15` ("Deploy plugin — container path (W4)") and + `0.0.1-beta.16` ("Deploy plugin — probe-gated clouds (W5)") exist. Existing: `0.0.1-beta.13` + (#15), `Backlog / Triage` (#3). +- **Assignment:** W1+W2+Host+W3 (DPB-1…19) + DPB-28 → `0.0.1-beta.13`; W4 (DPB-20…22) → + `0.0.1-beta.15`; W5 (DPB-23…27) → `0.0.1-beta.16`; DPB-29 → `Backlog / Triage`. Epic → + `0.0.1-beta.13`. +- **Common labels:** every child carries `epic:deploy-plugin`, `area:deploy`, `status:plan`, one + `type:`, one `priority:`; secondary `area:` where noted. No `wave:` labels (milestones carry + scheduling — consistent-or-not-at-all rule). + +## EPIC — `Epic: Deploy plugin family` + +labels: `type:umbrella epic:deploy-plugin area:deploy area:plugins priority:p1 status:plan` · milestone: beta.13 + +Body: RFC pointer (#891, corpus r5 in `.llm/runs/plan-deploy-plugin--seed/`), the family diagram, +`## Epic acceptance` gates: + +- [ ] gate: every child's acceptance gates are checked with linked evidence before it closes; no closing keyword ever targets this epic. +- [ ] gate: F-DEPLOY-1 and F-DEPLOY-2 are `gated` (not `reviewed`) and green from W1 on. +- [ ] gate: `deno task e2e:cli` (scaffold.runtime) is green at every wave exit; W1 sub-slices each keep it green. +- [ ] gate: the conformance matrix (target × variant × capability × verdict) runs in CI from W2 on; every `lossless` verdict carries live-platform evidence. +- [ ] gate: no `deploy-*` package imports another `deploy-*` package; core imports no leaf package (import-graph gates). +- [ ] gate: sentinel secret values never appear in plans, artifact manifests, Aspire state cache, argv, logs, telemetry, or errors (negative tests). +- [ ] gate: every documented legacy `netscript deploy …` invocation keeps its exact semantics through the compatibility window (golden tests). +- [ ] gate: all implementation PRs satisfy CI and the required opposite-family review before merge. + +`## Children` = checklist of the 29 live issue numbers (filled at filing). + +## Children (title · type · priority · milestone · secondary areas · body core) + +Each body = `Part of #` + scoping paragraph (with anti-scope) + `## Acceptance` gates + +`## Metadata` (`Dependencies:` by DPB-id · `Delivery shape:`). Corpus refs cite DP docs. + +### DPB-1 `[deploy-plugin DPB-1] Move deploy contracts to plugin-deploy-core behind compatibility re-exports` · type:refactor · p0 · beta.13 · +area:cli +Scope: move `DeployTargetPort`/`DeployOperation`/result+error types from the CLI kernel to `plugin-deploy-core/ports|domain` with compatibility re-exports; fix the stale "uniform 7-op" comment (eight-op/declared-subset) incl. matching doctrine/gate wording. Anti-scope: no behavior change, no registry/conventions move, no build-engine move (DP-2 intro, SF-2). +Gates: `- [ ] gate: e2e:cli green with zero verb changes` · `- [ ] gate: consumer imports compile via re-exports` · `- [ ] gate: no "7-op" wording remains in moved code or doctrine touched by this slice`. +Deps: none. Shape: contracts extraction, no vendor work. + +### DPB-2 `[deploy-plugin DPB-2] Move pure deploy conventions with their constants to core` · type:refactor · p1 · beta.13 · +area:cli +Scope: activation/secrets/rollback/observability/health-gate conventions move with constants; `runtime-overrides.ts` and `servy-config.ts` stay with their bare-metal/leaf owners. Anti-scope: no convention behavior change (DP-2 §6, SF-2). +Gates: `- [ ] gate: conventions' unit tests pass unchanged from the new home` · `- [ ] gate: adapters delegate (R-DEPLOY-3) — no forked convention copies remain`. +Deps: DPB-1. Shape: pure-module move. + +### DPB-3 `[deploy-plugin DPB-3] Empty duplicate-rejecting core registry + CLI compatibility composition root` · type:feat · p0 · beta.13 · +area:cli +Scope: `createDeployTargetRegistry(entries=[])` in core (port, key/error types, `DeployTargetCollisionError` — duplicate rejection is NEW; `replaceForCompatibility` composition-root-only for the shim); `DEFAULT_DEPLOY_TARGETS` deleted as a core concept — defaults stay in the CLI composition root. Anti-scope: core never imports an adapter (SF-1/SF-13). +Gates: `- [ ] gate: constructor + generated-registry duplicate tests reject with both owners named` · `- [ ] gate: environment-qualified keys and deterministic ordering tested` · `- [ ] gate: e2e:cli green via the compat composition root`. +Deps: DPB-1. Shape: registry extraction + NEW rejection behavior. + +### DPB-4 `[deploy-plugin DPB-4] Host-owned deploy shell split + router rewired over core contracts` · type:refactor · p1 · beta.13 · +area:cli +Scope: split the `deploy` group into a host-owned shell (owns `desktop`, shared help, future install hint) with the router rewired over core contracts; fix the stale "Windows Service" description. Anti-scope: zero verb changes; desktop subgroup not moved (DP-6 M-16/R-M4). +Gates: `- [ ] gate: help-output goldens unchanged except the description fix` · `- [ ] gate: desktop verbs untouched by diff` · `- [ ] gate: e2e:cli green`. +Deps: DPB-1, DPB-3. Shape: thin-router refactor. + +### DPB-5 `[deploy-plugin DPB-5] Capability + topology contracts, rejection compiler, conformance harness` · type:feat · p0 · beta.13 +Scope: structural `CapabilityRef`/`BindingRequirement`/`WorkloadConstraint`/`CapabilityVerdict` (levels incl. `unverified`; scopes runtime/adapter/binding) + per-variant `DeployCapabilityManifest` (schemaVersion, toolVersions, probedAt); `DeploymentCell`/`DeploymentTopologyPlan` + `suggested-cells.json`; ONE pure compiler entrypoint over a versioned `CapabilityCheckInput` snapshot; the conformance-suite harness + in-memory fakes in `./testing`. Anti-scope: core imports no leaf package; no live-provider claims (DP-2 §4/§5, SF-6/7/8, SG-4). +Gates: `- [ ] gate: compiler rejects a required unsupported capability at build time with the manifest note` · `- [ ] gate: compiler emits suggestedCells and never partitions silently` · `- [ ] gate: parity test — identical verdict JSON for two consumers over one snapshot` · `- [ ] gate: lossless requires an evidence id (schema-enforced)`. +Deps: DPB-1. Shape: core contracts, backend-truthful. + +### DPB-6 `[deploy-plugin DPB-6] Two-phase config loader + deploy schema re-home + frozen legacy union` · type:feat · p0 · beta.13 · +area:config +Scope: bootstrap-parse (identity+plugins+`deploy.targets` unstripped) → resolve schema loaders → compose target schema registry → full parse; unknown target ⇒ `DeployTargetAdapterMissingError` (the ONE deliberate behavior change); `DeployTargetBaseSchema` → core `./config`; `@netscript/config` keeps the loader seam + frozen delegating legacy union; `resolveDeploymentEnvironment` normalization (omitted Aspire env ⇒ production; alias registrations rejected). Anti-scope: no per-target member moves yet (DP-2 §6, SF-10, SG-5). +Gates: `- [ ] gate: installed-custom-target, missing-adapter, malformed-config, loader-failure and all existing-key tests pass` · `- [ ] gate: unknown key errors with the target-add hint — never silently stripped` · `- [ ] gate: bare + @production alias registration rejected`. +Deps: DPB-1. Shape: config bootstrap contract. + +### DPB-7 `[deploy-plugin DPB-7] Extract deploy-baremetal (build pipeline, Servy/systemd, compat handlers)` · type:refactor · p1 · beta.13 · +area:cli +Scope: one target `baremetal`, variants `windows|linux`; receives the `deno compile` build pipeline and `BaremetalCompatibilityCommands` (legacy verbs keep exact shipped semantics; only build/status/logs alias, pinned to this target); public composition of the convention ports (closes `DEPLOY-BAREMETAL-PUBLIC-WIRING`); systemd live-probe suite cell. Anti-scope: no desktop packaging; no core compile-emitter graduation yet (DP-3 §2, SF-2/SF-9, KF-3/4). +Gates: `- [ ] gate: state-transition tests — stop never uninstalls, start never registers` · `- [ ] gate: full declared op set advertised at runtime (composition root wires ports)` · `- [ ] gate: legacy help/exit-code goldens unchanged` · `- [ ] gate: systemd live probe green on the probe lane`. +Deps: DPB-2, DPB-3. Shape: adapter extraction + compat handlers. + +### DPB-8 `[deploy-plugin DPB-8] Extract deploy-aspire (target×op table, applier matrix, secret-safe state policy)` · type:refactor · p1 · beta.13 · +area:aspire +Scope: the target×op table (DP-3 §1) as the generated source of `operations`; `--list-steps` in pure plan; `aspire destroy` for canonical down; state-cache delegation under the SG-2 no-save secret policy (doctor fails closed on secret-mapped cache keys); `Parameters__*` secrets convention; per-variant `--prebuilt` applier rows (compose/kubernetes; azure absent until proven); adapter-neutral `runCapabilityCheck`; Radius watch (predicate-gated, DP-9 §3). Anti-scope: the pipeline STEP ships with DPB-17; legacy semantics preserved via the shim (DP-9 §2/§2a, SG-1/2/6/7). +Gates: `- [ ] gate: operations generated from the table — AST scan matches` · `- [ ] gate: sentinel secret absent from state cache/artifacts/argv/logs after a secret-resolving op` · `- [ ] gate: prebuilt applier digest-verifies before first mutation (per declared row)` · `- [ ] gate: legacy compose plan still publishes (shim goldens); canonical plan writes nothing`. +Deps: DPB-2, DPB-3, DPB-5, DPB-6. Shape: adapter extraction delegating to the aspire CLI. + +### DPB-9 `[deploy-plugin DPB-9] Extract deploy-deno (honest manifest; no emit by design)` · type:refactor · p1 · beta.13 +Scope: wrap the built-in `deno deploy` CLI (target key `deno-deploy`); manifest honesty rows (kv atomic per leaf conformance; `kv:queues`/`queue:consume` unsupported; cron lossless; sagas externalized); **`emit` deliberately not declared** (platform builds from source — declared-subset showcase); transitive unstable-scan improvement as follow-up note. Anti-scope: no CI-split claims on this target (DP-3 §3, KF-9). +Gates: `- [ ] gate: declared subset excludes emit; calling it yields DeployOperationUnsupportedError` · `- [ ] gate: manifest rows carry evidence ids from the suite` · `- [ ] gate: plan→up flow green against a live app (probe lane)`. +Deps: DPB-3, DPB-5. Shape: adapter extraction wrapping `deno deploy`. + +### DPB-10 `[deploy-plugin DPB-10] Adapter-side config member schemas over the schema registry` · type:refactor · p1 · beta.13 · +area:config +Scope: per-target member schemas move to their adapters (spreading the base — R-DEPLOY-4); the deploy slice of `config-plugin-specific-schema-debt` retires. Anti-scope: legacy union keeps parsing all existing keys. +Gates: `- [ ] gate: all shipped target keys parse identically (golden configs)` · `- [ ] gate: config debt entry retired with evidence link`. +Deps: DPB-6, DPB-7, DPB-8, DPB-9. Shape: config re-home. + +### DPB-11 `[deploy-plugin DPB-11] Legacy/config compatibility gate` · type:test · p1 · beta.13 · +area:cli +Scope: the first-class compatibility verification: golden argv/help/exit-code tests for every legacy verb; unknown-target error paths; artifact-side-effect goldens for legacy vs canonical Aspire paths. Anti-scope: no behavior fixes here — findings route to the owning card (SF-9/SF-10, SG-7). +Gates: `- [ ] gate: every documented deploy invocation covered by a golden` · `- [ ] gate: canonical-vs-legacy divergence is exactly the enumerated set (DP-6 §3)`. +Deps: DPB-6, DPB-7, DPB-8. Shape: compat test suite. + +### DPB-12 `[deploy-plugin DPB-12] Host: CLI mount-children contribution contract` · type:feat · p1 · beta.13 · +area:plugins +area:cli +Scope: `CliCommandContribution {mount,id,loader,export}` + `withCliCommands` + abstract + merger/verifier; host-owned reserved mounts; duplicate `(mount,id)` fails before parsing naming both owners. Anti-scope: no shadowing of top-level commands, no plugin-specific exceptions (DP-4 §5, SF-4). +Gates: `- [ ] gate: duplicate and reserved-name collisions fail pre-parse with both owners` · `- [ ] gate: verify-plugin gains cliCommands expectations`. +Deps: none. Shape: `@netscript/plugin` contribution contract. + +### DPB-13 `[deploy-plugin DPB-13] Host: async CLI bootstrap, loader isolation, plugin-absent UX` · type:feat · p1 · beta.13 · +area:cli +Scope: manifest-loader-fed async bootstrap (resolve manifests → validate safe loader subpaths → built-ins → contributed children → `program()`); loader failure isolation; install-hint when a mount's plugin is absent. Anti-scope: AST source walker does not feed this registry (SF-4). +Gates: `- [ ] gate: one broken plugin cannot take down the CLI (isolation test)` · `- [ ] gate: absent-plugin mount prints the install hint` · `- [ ] gate: startup-time budget unchanged for plugin-less projects`. +Deps: DPB-12. Shape: CLI bootstrap change. + +### DPB-14 `[deploy-plugin DPB-14] Host: doctor-checks as data + installer tooling variant + contributionAxes` · type:feat · p1 · beta.13 · +area:plugins +Scope: `DoctorCheckContribution {id, loader}` duplicate-guarded registry (auth migrates compatibly); `officialSource` `sourceKind:'tooling'` variant; `capabilities.contributionAxes`; v1 service manifests keep parsing. Anti-scope: no closed-union widening; no deploy-specific protocol fields (SF-3/SF-14). +Gates: `- [ ] gate: auth doctor check migrates with zero behavior change` · `- [ ] gate: schema parse + official copy/install + backward-compat fixtures green`. +Deps: none. Shape: protocol generalization. + +### DPB-15 `[deploy-plugin DPB-15] plugins/deploy: manifest triad, descriptor composition root, verify-plugin` · type:feat · p1 · beta.13 · +area:plugins +Scope: A5 plugin — protocol-valid tooling manifest; `DeployTargetContribution` descriptors resolved from the generated registry module (plugin depends on core ONLY; per-target permission profiles); doctor `deploy-target` check (states incl. adapter-not-installed/credential-unavailable; orphaned declarations); verify-plugin green. Anti-scope: no static adapter imports; no HTTP service (DP-4 §1–§3, SF-12). +Gates: `- [ ] gate: missing-peer/invalid-export/duplicate-key/uninstall/stale-registry tests` · `- [ ] gate: verify-plugin passes with the declared axes` · `- [ ] gate: plugin imports core only (import-graph)`. +Deps: DPB-5, DPB-9, DPB-10, DPB-12, DPB-14. Shape: thin A5 plugin. + +### DPB-16 `[deploy-plugin DPB-16] Plugin CLI children: target add/remove, capabilities, cells apply, eight-op router` · type:feat · p1 · beta.13 · +area:cli +Scope: children under the `deploy` mount — `target add|remove` (descriptor + settings member + assets; remove never edits `deploy/targets.ts`, `--keep-config`/`--purge-assets`), `capabilities [--json|--preview]` (bundled published-manifest preview catalog, honestly labeled), `cells apply` (diff shown), the eight-op router with the locked grammar sketch (`--env`, `secrets set|list|unset`, `rollback [--to]`, `emit [--output]`, `down [--yes]`). Anti-scope: no top-level command additions (DP-4 §6, KF-5/10/11/12). +Gates: `- [ ] gate: capabilities --json validates against the published schema incl. schemaVersion` · `- [ ] gate: target remove leaves deploy/targets.ts untouched; doctor flags orphans` · `- [ ] gate: cells apply materializes suggested-cells.json with a shown diff`. +Deps: DPB-13, DPB-15. Shape: plugin CLI contribution. + +### DPB-17 `[deploy-plugin DPB-17] Scaffolder: deploy/ leaf, Story-0 assets, conditional capability-check pipeline step` · type:feat · p1 · beta.13 +Scope: userland `deploy/targets.ts` leaf + per-target assets on `target add` (golden test per emitter); the **conditional** `netscript-capability-check` Aspire pipeline step (plugin present + ≥1 Aspire descriptor; snapshot-verified via the single compiler entrypoint; standardized failure with recovery command). Anti-scope: no `init --deploy` sugar (deferred fork) (DP-4 §4, SG-3/SG-4, KF-2). +Gates: `- [ ] gate: no plugin ⇒ generated AppHost has no step import; aspire start/deploy valid` · `- [ ] gate: step verdict parity with CLI plan over one snapshot` · `- [ ] gate: goldens for every emitted artifact`. +Deps: DPB-5, DPB-8, DPB-15. Shape: plugin scaffold contribution. + +### DPB-18 `[deploy-plugin DPB-18] Story-0 scaffold.runtime E2E (install → target add → plan)` · type:test · p1 · beta.13 · +area:cli +Scope: registered `scaffold.runtime` case: `plugin install deploy` → `deploy target add deno-deploy` → `deploy deno-deploy plan` on a generated workspace. Anti-scope: live platform deploys stay on probe lanes. +Gates: `- [ ] gate: the case is registered in runtime-gates and green in the merge-readiness suite`. +Deps: DPB-16, DPB-17. Shape: e2e registration. + +### DPB-19 `[deploy-plugin DPB-19] deploy-events stream, telemetry, runtime-config topic` · type:feat · p2 · beta.13 · +area:telemetry +Scope: versioned `deploy-events` envelope (started/succeeded/failed/rolled-back; target+variant, env, artifact digest, actor, trace ctx); telemetry spans with secret redaction; tiny `deploy` runtime-config topic (checked per invocation). Anti-scope: core deploy success never depends on the stream sink. +Gates: `- [ ] gate: sink outage cannot fail a deploy (fault test)` · `- [ ] gate: sentinel secret absent from events/spans`. +Deps: DPB-15. Shape: plugin contributions. + +### DPB-20 `[deploy-plugin DPB-20] deploy-container: OCI build/push + ContainerBuildPort + Dockerfile emission` · type:feat · p1 · beta.15 +Scope: `ContainerBuildPort` implementation (Dockerfile generation from `EmittedArtifactManifest`, `denoland/deno:2` base; build/push via docker/podman; registry auth); the generic container-platform port. Anti-scope: no platform clients here; injected into cloudflare/aws lanes — never imported (DP-3 §4, SF-11). +Gates: `- [ ] gate: emitted Dockerfile goldens` · `- [ ] gate: image digest recorded in the artifact manifest and verified by up --prebuilt`. +Deps: DPB-5. Shape: new adapter (injectable shared path). + +### DPB-21 `[deploy-plugin DPB-21] Thin platform clients: fly, koyeb, sevalla, coolify, dokploy + live smokes` · type:feat · p1 · beta.15 +Scope: subpath REST clients mapping the generic port (managed: fly Machines API, koyeb, sevalla; self-hosted: coolify, dokploy with base URL + token env names); live smokes ≥1 managed + ≥1 self-hosted. Anti-scope: graduation to own package only per the OF-2 rule. +Gates: `- [ ] gate: one shared conformance run per client` · `- [ ] gate: live smoke green on one managed + one self-hosted platform (probe lane)`. +Deps: DPB-20. Shape: subpath REST clients. + +### DPB-22 `[deploy-plugin DPB-22] Container scaffold story (Story 3) + artifact goldens` · type:feat · p2 · beta.15 +Scope: `target add fly|koyeb|sevalla|coolify|dokploy` assets (Dockerfile + compose parity, workflows, env-name contracts). Anti-scope: platform add-on catalogs only where leaf backings exist. +Gates: `- [ ] gate: goldens for every emitted artifact` · `- [ ] gate: workflows reference only documented secret/OIDC contracts`. +Deps: DPB-17, DPB-20. Shape: scaffold assets. + +### DPB-23 `[deploy-plugin DPB-23] CF-PROBE: live Workers conformance, Miniflare fidelity, token story` · type:feat · p1 · beta.16 +Scope: findings-only probe — live Workers deploy of a NetScript service; Miniflare-vs-production fidelity; `nodejs_compat` coverage for the service runtime's touchpoints; CI token story (no OIDC on CF). Anti-scope: no adapter code ships from this card. +Gates: `- [ ] gate: probe report committed with live evidence ids` · `- [ ] gate: go/no-go recommendation for DPB-24 recorded`. +Deps: DPB-5. Shape: probe card, findings-only. + +### DPB-24 `[deploy-plugin DPB-24] deploy-cloudflare (workers variant) + Story 1` · type:feat · p1 · beta.16 +Scope: wrangler wrap (emitted `wrangler.jsonc` + Web-standard worker entry over `ServiceApp.fetch`); workers-variant manifest (sagas rejected, kv:atomic unsupported, long-running unsupported); `suggestedCells` flow to a containers cell via injected `ContainerBuildPort`; Story-1 scaffold assets. Anti-scope: CF Queues consumption is leaf territory; one compute variant per declaration (L-7, DP-3 §5). +Gates: `- [ ] gate: ships only behind CF-PROBE pass` · `- [ ] gate: plan rejects isolate-exceeding graphs with suggestedCells` · `- [ ] gate: conformance cells for every claimed verdict`. +Deps: DPB-20, DPB-23. Shape: probe-gated adapter. + +### DPB-25 `[deploy-plugin DPB-25] Vercel probe + deploy-vercel (Build Output API) + Story 4` · type:feat · p1 · beta.16 +Scope: probe (minimal service through `.vercel/output` → live deploy → HTTP conformance) then the adapter (Build Output API v3 emission, `vercel deploy --prebuilt`; Node runtime default, `vercel-deno` opt-in with version-lag warning) + Story-4 assets. Anti-scope: marketplace backings are leaf cards. +Gates: `- [ ] gate: .vc-config.json emission validated by the live probe before adapter claims` · `- [ ] gate: conformance cells for claimed verdicts`. +Deps: DPB-5, DPB-17. Shape: probe-gated adapter. + +### DPB-26 `[deploy-plugin DPB-26] AWS-PROBE-HTTP: live Lambda Web Adapter conformance` · type:feat · p1 · beta.16 +Scope: findings-only probe — LWA container (denoland/deno image) behind Function URL/API GW; OIDC role-assume CI auth. Anti-scope: NO event/queue claims (SQS semantics are leaf territory, DPB-29). +Gates: `- [ ] gate: probe report with live evidence ids` · `- [ ] gate: go/no-go for DPB-27 recorded`. +Deps: DPB-20. Shape: probe card, findings-only. + +### DPB-27 `[deploy-plugin DPB-27] deploy-aws (lambda variant, HTTP scope) + Story 2` · type:feat · p1 · beta.16 +Scope: LWA container path via injected `ContainerBuildPort`; lambda-variant manifest (HTTP rows only; queue consume unverified with the leaf-card note); optional `./pulumi` subpath (Automation API); Story-2 assets with the OIDC role-trust snippet. Anti-scope: HTTP-only until AWS event probes pass (OF-8, L-7). +Gates: `- [ ] gate: ships only behind AWS-PROBE-HTTP pass` · `- [ ] gate: no event-semantics claims anywhere in manifest or docs` · `- [ ] gate: conformance cells for claimed verdicts`. +Deps: DPB-26. Shape: probe-gated adapter, HTTP scope. + +### DPB-28 `[deploy-plugin DPB-28] Docs: target-matrix reference + per-target how-tos replace the alpha-minimal page` · type:docs · p1 · beta.13 · +area:docs +Scope: the docs IA from the doc-story forecast (getting-started `plan → up` on deno-deploy; capabilities/doctor reference incl. `unverified` vs `unsupported`; migration page; per-target how-tos landing with their adapters). Anti-scope: no capability claims beyond shipped manifests; public-docs hygiene. +Gates: `- [ ] gate: docs accuracy eval green (docs gate)` · `- [ ] gate: every claim traces to a shipped manifest or command`. +Deps: DPB-16 (rolling; first tranche with W3). Shape: docs consolidation. + +### DPB-29 `[deploy-plugin DPB-29] Deferred RFC: AWS event semantics, leaf backing graduation, Radius target graduation` · type:feat · p2 · Backlog / Triage +Scope: RFC-first — SQS/event conformance design (leaf co-owned); leaf backing catalog graduation (kv/queue provider adapters); Radius as a descriptor-selected `deploy-aspire` variant behind machine-verifiable predicates (pinned CLI ≥ first TS-API release; `deno check` fixture; `app.bicep` emission; Radius step in `--list-steps`; recipe-binding fixture; per-environment capabilities `unverified` until probed). Anti-scope: no implementation from this card. +Gates: `- [ ] gate: RFC lands with owner-arbitrated forks before any implementation card is filed`. +Deps: DPB-27. Shape: RFC-first, leaf/upstream co-owned. + +## Post-steps + +1. Update epic body `## Children` with live numbers. +2. `FILING-LOG.md` (DPB-id → live number map + label/milestone actions) committed to the run dir. +3. Supersession (per RFC §6): close #824 with successor pointer; pointer comments on #823/#327/#454 (no closes); #451/#453/#455 untouched (KEEP). +4. PR #891 filing comment; labels.yml parity noted as follow-up. From 34376100d0685676a414344be74c4c87f4cb6f0a Mon Sep 17 00:00:00 2001 From: openhands Date: Sun, 19 Jul 2026 09:19:57 +0000 Subject: [PATCH 19/20] PLAN-EVAL: PASS for plan-deploy-plugin--seed (r5 corpus, 3 adversarial trails integrated) Co-authored-by: openhands --- .../plan-deploy-plugin--seed/plan-eval.md | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/plan-eval.md diff --git a/.llm/runs/plan-deploy-plugin--seed/plan-eval.md b/.llm/runs/plan-deploy-plugin--seed/plan-eval.md new file mode 100644 index 000000000..f802b3c6e --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/plan-eval.md @@ -0,0 +1,56 @@ +# PLAN-EVAL — plan-deploy-plugin--seed + +- **Plan evaluator session:** OpenHands / `openrouter/qwen/qwen3.7-max` (open-model evaluator lane), PR #891, action run 29681236970 +- **Run:** `plan-deploy-plugin--seed` (r5) — seed run, deliverable = board, not code +- **Surface / archetype:** composite **Archetype 7** delivered as A5 plugin + A2 core + A2 adapters (DP-1 §3) +- **Scope overlays:** none (framework/plugin design) +- **Hardening trail consumed:** Sol-xhigh adversarial r2 (16/16 accepted) → Kimi-K3 doc-story r3 (13/13 accepted) → DP-9 aspire composition r4 → Sol round-2 r5 (9/9 accepted, SG-1…SG-9 all integrated) +- **Context from drift.md:** D-2 explicitly notes this run's kickoff replaced the default stage pipeline with an owner-defined one; this session is the formal PLAN-EVAL of record, dispatched by owner directive via PR comment `@openhands-agent … use harness`. + +OPENHANDS_VERDICT: PASS + +## Checklist results + +| Plan-Gate item (from `gates/plan-gate.md`) | Result | Evidence / location | +| --- | --- | --- | +| Research present and current | ✅ PASS | `research.md` + six corpus files under `research/` (auth-composition-anatomy, board-parity-871-887, deploy-layer-inventory, doctrine-constraints, prior-run-distillation, provider-deploy-surfaces). Re-baseline: "`origin/main = 290c68ef` (2026-07-18). The prior run's board was drafts-only and never filed." | +| Decisions locked | ✅ PASS | `plan.md` §2: **LD-1…LD-12**, each with a rationale pointer to a specific `DP-*` section (family topology, dependency law, eight-op lifecycle, capability contracts, bindings, two-phase loader, wrap map, no service surface, named host extensions, refactor-then-extract, provider-optimized scaffolds, desktop exclusion). Spot-verified load-bearing: shipped `packages/cli/src/kernel/domain/deploy/deploy-target-port.ts` already exports `DeployOperation = plan|emit|up|down|status|logs|rollback|secrets` — the "extraction, not invention" claim in research.md §1 conclusion #2 holds. | +| Open-decision sweep | ✅ PASS | `plan.md` §3: **OF-1…OF-8** (owner forks — each with recommendation + alternative + rationale pointer), followed by four explicit "safe to defer" decisions (cloud-run re-home timing W4; `init --deploy ` sugar pure UX; deploy dashboard seam-priced per OF-4; secrets rotation overlap-window card). Each deferred item states why deferral is rework-safe. Evaluator re-sweep (below): **none**. | +| Commit slices (< 30, gate + files each) | ✅ PASS (seed-run interpretation) | `plan.md` §5: **29 `DPB-n`** child cards (under the 30 cap), ordered by dependency DAG, each with wave, priority, description, and dependencies. Per-card acceptance predicate / proving-gate text / files-touched are authored at stage-H board filing (per the seed-run convention explicitly stated in the final template paragraph "`Part of #EPIC` → scoping paragraph → `- [ ] gate:` acceptance → `Dependencies:` + `Delivery shape:`"). For a seed run whose deliverable is a board, this table is the slicing artifact. | +| Risk register | ✅ PASS | `plan.md` §6: 10 risks, each with severity (`high`/`med`/`low`) and mitigation. Notable: capability-vocabulary risk marked `~~high~~ resolved (r2)` via SF-6; Aspire surface churn risk addressed in r4 DP-9 §4; JSR surface growth mitigated by OF-2 subpath folding + export budgets; probe-failures risk mitigated by findings-first probes + W1–W4 value independence. | +| Gate set selected | ✅ PASS | `plan.md` §7: A5 gates for the plugin (+R-PLUGIN-PARITY), full A2 set for core and every adapter, A7 union + F-DEPLOY-1/2 flipped to `gated` at W1, plus `quality:scan`, `arch:check`, scoped check/lint/fmt wrappers, `deno doc --lint`, publish dry-run, jsr-audit per package, `scaffold.runtime` at W3, conformance suite matrix from W2. Referenced against `gates/archetype-gate-matrix.md`. | +| Deferred scope explicit | ✅ PASS | `plan.md` §9: explicit list — frontend contribution axis (parallel run); leaf backing packages (`kv-cloudflare`, `queue-sqs`, DO saga store); desktop packaging (#830); Netlify/Railway/Render/GKE (open registry, community path); Nitro emitter (OF-7 conditional); deploy dashboard service (OF-4); secrets rotation overlap-window; AWS event semantics (DPB-29). | +| jsr-audit surface scan (pkg/plugin waves) | ✅ PASS | `plan.md` §7 second paragraph: "no oRPC contract in core or plugin v1 ⇒ no `--allow-slow-types` exception anywhere in the family"; "adapters keep vendor CLIs at process boundaries so no vendor types can leak into public signatures (AP-14 guarded by F-15)"; "export budgets per subpath ≤ 20 (F-5)"; "string-constant templates for all emitted artifacts". | + +## Open-decision sweep (evaluator-run) + +I re-ran the sweep against the full r5 corpus (plan.md, rfc.md + Addendum A, DP-0…DP-9, the r2/r4 adversarial trails, the r3 doc-story trail, drift.md). + +**Result: no open decisions found that would force rework if deferred.** + +The SG-1…SG-9 round-2 blockers/majors (prebuilt applier matrix, no-save secret/state policy, single-compiler snapshot parity, environment identity normalization, capability-pipeline step re-homing, target×op table, Aspire compatibility wording, conditional step delivery, Radius predicate gates) are all recorded as **accepted** in `adversarial-sol-r4-triage.md` (9/9) and integrated in the r5 corpus (per drift D-8). Spot-checks: + +- SG-1 (prebuilt applier matrix): plan.md DPB-8 cell mentions "the **applier matrix** for declared `--prebuilt` rows" ✓ +- SG-3 (pipeline step re-homing): DPB-8 now says `adapter-neutral runCapabilityCheck`; the step registration moved to DPB-17 ✓ +- SG-8 (Radius predicate gates): DPB-29 captures Radius graduation with machine-verifiable predicates and unverified-until-probed environment capabilities ✓ + +Three other potential open candidates I tested and discharged: + +1. **"Is the 29-child board actually fileable as-is?"** — Yes. Each card has enough identity (wave, priority, description, dependencies) that stage-H body-authoring is mechanical. The template explicitly scopes bodies to the filing step. +2. **"Aspire pipeline-step delivery is now in DPB-17 but W3 plugin scaffold depends on W2 adapter extraction (DPB-8) — is that circular?"** — No. DPB-17 depends on DPB-5, DPB-8, DPB-15 (plan.md §5 line 106); all three precede it in the DAG. +3. **"Do the probe-gated W5 adapters need anything that's not in W1–W4?"** — No. DPB-23/26 probe cards are findings-first; the adapters land only behind passing probes (L-7); plan.md §6 "Probe failures strand W5 adapters" notes W1–W4 value is independent of W5. + +## Verdict + +**PASS** + +The plan-gate checklist is satisfied in full. The corpus has been through three downstream adversarial/doc-story passes (Sol-xhigh 16/16, Kimi-K3 13/13, Sol-r4 9/9) with every finding integrated, producing a coherent locked-decision set (LD-1…LD-12), a dependency-ordered 29-child board (DPB-1…DPB-29), an explicit owner-fork arbitration table (OF-1…OF-8), a risk register with named mitigations, and an explicit deferred-scope list. Load-bearing repo claims (the shipped 8-op `DeployTargetPort`, the `DeployTargetRegistryPort`, the config deploy schema, the auth-plugin composition pattern this family mirrors) are spot-verified first-hand against the tree and match the corpus citations. + +Implementation may begin after **owner ratification of OF-1…OF-8** and stage-H board filing — neither of which this evaluator pass performs. + +## Notes + +- **Seed-run interpretation (commit-slices item):** The plan-gate's "each names what it proves, the gate that proves it, and the files it touches" clause is literal for implementation-bound plans. For a seed run whose deliverable is a fileable board, the accepted convention (stated in `plan.md` §5) is that this metadata lives in the issue body authored at filing. The evaluator accepts the 29-card dependency-ordered table as the slicing artifact under this interpretation; if the implementer's PLAN-EVAL were to apply on a future code-bound run, the per-card gate/file enumeration would be re-checked against the filed issue bodies. +- **Load-bearing spot-verification performed:** `packages/cli/src/kernel/domain/deploy/deploy-target-port.ts` (8 canonical ops confirmed), `packages/cli/src/kernel/domain/deploy/deploy-target-registry-port.ts` (registry port confirmed), `packages/config/src/domain/schemas/deploy-schema.ts` (config deploy schema present), `plugins/auth/` (auth composition pattern confirmed as the mirror template). +- **Hardening trail integrity:** `adversarial-sol-triage.md` (16/16), `doc-story-kimi-triage.md` (13/13), `adversarial-sol-r4-triage.md` (9/9) — all triages present, all findings accepted, all integrated in r5 per drift D-8. +- **No code was evaluated** (this is PLAN-EVAL, not IMPL-EVAL). No implementation slices exist yet. From d592d63fe53766cf305a7e665c69287775981366 Mon Sep 17 00:00:00 2001 From: Rickylabs Date: Sun, 19 Jul 2026 11:27:42 +0200 Subject: [PATCH 20/20] =?UTF-8?q?plan(deploy-plugin):=20FILING-LOG=20?= =?UTF-8?q?=E2=80=94=20board=20filed=20on=20PLAN-EVAL=20PASS=20(epic=20#89?= =?UTF-8?q?2,=20children=20#893-#921)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EWs7xBg7oNqCCkxipVM5HR --- .../plan-deploy-plugin--seed/FILING-LOG.md | 54 +++++++++++++++++++ .llm/runs/plan-deploy-plugin--seed/worklog.md | 13 +++++ 2 files changed, 67 insertions(+) create mode 100644 .llm/runs/plan-deploy-plugin--seed/FILING-LOG.md diff --git a/.llm/runs/plan-deploy-plugin--seed/FILING-LOG.md b/.llm/runs/plan-deploy-plugin--seed/FILING-LOG.md new file mode 100644 index 000000000..5e05c2c34 --- /dev/null +++ b/.llm/runs/plan-deploy-plugin--seed/FILING-LOG.md @@ -0,0 +1,54 @@ +# FILING-LOG — deploy-plugin board (2026-07-19) + +> **Authority: GitHub wins on conflict from this point.** Filed on PLAN-EVAL PASS +> (OpenHands · qwen/qwen3.7-max, verdict comment on PR #891) under the owner directive +> "dispatch & on pass create the epic, sub issues and prioritize them on milestones (allowed to +> create further betas)". Source: `filing-manifest.md` (commit `c7c68270`), executed one-shot. + +## Created + +- Label `epic:deploy-plugin` (5319e7). **Parity follow-up:** add to `.github/labels.yml` in a + framework slice (this run cannot edit repo config). +- Milestones: `0.0.1-beta.15` = #17 (W4 container path) · `0.0.1-beta.16` = #18 (W5 probe-gated + clouds). Existing used: `0.0.1-beta.13` = #15 (W1–W3 + host + docs) · `Backlog / Triage` = #3. +- **EPIC #892** — Epic: Deploy plugin family (beta.13). + +## DPB → live issue map + +| DPB | # | Milestone | | DPB | # | Milestone | +| --- | --- | --- | --- | --- | --- | --- | +| DPB-1 | #893 | beta.13 | | DPB-16 | #908 | beta.13 | +| DPB-2 | #894 | beta.13 | | DPB-17 | #909 | beta.13 | +| DPB-3 | #895 | beta.13 | | DPB-18 | #910 | beta.13 | +| DPB-4 | #896 | beta.13 | | DPB-19 | #911 | beta.13 | +| DPB-5 | #897 | beta.13 | | DPB-20 | #912 | beta.15 | +| DPB-6 | #898 | beta.13 | | DPB-21 | #913 | beta.15 | +| DPB-7 | #899 | beta.13 | | DPB-22 | #914 | beta.15 | +| DPB-8 | #900 | beta.13 | | DPB-23 | #915 | beta.16 | +| DPB-9 | #901 | beta.13 | | DPB-24 | #916 | beta.16 | +| DPB-10 | #902 | beta.13 | | DPB-25 | #917 | beta.16 | +| DPB-11 | #903 | beta.13 | | DPB-26 | #918 | beta.16 | +| DPB-12 | #904 | beta.13 | | DPB-27 | #919 | beta.16 | +| DPB-13 | #905 | beta.13 | | DPB-28 | #920 | beta.13 | +| DPB-14 | #906 | beta.13 | | DPB-29 | #921 | Backlog/Triage | +| DPB-15 | #907 | beta.13 | | | | | + +All children: `[deploy-plugin DPB-n]` titles; labels `epic:deploy-plugin area:deploy status:plan` ++ one `type:` + one `priority:` + secondary areas per manifest; `Part of #892` bodies with +`- [ ] gate:` acceptance + `Dependencies:`/`Delivery shape:` metadata. + +## Supersession executed (RFC #891 §6) + +| Issue | Action | +| --- | --- | +| #824 | CLOSED with successor pointer (epic #892 / RFC #891) | +| #823 | KEPT OPEN — pointer comment: deploy half now owned by #892; single-runtime framing to be re-stated (owner) | +| #327 | KEPT OPEN — pointer comment: #892 is the deployment architecture successor | +| #454 | KEPT OPEN — pointer comment: deploy aspect absorbed by the cell/capability model; close-or-fold = owner call | +| #451 / #453 / #455 | UNTOUCHED (KEEP per RFC) | + +## Not done (deliberate) + +- GitHub-native sub-issue linkage (opportunistic nice-to-have; Markdown checklist + `Part of` + used, per netscript-pr). +- `.github/labels.yml` parity (framework slice; noted above). diff --git a/.llm/runs/plan-deploy-plugin--seed/worklog.md b/.llm/runs/plan-deploy-plugin--seed/worklog.md index 704bdffa2..d1631de67 100644 --- a/.llm/runs/plan-deploy-plugin--seed/worklog.md +++ b/.llm/runs/plan-deploy-plugin--seed/worklog.md @@ -229,3 +229,16 @@ STAGE-COMPLETE: doc-story-integration canonical changes deliberate); plan.md DPB-1/8/17/29 recut (step → DPB-17 with deps 5/8/15); rfc.md r5 (status, abstract purity wording, §5/§6, Addendum A round-2 paragraph, provenance). Family topology, ports, and 29-child board count unchanged. + +## 2026-07-19 — PLAN-EVAL PASS + one-shot board filing + +- Formal PLAN-EVAL dispatched on PR #891 (OpenHands, `openrouter/qwen/qwen3.7-max`) → + **`[PHASE: PLAN-EVAL] [VERDICT: PASS]`** (verdict comment on the PR = record). +- Filing executed from `filing-manifest.md` under the owner's in-turn directive: label + `epic:deploy-plugin`; milestones beta.15 (#17) + beta.16 (#18) created; **EPIC #892** + + children **#893–#921** (29/29, sequential, zero failures); epic children checklist updated; + supersession per RFC §6 (close #824; pointers #823/#327/#454). Details: `FILING-LOG.md`. +- **GitHub is now the single source of truth for the board**; run docs carry tags under that + authority rule. + +STAGE-COMPLETE: filing