Skip to content

fix(aspire): emit withBrowserLogs for endpoint-bearing generated app resources - #1607

Merged
rickylabs merged 2 commits into
mainfrom
fix/1577-aspire-browser-logs
Aug 12, 2026
Merged

fix(aspire): emit withBrowserLogs for endpoint-bearing generated app resources#1607
rickylabs merged 2 commits into
mainfrom
fix/1577-aspire-browser-logs

Conversation

@rickylabs

@rickylabs rickylabs commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

Restore the documented default browser-log child for generated frontend app resources. Generated
register-apps.mts now awaits withBrowserLogs() after endpoint binding while endpoint-less
task/desktop executables remain unchanged.

Scope

Slices

  • S1 Prove endpoint-gated browser-log emission and stale-test reconciliation — 8797596bb

API confirmation

Restored a TypeScript AppHost against the exact pinned
Aspire.Hosting.Browsers@13.4.6-preview.1.26319.6 package and inspected the generated
.aspire/modules/aspire.mts surface. It declares:

withBrowserLogs(options?: WithBrowserLogsOptions): ExecutableResourcePromise;
export interface ExecutableResourcePromise extends PromiseLike<ExecutableResource> { ... }

The generated implementation returns ExecutableResourcePromiseImpl around a
Promise<ExecutableResource>, so await <app>.withBrowserLogs() is supported on the exact pinned
executable type.

Stale-test rationale

The contradicted test generated this current setup:

apps: { frontend: fixtures.MINIMAL_APP }

and its immediately preceding case asserted
.withHttpEndpoint({ port: 8000, env: 'PORT' }). It therefore covered an endpoint-bearing frontend
app, not a background or endpoint-less resource. Its negative withBrowserLogs assertion encoded
the removed #781 API limitation and is now a positive assertion with endpoint-before-browser-log
ordering. The endpoint-less fixtures.UNPINNED_TASK_APP case remains negative and now explicitly
forbids withBrowserLogs.

Validation

  • focused package-task filter — exit 0
  • scoped check — exit 0; 867 files, 8 batches, 0 failed, 0 findings
  • scoped lint — exit 0; 867 files, 5 batches, 0 findings
  • scoped fmt — exit 0; 867 files, 5 batches, 0 failed, 0 findings
  • deno task --cwd packages/cli test — exit 1: 799 passed (531 steps) | 3 failed; all changed
    generator/config tests pass, while three unrelated E2E harness tests fail on missing repo-relative
    docs/script paths under the mandated package cwd
  • deno task quality:gate — exit 0; quality scan clean, doctrine FAIL=0 with existing warnings
  • deno.lock SHA-256 unchanged:
    73be92b116b9065372505157da4f6729176e975aa118e9944746317887e9a4c4
  • e2e:cli — not run; explicitly reserved for the orchestrator

Harness

  • Run dir: .llm/runs/fix-1577-aspire-browser-logs--impl/
  • Phase: impl; automatic separate-session evaluation remains orchestrator-owned
  • PLAN-EVAL: N/A — mechanical issue with locked contract, predicate, placement, tests, and gates

Drift / Debt

  • No new or deepened architecture debt.
  • Required package task remains red on three unrelated missing-path/missing-script E2E tests; exact
    failures are recorded in worklog.md and the IMPL phase comment.
  • Shipped help source skills/help.md already describes the restored behavior accurately; no
    source or generated asset change was needed.
issue: 1577
entries:
  - box-index: 1
    evidence: "generators-background-app_test.ts: endpoint-bearing app requires awaited withBrowserLogs after endpoint binding"
  - box-index: 2
    evidence: "generate-aspire-config_test.ts passes and retains Aspire.Hosting.Browsers@13.4.6-preview.1.26319.6"
  - box-index: 3
    evidence: "stale MINIMAL_APP negative assertion replaced with positive call and ordering assertions"
  - box-index: 4
    evidence: "exact pinned-package aspire restore generated an awaitable ExecutableResourcePromise.withBrowserLogs surface; scoped package check passes"
  - box-index: 5
    evidence: "Orchestrator-run serialized scaffold.runtime smoke at head 966bed5dd (passed=79 failed=1; runtime.aspire-restore, generated.deno-check and runtime.aspire-describe all PASS; sole failure is an unrelated WSL headless-browser vsock timeout in behavior.app-reference). Live `aspire describe --apphost aspire/apphost.mts --include-hidden --format Json` against the running generated AppHost returned resource plugin-smoke-20260812-194940-web-browser-logs with resourceType BrowserLogs and source plugin-smoke-20260812-194940-web. Evidence comment: https://github.com/rickylabs/netscript/pull/1607#issuecomment-5270636873"
  - box-index: 6
    evidence: "HelpersGeneratorPipeline and generateRegisterApps package tests pass; behavior is emitted by the source generator rather than a downstream helper edit"

