Fix isolated realm server teardown in software-factory test harness (CS-10472)#4233
Fix isolated realm server teardown in software-factory test harness (CS-10472)#4233
Conversation
Add `bootstrapProjectArtifacts()` that creates Project, KnowledgeArticle, and Ticket cards in a target realm from a normalized brief. Content is derived deterministically from the brief's sections, tags, and summary. Stable slug-based IDs ensure idempotency — rerunning skips existing cards. - New module `src/factory-bootstrap.ts` with full card generation logic - Wired into `runFactoryEntrypoint` after target realm bootstrap - 21 hermetic QUnit tests for artifact generation and idempotency - 3 Playwright specs testing live realm card creation and rendering - E2e subprocess test for full Matrix auth → realm creation → bootstrap flow - CLI entrypoint now calls `process.exit()` to prevent hanging on open handles Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…oject-artifact-bootstrap-from-a-brief
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix activeTicket to reflect actual in-progress ticket instead of always using tickets[0]; hasInProgressTicket now returns the active path - Rename bootstrap summary fields: createdProject → projectId, createdTickets → ticketIds, createdKnowledgeArticles → knowledgeArticleIds - Use actual activeTicket status instead of hardcoded 'in_progress' - Wait for stdout flush before process.exit to prevent truncated output - Add response status checks in matrix-auth test helpers - Clear timeout timer in run-command when child exits - Split e2e test into separate playwright config (pnpm test:playwright-e2e) with shared config extracted to playwright.shared.ts - Remove test.fixme — e2e test runs via dedicated command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After _create-realm succeeds, append the new realm URL to the Matrix user's app.boxel.realms account data. This mirrors what the host app does when creating a realm through the UI — without this step, the realm won't appear in the user's realm list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Set iconURL from first letter of realm name (Letter-{x}.png from boxel CDN)
- Set backgroundURL to a random curated image from boxel CDN
- Mirrors iconURLFor() and getRandomBackgroundURL() from host app
- Format card artifact JSON with 2-space indentation for readability
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The split module pattern (schema in one file, UI side-effects in another) caused custom templates to not render when cards adopted from the public module. The realm server's module loader could resolve the schema without executing the UI side-effects, leaving cards with default edit views. Combining everything into darkfactory.gts ensures the fitted/isolated/ embedded templates are always co-located with their card class definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix flaky "Accept All" code patch test caused by Monaco diff editor race During test teardown, Monaco's WorkerBasedDocumentDiffProvider.computeDiff can receive a null result from the editor worker when models are disposed mid-computation. This caused an unhandled "no diff result available" error that surfaced as a flaky QUnit global failure in CI. Extend the existing Monaco patch to return an empty diff result instead of throwing — matching the pattern Monaco already uses for disposed models. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Narrow Monaco patch to only suppress error when models are disposed Address review feedback: instead of unconditionally returning an empty diff when the worker returns null, only suppress the error when the models are confirmed disposed (the teardown race). Genuine worker failures with live models still throw. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Separate the "manually visit prerendered url" debug message into a dedicated `prerenderer-reproduce` log category so it can be enabled independently of the noisy `prerenderer` logs. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…racking hot path (#4223) * Optimize prerender performance: eliminate URL() construction in dependency tracking hot path Flame chart profiling revealed that 98% of active CPU per frame during card prerendering was spent in the runtime dependency tracking system, primarily constructing URL objects. The render produced 22 identical ~9-second long tasks (one per card deserialization), totaling ~200 seconds of blocked main thread for a card with 23 linksToMany relationships. Three optimizations applied: 1. trimModuleIdentifier (loader.ts): Replace `new URL(id).href` with string slice operations + a Map cache. Module identifiers are already full URL strings, so extension trimming only needs string ops. This was the single largest CPU consumer at 52.8% of active time (~5s per card). 2. collectKnownModuleDependencies (loader.ts): Cache the flattened dependency set per module identifier. Once a module is evaluated its consumedModules never change, so repeated graph walks for the same module return the cached result. This turns O(cards × modules) into O(modules). 3. trackRuntimeRelationshipModuleDependencies (card-api.gts): Track which modules have already had their full dep trees tracked and skip redundant getKnownConsumedModules() calls. This function was called on every linksTo field getter access during rendering, each time walking the full module dependency graph. Additionally, normalizeModuleURL/normalizeInstanceURL/canonicalURL in dependency-tracker.ts now use string operations instead of URL construction, eliminating another hot source of URL() calls in the tracking pipeline. Closes CS-10473 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix cached Set mutation and remove session-scoping issue in dep tracking Address review feedback: - getKnownConsumedModules: filter instead of delete to avoid mutating the cached Set returned by collectKnownModuleDependencies - Remove trackedRelationshipModules skip cache from card-api.gts — it was process-global and not cleared between dependency tracking sessions, which could cause subsequent renders to under-report module deps. The Loader-level caching in collectKnownModuleDependencies already makes getKnownConsumedModules fast enough without a caller-side skip. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Software factory plan refinements * updated with note of orchestrator vs agent calling boxel apis * more refinement * lint
…CS-10472)
Spawn ts-node directly instead of pnpm to eliminate the intermediate
wrapper process that broke process-group killing. Add port-free
verification after stop() and a process.on('exit') safety net in
serve-realm.ts. Consolidate playwright configs back to a single file
now that all specs can run together.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR primarily hardens the packages/software-factory Playwright test harness teardown for isolated realm servers (CS-10472), aiming to prevent orphaned processes and port conflicts between specs, and it simplifies Playwright configuration so all specs run together.
Changes:
- Replace
pnpm serve:*wrapper process spawning with directts-nodeCLI invocation to improve process-group teardown reliability. - Add post-
stop()verification that key ports are released before subsequent tests start. - Consolidate Playwright configs/scripts so all specs run under
pnpm test:playwright.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Updates patch hash for monaco-editor patch contents. |
| patches/monaco-editor@0.52.2.patch | Suppresses Monaco diff worker null-result error when models are disposed mid-computation. |
| packages/software-factory/tests/fixtures.ts | Spawns ts-node directly and adds waitForPortFree() checks after teardown. |
| packages/software-factory/tests/factory-target-realm.spec.ts | Removes CS-10472 workaround comment now that teardown is addressed. |
| packages/software-factory/src/harness.ts | Exposes child process PIDs from the isolated stack for cleanup. |
| packages/software-factory/src/cli/serve-realm.ts | Adds process.on('exit') safety net to SIGKILL child PIDs on unclean exit. |
| packages/software-factory/playwright.shared.ts | Deleted; config consolidated into a single Playwright config. |
| packages/software-factory/playwright.global-setup.ts | Spawns ts-node directly for serve-support. |
| packages/software-factory/playwright.e2e.config.ts | Deleted; e2e tests now run with the main config. |
| packages/software-factory/playwright.config.ts | Single unified config that runs all *.spec.ts together. |
| packages/software-factory/package.json | Removes test:playwright-e2e script. |
| packages/software-factory/docs/software-factory-testing-strategy.md | Updates docs to include a “test realm” and AI-generated test loop. |
| packages/software-factory/docs/one-shot-factory-go-plan.md | Expands docs around test realm + implement→test→iterate loop; adds model flag note. |
| packages/runtime-common/loader.ts | Adds caching for known module dependencies and uses string-based module-id trimming. |
| packages/runtime-common/dependency-tracker.ts | Replaces URL-constructor heavy normalization with string-based normalization. |
| packages/realm-server/prerender/render-runner.ts | Routes reproduce log line to a separate logger name. |
| packages/base/card-api.gts | Updates comment to reflect loader perf improvements. |
| .github/workflows/pr-review-reminder.yml | Adds scheduled workflow to post awaiting-review PRs to Discord. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
packages/software-factory/src/cli/serve-realm.ts:62
- The SIGINT/SIGTERM handlers call the async stop() without handling rejections. If runtime.stop() throws, this becomes an unhandled promise rejection and may leave the process running without exiting (and without triggering the exit cleanup). Consider attaching a .catch() that logs and sets a non-zero exit code, and/or setting cleanExit intent earlier so the exit handler behavior is deterministic.
let stop = async () => {
await runtime.stop();
cleanExit = true;
process.exit(0);
};
process.on('SIGINT', () => void stop());
process.on('SIGTERM', () => void stop());
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Only retry on EADDRINUSE; propagate other errors immediately. Always close the server handle on error to prevent handle leaks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Host Test Results 1 files 1 suites 2h 6m 51s ⏱️ Results for commit 7be0c51. ♻️ This comment has been updated with latest results. |
…rver-teardown # Conflicts: # packages/runtime-common/loader.ts # packages/software-factory/tests/factory-target-realm.spec.ts
Preview deployments |
Summary
ts-nodedirectly instead ofpnpm serve:realmto eliminate the intermediate wrapper process that broke process-group killing during teardownstop()to confirm ports 4205/4201/4232 are released before the next test startsprocess.on('exit')safety net inserve-realm.tsthat force-kills child processes on unclean exitplaywright.config.ts,playwright.e2e.config.ts,playwright.shared.ts) back into a singleplaywright.config.ts— all specs now run together underpnpm test:playwrighttest:playwright-e2eworkaround script frompackage.jsonfactory-target-realm.spec.tsDependencies
Test plan
pnpm test:playwright— all 8 tests pass (darkfactory, factory-bootstrap, factory-target-realm specs running together)ss -tlnpshows no lingering listeners on 4201/4205/4232)pnpm lint:jsandpnpm lint:formatpassRelated
🤖 Generated with Claude Code