Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .llm/runs/fix-aspire-ephemeral-host-ports--952/context-pack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Context Pack — fix-aspire-ephemeral-host-ports--952

## What this run is

Fixes #952: a pristine `netscript init` generates `.withHttpEndpoint({ port: N, env: 'PORT' })` for
service and app resources. `port` is the Aspire **host** port; `aspire start --isolated` cannot
randomise a port the AppHost pinned, so two NetScript workspaces on one machine collide and the
dashboard can advertise a URL owned by another instance.

## The one-paragraph root cause

The three register generators interpolate `entry.Port` into the endpoint options unconditionally.
Aspire's documented shape for a non-.NET executable resource is `.withHttpEndpoint({ env: 'PORT' })`
— no port — which lets Aspire allocate both the host and target port and inject the target into the
process. `packages/cli/.../generate-register-infrastructure.ts` already uses the isolation-safe
idiom for containers; the executable registrations were simply never updated.

## Load-bearing facts (do not re-derive)

- For `addExecutable` resources, **both** `port` and `targetPort` are host-machine ports. The issue's
suggested `targetPort` fix moves the collision rather than removing it. → `research.md` §4 C-2.
- `aspire start --isolated` exports **no** env signal an AppHost can read, so a
"pin unless isolated" design is not available. → `research.md` F-2.
- The scaffolded service and app templates already read `PORT` with a literal fallback, so
un-pinning needs no runtime change. → `research.md` F-4.
- Cross-resource wiring uses `getEndpoint('http')` and never reads `Port`. → `research.md` F-5.
- `e2e/.../runtime-gates.ts` live-probes `127.0.0.1:8091–8094`; nothing probes `:3000` or `:8010`.
This is why plugin API ports are out of scope and service/app ports are in. → `research.md` F-8.

## Contract shape after this run

`HostPort ?? Port ?? (no pin)`. `Port` keeps its exact current meaning as a deprecated alias, so
every workspace already on disk behaves identically. Only newly scaffolded workspaces get the
isolation-safe default.

## Where things live

| Thing | Path |
| ------------------------------ | -------------------------------------------------------------------------------- |
| Endpoint rendering rule | `packages/cli/src/kernel/templates/aspire/helpers/register/render-http-endpoint.ts` |
| The three register generators | `packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-{services,plugins,apps}.ts` |
| Config contract | `packages/aspire/config.ts` (types + zod) |
| Scaffold plan | `packages/cli/src/kernel/application/scaffold/{validate-init,render-ts-apphost}.ts` |
| Pristine appsettings | `packages/cli/src/kernel/templates/aspire/generate-appsettings.ts` |
| Regression guard | `.llm/tools/validation/check-aspire-host-ports.ts` → `deno task check:aspire-host-ports` |

## Open after this run

- Plugin API resources (`8091–8094`) still pin host ports. Blocked on the `scaffold.runtime` gates
resolving endpoints from the Aspire resource service. Follow-up issue.
- `scaffold.runtime` was not runnable in this worktree (needs Docker + dotnet). The release cut that
picks this change up must run it. → `drift.md` D-5.

## Run outcome

PR: https://github.com/rickylabs/netscript/pull/978 (`fix/aspire-ephemeral-host-ports`, 3 commits).
Follow-ups filed: #979 (plugin API ports), #980 (`netscript service add`).
All gates green except `scaffold.runtime`, declared not runnable here.
56 changes: 56 additions & 0 deletions .llm/runs/fix-aspire-ephemeral-host-ports--952/drift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Drift — fix-aspire-ephemeral-host-ports--952

## D-1 — Evaluator passes are self-recorded, not independently sessioned

`run-loop.md` §4 and §7 require PLAN-EVAL and IMPL-EVAL to run in sessions separate from the
implementation session. The task brief assigns one issue to one agent with a single hand-back, and
no second session is available. Both verdicts are therefore **self-recorded** by the implementing
supervisor. Disclosed in `supervisor.md` § overrides and stated in the PR body so a reviewer does not
mistake `plan-eval.md` for an independent verdict.

## D-2 — The issue's suggested mechanism (`targetPort`) does not fix the reported bug

Issue #952 proposes "treat a configured service port as the target port … and leave the Aspire host
port ephemeral". For **container** resources a target port is namespaced and pinning it is free; for
**executable** resources — which every NetScript service, plugin and app is (`builder.addExecutable`)
— the target port is the port the `deno` process itself binds on the host machine. Pinning it moves
the collision from Aspire's proxy to the process's own `listen()` and additionally blocks replicas.