Definition of Done

  • Endpoint-bearing generated frontend apps await withBrowserLogs() after endpoint binding.
  • Endpoint-less task/desktop resources remain unaffected with a focused negative test.
  • Browser integration pin and regeneration behavior remain covered.
  • Required static and quality gates pass; full package-task red is reported without hiding it.
  • Automatic separate-session evaluation and orchestrator-owned live runtime smoke complete — formal IMPL-EVAL PASS on exact head 966bed5dd (run 31623332508); serialized scaffold.runtime smoke passed=79 failed=1 with runtime.aspire-restore, generated.deno-check, and runtime.aspire-describe all PASS, the single failure being an unrelated WSL headless-browser vsock transport timeout in behavior.app-reference; live aspire describe --include-hidden returned the ...-web-browser-logs child with resourceType: BrowserLogs and source: ...-web. Evidence in the runtime-gate comment.

@rickylabs

rickylabs commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL]

Landed endpoint-gated browser-log emission and reconciled the stale endpoint-bearing app test in
8797596bb26dd7425eeb826dcec0bfa789282ee9.

Scope

  • Emits await <app>.withBrowserLogs(); inside the runtime enabled block, immediately after the
    existing HTTP endpoint call, only when the generated entry is Type: 'app' and endpoint-bearing.
  • Keeps endpoint-less task/desktop executables free of browser-log children.
  • Converts the stale fixtures.MINIMAL_APP negative assertion into positive call + ordering
    evidence; extends the endpoint-less task negative guard.
  • Leaves correct help source and generated assets untouched.

API proof

An exact restore using Aspire.Hosting.Browsers@13.4.6-preview.1.26319.6 generated
withBrowserLogs(options?): ExecutableResourcePromise; that promise interface extends
PromiseLike<ExecutableResource>, confirming the emitted call is awaitable.

Gate output

scoped check: {"selection":{"filesSelected":867,"batches":8,"failedBatches":0},"summary":{"totalOccurrences":0,"uniqueOccurrences":0,"uniqueCodes":0,"uniquePaths":0},"groups":[]}
scoped lint: {"selection":{"filesSelected":867,"batches":5},"summary":{"totalOccurrences":0,"uniqueOccurrences":0,"uniqueRules":0,"uniquePaths":0},"groups":[]}
scoped fmt: {"summary":{"filesSelected":867,"batches":5,"failedBatches":0,"findings":0,"ignoredFindings":0},"findings":[]}
package test: FAILED | 799 passed (531 steps) | 3 failed (2m46s)
quality:gate: exit 0; quality scan ok=true; doctrine FAIL=0 (existing warnings only)
deno.lock: 73be92b116b9065372505157da4f6729176e975aa118e9944746317887e9a4c4 (unchanged)

The package-task failures are unrelated missing-path/missing-script E2E fixtures under the mandated
package cwd:

  1. docs/site/durable-workflows/streams.md not found.
  2. packages/cli/e2e/src/application/gates/scaffold/service-env/configure-service-env.ts not found.
  3. docs/site/quickstart.vto not found.

All changed generator tests and the browser integration pin test passed in that run. The serialized
runtime smoke and separate-session evaluation remain pending and orchestrator-owned; this session
did not launch either.

@rickylabs

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL] resync + slice review

Resynced onto current main@66196034e by merge — head 966bed5dd, clean. Re-ran the package suite on the merged head because #1587 touched 14 packages/cli files: 799 passed (531 steps) | 3 failed, the same three failures you reported and identical to the base.

Those three are now filed as #1604docs/site/durable-workflows/streams.md, packages/cli/e2e/.../configure-service-env.ts, and docs/site/quickstart.vto are repo-root-relative reads that cannot resolve under --cwd packages/cli, so the command AGENTS.md prescribes as canonical is red on a clean tree for everyone. Your run independently reproduced them, which is part of what made the pattern legible enough to file. Reporting the red rather than hiding it was the right call.

On the two things I asked you to be careful about:

You confirmed the API before emitting — withBrowserLogs(options?): ExecutableResourcePromise, and that promise extending PromiseLike<ExecutableResource> is what makes the emitted await correct. That mattered: emitting a call that did not resolve on the pinned type is precisely how #781 removed this in the first place.

And you reconciled the stale assertion rather than flipping it. Converting fixtures.MINIMAL_APP from a negative assertion into positive call plus ordering evidence, while extending the endpoint-less task negative guard, keeps the negative case with a home — which was my specific concern. The endpoint-bearing absence assertion encoded the old ExecutableResourcePromise limitation and is genuinely stale now; the endpoint-less absence is real behaviour and still guarded.

Worth stating plainly for the record: before this change NetScript shipped the Aspire.Hosting.Browsers pin and agent-facing help text telling agents that generated app resources emit withBrowserLogs() by default and to use aspire logs instead of Playwright — for a call the generator never emitted. The documentation was right about the intent and the generator was wrong.

On the remaining Definition-of-Done box bundling evaluation with a live runtime smoke: this changes Aspire helper generation, which is on the list that warrants a runtime pass. That is covered by the required scaffold-runtime CI lanes that run on ready, so I am not dispatching a separate serialized e2e:cli run. If those lanes come back red on generated Aspire output, that is a real product signal and I will treat it as one.

deno.lock unchanged (hash recorded in your report). Flipping to ready — automatic IMPL-EVAL fires once at this head.

@rickylabs
rickylabs marked this pull request as ready for review August 12, 2026 17:34
@rickylabs

Copy link
Copy Markdown
Owner Author

@openhands-agent model=openrouter/deepseek/deepseek-v4-flash-0731 output=pr-comment iterations=800 phase=impl head=966bed5dd7b6c94c55a03c56a5f0f26e2cf7feaa

Trusted base SHA: 6619603
Evaluated head SHA: 966bed5

use harness

SKILL

  • netscript-harness — apply the formal IMPL-EVAL protocol and verdict vocabulary.
  • openhands-handoff — publish one machine-readable OpenHands verdict.
  • netscript-tools — run the smallest decisive repository-native gates without mutating source.
  • netscript-doctrine — apply package/plugin doctrine when the changed surface requires it.

Act as the formal IMPL-EVAL session for this pull request. Do not edit files, create commits, push,
or repair findings. The trigger metadata supplies the trusted base SHA and immutable head SHA: read
the evaluator protocol, verdict definitions, and selected profiles from that base commit, then
evaluate the PR body, linked issues, run artifacts, final diff, review threads, and architecture
debt at the immutable head. Verify the approved plan or recorded PLAN-EVAL: N/A, design
checkpoint, acceptance criteria, static/runtime/consumer gates, public surface, lock hygiene, and
false-done states. For documentation changes, also read every changed document fully and hand-test
representative executable claims.

Return concise, severity-ranked findings with exact evidence and required action. End with exactly
one supported verdict line using OPENHANDS_VERDICT: PASS, OPENHANDS_VERDICT: FAIL_FIX,
OPENHANDS_VERDICT: FAIL_RESCOPE, OPENHANDS_VERDICT: FAIL_DEBT, or
OPENHANDS_VERDICT: FAIL_PLAN. Write the same verdict to OPENHANDS_SUMMARY_PATH.

@rickylabs

rickylabs commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

OpenHands Agent — Completed

OPENHANDS_VERDICT: PASS

Model: openrouter/deepseek/deepseek-v4-flash-0731
Provider: OPENROUTER
Job status: success
Verdict source: summary-file
Verdict comment: #1607 (comment)
Summary source: agent
Housekeeping: ack=success trace=success commit-artifacts=skipped replies=skipped draft-pr=skipped
Trace: .llm/tmp/run/openhands/pr-1607/run-31623332508-1 (in the Actions artifact)
Artifact: openhands-agent-31623332508-1