The plan follows the issue's stated *expected behaviour* and its `HostPort` naming suggestion, and
rejects the `targetPort` mechanism in favour of emitting no port at all
(`.withHttpEndpoint({ env: 'PORT' })`), which is Aspire's own documented shape for non-.NET
resources. Recorded in `research.md` §4 C-2 and stated in the PR.

## D-3 — "`targetPort` appears nowhere in `packages/aspire/src`" is true but points at the wrong file

Literally true, but the generated apphost is produced by
`packages/cli/src/kernel/templates/aspire/helpers/register/`, and
`generate-register-infrastructure.ts` **already emits `targetPort`** for the DenoKV container and the
Garnet executable. The idiom was present; it had simply never been applied to the executable
resources — and, per D-2, applying it there would have been the wrong move anyway.

## D-4 — Scope is wider than the issue title in one direction, narrower in another

- **Wider:** the issue reports service ports; the app resource (`8010`) carries the identical defect
and is in the pristine scaffold, so leaving it pinned would leave the issue's own reproduction
failing. Apps are included.
- **Narrower:** plugin API resources (`8091–8094`) carry the identical defect but are live-probed by
`e2e/src/application/gates/scaffold/runtime-gates.ts` and referenced by ~20 tutorial passages.
Un-pinning them is blocked on the E2E gates resolving endpoints from the Aspire resource service.
Deferred with a named blocker and a follow-up issue.

## D-5 — `scaffold.runtime` could not be run in this worktree

`gates/release-gates.md` makes `scaffold.runtime` required before the next release cut for any run
that changes scaffold output — which this run does. The gate needs Docker and the dotnet Aspire host,
neither available here. **Declared not run**, in the PR body, rather than silently skipped. The
release cut that picks this change up must run it.

## D-6 — `packages/config` dropped from the change set

`plan.md` slice 2 listed `packages/config/src/domain/schemas/service-schema.ts`. Verification during
implementation showed that surface (`netscript.config.ts`, lowercase `port`) never reaches the
generated apphost: the only `port` → `Port` bridge in the repo is the local `AppsettingsServiceOption`
type inside `generate-appsettings.ts`. Widening `ServiceConfig.port` would have been a contract change
with no consumer — and `config-root-types.ts` deliberately requires it in the override shape
(`Pick<ServiceConfig, 'port'>`). Dropped.
31 changes: 31 additions & 0 deletions .llm/runs/fix-aspire-ephemeral-host-ports--952/plan-eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# PLAN-EVAL — fix-aspire-ephemeral-host-ports--952

> **Provenance:** self-recorded by the implementing supervisor. `run-loop.md` §4 requires a separate
> session; none is available in this single-agent assignment. See `drift.md` D-1. Read this as a
> completed checklist, not an independent verdict.

Protocol: `evaluator/plan-protocol.md`. Checklist: `gates/plan-gate.md`.

| # | Check | Verdict | Evidence |
| - | ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
| 1 | Research present and current | PASS | `research.md` F-1…F-8 each re-derived against `main @ 8e0bcef39`; nothing carried in; §4 records two issue claims that failed re-baselining. |
| 2 | Decisions locked | PASS | D-1…D-7 in `plan.md`, each with rationale. The load-bearing one (D-1) is grounded in the endpoint-semantics table in `research.md` §4 C-2. |
| 3 | Open-decision sweep | PASS | Seven decisions swept; five resolved, two marked "safe to defer" with the reason they cannot force rework. |
| 4 | Commit slices | PASS | 8 slices, ordered, < 30; each names what it proves, its gate, and its files. Ordering constraint 2→3→5 stated. |
| 5 | Risk register | PASS | R1–R6 with mitigations. R4/R6 are the ones that matter: R4 is the E2E blast radius (mitigated by D-5 scoping), R6 is regression (mitigated by slice 6). |
| 6 | Gate set selected | PASS | Arch 6 + SCOPE-service table in `plan.md`; the one gate that cannot run (`scaffold.runtime`) is declared, not omitted. |
| 7 | Deferred scope explicit | PASS | Four items, each with the blocker that defers it. |
| 8 | jsr-audit surface scan | PASS | `research.md` §3 — widening-only contract change, no new exported symbol, no slow-type risk; `publish:dry-run` in the gate set. |

## Findings that would have been `FAIL_PLAN` if unaddressed

- **The plan initially inherited the issue's `targetPort` mechanism.** Research §4 C-2 shows it does
not fix the bug for executable resources. Had this been deferred to implementation, slice 3 would
have been written and thrown away. Resolved in D-1 *before* slicing — this is the Plan-Gate doing
its job.
- **Scope symmetry.** An early reading fixed services only, which leaves the pristine app pinned and
the issue's own reproduction failing. Resolved in D-5 / drift D-4.

## Verdict

**PASS** — implementation may begin.
Loading
Loading