OPENHANDS_VERDICT: PASS

IMPL-EVAL — PR #1607 · fix(aspire): emit withBrowserLogs for endpoint-bearing generated app resources

Evaluated head: 966bed5dd7b6c94c55a03c56a5f0f26e2cf7feaa (merge of origin/main 6619603 into fix/1577-aspire-browser-logs)
Run: .llm/runs/fix-1577-aspire-browser-logs--impl/, archetype 6 (CLI/Tooling), owner-authorized OpenHands/DeepSeek V4 Flash 0731 IMPL-EVAL lane.

Summary

The PR restores the documented default browser-log child for generated Aspire apps. generate-register-apps.ts
now emits await <app>.withBrowserLogs(); inside the endpoint branch, after withHttpEndpoint(...) and before
the readiness probe, gated by type === 'app' so endpoint-bearing task/tauri/desktop executables never gain a
browser child. The stale fixtures.MINIMAL_APP negative assertion was converted to positive + ordering evidence,
and the endpoint-less task negative guard now also forbids withBrowserLogs.

Changes

  • packages/cli/src/kernel/templates/aspire/helpers/register/generate-register-apps.ts — endpoint-gated awaited withBrowserLogs() emission after withHttpEndpoint, before health probe.
  • packages/cli/src/kernel/templates/aspire/helpers/tests/generators-background-app_test.ts — stale minimum-app absence assertion → positive call + endpoint-before-browser-log ordering assertion; endpoint-less task negative guard extended.
  • Run artifacts under .llm/runs/fix-1577-aspire-browser-logs--impl/ (supervisor/research/plan/worklog/drift/context-pack).
  • No other source touched; deno.lock unchanged (SHA-256 73be92b116b9065372505157da4f6729176e975aa118e9944746317887e9a4c4).

Validation (independent)

  • Focused suite generators-background-app_test.ts: 2 passed (42 steps), 0 failed — new browser-log test and extended negative pass.
  • generate-aspire-config_test.ts: passes; Aspire.Hosting.Browsers pin 13.4.6-preview.1.26319.6 retained.
  • Scoped wrappers on packages/cli/src/kernel/templates/aspire: check exit 0 (33 files, 0 findings), lint exit 0, fmt exit 0.
  • Emission semantics verified by reading generator + fixtures: needsHttpEndpoint is always true for Type:'app'; non-app types with configured ports (TAURI_APP 8998, TASK_APP 8997) enter the endpoint branch but are excluded from withBrowserLogs by the type==='app' guard.
  • deno.lock hash re-verified unchanged after my runs; working tree clean.

Process / plan conformance

  • PLAN-EVAL: N/A recorded in worklog.md with concrete mechanical-fix rationale before implementation (run-loop §4 allows this).
  • Full Design checkpoint present in worklog (public surface, vocabulary, ports, constants, commit slices, deferred scope, contributor path).
  • Slice review gate performed by owner (PR comment 5270237107): API awaitability and stale-test reconciliation explicitly re-checked before handoff.
  • Commit trail present: 8797596bb (implementation) + merge 966bed5dd, with per-slice IMPL/resync comments.
  • No new any/casting, no doctrine violation, no arch-debt entry needed (plan records none).

Findings (severity ranked)

  1. Pending runtime gate — orchestrator-owned, NOT blocking IMPL-EVAL, blocks ready-merge. Issue fix(aspire): restore default browser-log child for generated executable apps #1577 criteria
    Wave 0b·A — Plan-Gate reinforcement (Plan & Design — READY FOR REVIEW) #4 (generated scaffold type-checks against the pinned Aspire package) and Wave 0b·B — .agents/docs + skills cluster #5 (live aspire describe --include-hidden
    shows the <app>-browser-logs child) map to the release-gate class (scaffold.runtime / e2e-cli-prod) and to the
    is required by the gate matrix for Aspire-helper-generation changes. It was not run in the implementation lane —
    the deferral is owner-authorized and recorded (plan.md open-decision sweep "safe to defer", drift.md process
    override, PR Def-of-Done box left unchecked). The PR is currently status:impl-eval and merge is BLOCKED, so this
    is an honest pending item, not a false-done. Required action: run the release-gate class / runtime smoke and
    satisfy issue criteria Wave 0b·A — Plan-Gate reinforcement (Plan & Design — READY FOR REVIEW) #4Wave 0b·B — .agents/docs + skills cluster #5 before any status:ready-merge transition.

  2. Non-blocking test-coverage gap. The safety-boundary negative guard only pins the endpoint-less task
    (UNPINNED_TASK_APP). Endpoint-bearing non-app types (tauri TAURI_APP, task TASK_APP) correctly do NOT emit
    withBrowserLogs (guard is type==='app'), but no test asserts that absence. Optional hardening: extend the
    "health probe for every endpoint-bearing executable" test to also assert !output.includes('withBrowserLogs').

  3. Verified — no finding. All other required gates (static F-19 wrappers, focused + config package tests, quality
    gate per worklog) pass; lock hygiene clean; no unrecorded debt.

Responses to review comments

None outstanding. The owner's resync + slice review (5270237107) confirmed both requested cautions (API awaitability
on the exact pinned type; stale-test reconciliation preserving a negative home). No review threads are unresolved
(0 PR review comments).

Remaining risks

  • Live runtime evidence (Aspire describe / scaffold type-check) is unverified at this head; it must gate ready-merge.
  • Api-probe scratch (/tmp/api-probe-1577) is not present in this checkout; awaitability rests on recorded research
    evidence and the unit-level pin test, to be confirmed by the runtime gate.

OPENHANDS_VERDICT: PASS

Run: https://github.com/rickylabs/netscript/actions/runs/31623332508

@rickylabs

Copy link
Copy Markdown
Owner Author

OPENHANDS_VERDICT: PASS

IMPL-EVAL — PR #1607 · fix(aspire): emit withBrowserLogs for endpoint-bearing generated app resources

Evaluated head 966bed5dd (merge of origin/main 6619603 into fix/1577-aspire-browser-logs) with the harness IMPL-EVAL protocol. Implementation matches the approved plan.

Findings (severity-ranked)

  1. Pending runtime gate — orchestrator-owned; does not block IMPL-EVAL, but blocks ready-merge. Issue fix(aspire): restore default browser-log child for generated executable apps #1577 criteria Wave 0b·A — Plan-Gate reinforcement (Plan & Design — READY FOR REVIEW) #4 (generated scaffold type-checks against the pinned Aspire package) and Wave 0b·B — .agents/docs + skills cluster #5 (live aspire describe --include-hidden shows the <app>-browser-logs child) map to the release-gate class (scaffold.runtime / e2e-cli-prod), required by the archetype gate matrix for Aspire-helper-generation changes. Not run in this lane — deferral is owner-authorized and recorded (plan.md sweep "safe to defer", drift.md override, PR Def-of-Done box unchecked). PR is status:impl-eval, merge BLOCKED → honest pending item, no false-done. Required action: run the release-gate class / runtime smoke and close criteria Wave 0b·A — Plan-Gate reinforcement (Plan & Design — READY FOR REVIEW) #4Wave 0b·B — .agents/docs + skills cluster #5 before status:ready-merge.

  2. Non-blocking coverage gap. The safety-boundary negative guard pins only the endpoint-less task. Endpoint-bearing non-app types (tauri TAURI_APP Port 8998, task TASK_APP Port 8997) correctly do NOT emit withBrowserLogs (guard is type === 'app'), but no test asserts that absence. Optional: assert !output.includes('withBrowserLogs') there too.

What was verified

  • await <app>.withBrowserLogs() emitted after withHttpEndpoint, before the readiness probe, only for Type:'app' + endpoint-bearing entries (guard at lines 49/108/124; emission line 117).
  • Stale MINIMAL_APP absence assertion correctly converted to positive + endpoint-before-browser-log ordering evidence; endpoint-less task negative extended to forbid withBrowserLogs.
  • Independent gates: focused generator tests 2 passed (42 steps) 0 failed; config pin test passes (Aspire.Hosting.Browsers 13.4.6-preview.1.26319.6); scoped check/lint/fmt on packages/cli/src/kernel/templates/aspire all exit 0.
  • Lock hygiene: deno.lock unchanged; working tree clean; only intended source files touched.
  • Process: PLAN-EVAL N/A justified; full design checkpoint; owner slice-review gate (comment 5270237107) reconfirmed API awaitability and the stale-test reconciliation; commit trail 8797596bb + merge 966bed5dd.

No unresolved review threads. Remaining risk is purely the orchestrator-owned runtime smoke above.

This comment was created by an AI agent (OpenHands) on behalf of rickylabs.

@rickylabs rickylabs added status:augment-review Advisory augment/review pass and removed status:impl-eval labels Aug 12, 2026
@rickylabs

Copy link
Copy Markdown
Owner Author

[PHASE: IMPL] orchestrator-owned runtime gate — evidence attached

Ran the serialized scaffold.runtime smoke once from this leaf at head 966bed5dd. No evaluation was re-triggered; the formal PASS on this exact head stands.

Suite verdict

Summary: passed=79 failed=1 skipped=0

The three gates this issue turns on all passed:

Gate Result
runtime.aspire-restore (Restore Aspire TypeScript SDK — the pinned integration) PASSED 9071ms
generated.deno-check (Run the generated workspace type-check task) PASSED 6080ms
runtime.aspire-describe (Describe generated topology) PASSED 184ms
cleanup.aspire-stop PASSED 900ms

The single failure is infrastructure, recorded as such rather than as a product result. behavior.app-reference failed inside headless Chrome with WSL (1115922) ERROR: UtilAcceptVsock:273: accept4 failed 110 — a WSL vsock transport timeout in the browser probe, on a gate unrelated to Aspire helper generation. Not rerun.

The <app>-browser-logs topology proof

runtime.aspire-describe passing is not sufficient on its own — the suite's describe calls do not pass --include-hidden (capture-db-endpoint-allocation.ts:9, validate-aspire-task-traces.ts:51), and a browser-logs child is a hidden resource. So I obtained the proof directly against a live AppHost started from the generated workspace this run produced:

{
  "name": "plugin-smoke-20260812-194940-web-browser-logs",
  "displayName": "plugin-smoke-20260812-194940-web-browser-logs",
  "resourceType": "BrowserLogs",
  "state": "NotStarted",
  "source": "plugin-smoke-20260812-194940-web",
  "dashboardUrl": "https://localhost:43001/?resource=plugin-smoke-20260812-194940-web-browser-logs"
}

From aspire describe --apphost aspire/apphost.mts --include-hidden --format Json against the running AppHost (40233-byte response). The child exists, its resourceType is BrowserLogs, and its source is the generated frontend app — which is precisely the node #1577 reported as missing.

The gating boundary, proven in real generated output

The generated aspire/.helpers/register-apps.mts from that same run contains exactly one emission:

    // HTTP endpoint
    await plugin_smoke_20260812_194940_web.withHttpEndpoint({ env: 'PORT' });
    await plugin_smoke_20260812_194940_web.withBrowserLogs();

and the readiness-dead-port executable in the same file — which also has an HTTP endpoint but is not Type: 'app' — correctly did not receive it. That is the negative case demonstrated in a real artifact rather than asserted, and it matches the source predicate at generate-register-apps.ts:117, where the emission is nested inside both needsHttpEndpoint(type, entry) and type === 'app'.

I agree the endpoint-bearing non-app assertion is optional: the nested predicates make that combination structurally unreachable, and the generated artifact above already exercises it. Not churning the head for it.

Resource hygiene

The AppHost I started was stopped, and agentic:leak-check then reported one surviving container proven owned by path containment (mountSource under this worktree). Removed via agentic:teardown --apply; re-verified gone. aspire stop reporting success was not treated as proof.

Advancing to status:ready-merge on the unchanged head 966bed5dd.

@rickylabs rickylabs removed the status:augment-review Advisory augment/review pass label Aug 12, 2026
@rickylabs
rickylabs merged commit 1f9efb4 into main Aug 12, 2026
51 of 55 checks passed
@rickylabs
rickylabs deleted the fix/1577-aspire-browser-logs branch August 12, 2026 18:06
@github-actions github-actions Bot added the canary:0.0.6-canary.5 Published NetScript prerelease 0.0.6-canary.5 label Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(aspire): restore default browser-log child for generated executable apps

1 participant