Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
11 KiB
11 KiB
the iteration loop)
- I treat this as a deterministic “one fixture at a time” pipeline: import → isolate → trigger → wait → assert → cleanup → record artifacts → commit.
- I proceed alphabetically through ee/test-data/ workflow-harness/ so we always know exactly where we are and what’s next, and so failures are reproducible and easy to bisect (fixture ordering is stable in ls | sort).
Step-by-step: what happens on every single run
- Select fixture folder
- Each fixture is a directory under ee/test-data/
workflow-harness/ containing:
- bundle.json (Workflow Bundle V1 with a deterministic workflow.key like fixture.)
- test.cjs (CommonJS script that triggers and asserts)
- Conventions are documented in ee/test-data/ workflow-harness/README.md:1.
- Run the harness (CLI entrypoint)
- The harness entrypoint is tools/workflow-harness/ run.cjs:1.
- The harness parses args, validates paths, and loads bundle.json + test.cjs (see usage + flag docs at tools/workflow-harness/run.cjs:17 and tools/workflow-harness/README.md:1).
- Import workflow definition into the running server
- The harness posts bundle.json to the server import endpoint via tools/workflow-harness/lib/ workflow.cjs:1.
- We almost always pass --force so the same fixture can be rerun repeatedly without manual cleanup (documented at tools/workflow-harness/ README.md:12 and reinforced in the plan’s decisions at ee/docs/plans/2026-01-26-workflow- harness-fixture-suite/SCRATCHPAD.md:1).
- Isolate the fixture (avoid cross-fixture fanout)
- To keep results deterministic, the harness “turns
off” other fixtures:
- It sets is_paused=true for all workflow_definitions whose key matches fixture.%, except the current workflow key.
- This behavior is implemented in tools/ workflow-harness/run.cjs:150.
- Then it sets the current fixture’s pause state to
match the bundle’s metadata.isPaused:
- This is critical for fixtures like runtime- paused-no-run that must remain paused.
- This behavior is implemented in tools/ workflow-harness/run.cjs:175 (and was fixed recently to respect isPaused).
- Create a test context (shared utilities for fixtures)
- The harness builds ctx via tools/workflow- harness/lib/context.cjs:14.
- The context includes:
- ctx.http (HTTP wrapper)
- ctx.db (read-only DB client)
- ctx.dbWrite (write-capable DB client used only for cleanup)
- ctx.onCleanup(fn) hooks and ctx.runCleanup() (reverse-order, always runs, even on failure)
- HTTP behavior is standardized
- ctx.http is created by tools/workflow-harness/
lib/http.cjs:22 and automatically injects:
- Cookie (session) if provided
- x-tenant-id (tenant scoping) if provided
- x-api-key if WORKFLOW_HARNESS_API_KEY (or ALGA_API_KEY) is set and the request didn’t explicitly override it
- This means fixtures can safely do ctx.http.request('/api/workflow/events', ...) without each fixture needing to hand-roll auth headers.
- DB behavior is standardized
- ctx.db and ctx.dbWrite are created by tools/ workflow-harness/lib/db.cjs:12.
- ctx.db is intentionally read-only and will refuse obvious write queries (guardrails in tools/ workflow-harness/lib/db.cjs:5).
- ctx.dbWrite is used only for cleanup when HTTP deletion endpoints fail due to FK constraints (a recurring real-world issue).
- Trigger the workflow
- Most fixtures trigger runtime V2 by posting to / api/workflow/events.
- The fixture is responsible for sending a payload that matches the workflow trigger (eventName + payloadSchemaRef + required payload fields).
- Important practical rule we learned during iteration: if a fixture creates a domain object via /api/v1/..., that does not automatically mean the corresponding workflow event is emitted in the harness context. If the workflow trigger is an event (like PROJECT_CREATED), the fixture must POST the event explicitly unless we know the API emits it.
- Wait for the run
- The harness exposes ctx.waitForRun(...) which polls workflow_runs for the imported workflowId and started_at >= triggerStartedAt.
- The polling logic is in tools/workflow-harness/ lib/runs.cjs:120.
- On timeout, it throws with diagnostics (recent runs + last seen) for fast debugging (tools/ workflow-harness/lib/runs.cjs:150).
- Assert outcomes
- Assertions are “business outcomes”, usually by
querying the DB:
- project tasks created, notifications inserted, interactions/notes written, ticket comments created, etc.
- Assertions use either:
- ctx.expect helpers (tools/workflow-harness/ lib/expect.cjs:17)
- or explicit checks with detailed error messages (common in fixture test.cjs)
- Cleanup always runs
- Whether pass or fail, ctx.runCleanup() runs all registered cleanup hooks in reverse order (tools/ workflow-harness/lib/context.cjs:34).
- If the test fails and cleanup fails, the harness throws a combined error so we don’t miss cleanup regressions (this behavior is in tools/workflow- harness/run.cjs:219).
- Artifacts are written on failure
- Failures generate a timestamped folder under $TMPDIR/workflow-harness//... via tools/ workflow-harness/lib/artifacts.cjs:15.
- The harness writes:
- failure.context.json (includes importSummary, workflowId/key, run/steps/logs if available, plus exported workflow bundle and a step summary)
- failure.error.txt (stack trace + cleanup error section if applicable)
- This logic is in tools/workflow-harness/ run.cjs:262.
- Fix scope decision: fixture bug vs harness bug vs product bug When something fails, I triage it into exactly one of these buckets:
- Fixture bug (most common): wrong event/payload,
wrong assumption about transforms, brittle query,
bad cleanup
- Fix in ee/test-data/workflow-harness/ /bundle.json or test.cjs
- Harness bug: incorrect harness behavior (e.g., it
unpaused workflows that must stay paused)
- Fix in tools/workflow-harness/*
- Product bug: the server/runtime/API is wrong
relative to its contract (e.g., /api/v1/projects
missing required DB fields)
- Fix in server/, shared/, etc.
- Re-run the same fixture until it is green
- I always re-run the same fixture immediately after the fix with --debug if it was non-obvious, so we confirm the root cause is resolved (not masked).
- Commit + push in small, reviewable chunks
- Once a cluster of related fixes is proven by reruns, I commit and push so the branch stays usable and we don’t accumulate an unreviewable pile.
- This also helps testers pull at any time and reproduce the same state.
How we measure iteration time
- We time runs using /usr/bin/time -p wrapped around node tools/workflow-harness/run.cjs ....
- Interpreting timing:
- Fast runs (~0.25–1s) are typically: import + event + DB poll + assertions.
- Slower runs (several seconds) usually mean: the fixture is designed to wait for delayed behavior, or it’s doing heavier domain setup.
- 60s runs are almost always a logic issue: “workflow never triggered” or “waiting for a run that can’t exist”.
Where testers should look for “source of truth” docs
- PRD / scope / decisions / usage examples:
- ee/docs/plans/2026-01-26-workflow-harness- fixture-suite/PRD.md:1
- ee/docs/plans/2026-01-26-workflow-harness- fixture-suite/SCRATCHPAD.md:1
- ee/docs/plans/2026-01-26-workflow-harness- fixture-suite/features.json:1
- ee/docs/plans/2026-01-26-workflow-harness- fixture-suite/tests.json:1
- Harness usage + flags:
- tools/workflow-harness/README.md:1
- tools/workflow-harness/run.cjs:17
- Fixture conventions:
- ee/test-data/workflow-harness/README.md:1
- Harness architecture code map:
- CLI entry: tools/workflow-harness/run.cjs:1
- Context + cleanup: tools/workflow-harness/ lib/context.cjs:14
- HTTP wrapper + auth injection: tools/ workflow-harness/lib/http.cjs:22
- DB client + read-only guard: tools/workflow- harness/lib/db.cjs:12
- Import/export wrapper: tools/workflow- harness/lib/workflow.cjs:1
- Run polling + steps/logs: tools/workflow- harness/lib/runs.cjs:120
- Assertions helper: tools/workflow-harness/ lib/expect.cjs:17
- Cookie file helper: tools/workflow-harness/ lib/cookie.cjs:3
- Failure artifact writing: tools/workflow- harness/lib/artifacts.cjs:15
- Fixture scaffolding tool (for adding more
fixtures consistently):
- tools/workflow-harness/scaffold.cjs:1
Where testers should look when a fixture fails
- The harness prints the artifacts directory on
failure (example shape):
- $TMPDIR/workflow-harness// /
- Key files inside that folder:
- failure.error.txt (exception + stack + cleanup error if any) written by tools/ workflow-harness/run.cjs:331
- failure.context.json (full structured diagnostics) written by tools/workflow- harness/run.cjs:319
- Server-side logs (when needed):
- docker logs -f prep_1_0_server_ee
- If a run appears “stuck” or missing, check workflow-run ingestion endpoints and runtime logs in prep_1_0_server_ee.
How testers can run exactly what I run
- The canonical CLI form is documented in tools/ workflow-harness/README.md:14.
- In our dockerized dev setup, the reliable pattern
is to run inside the server container so Postgres
is reachable and cookie/API key files are
accessible:
- node tools/workflow-harness/run.cjs --test ee/test-data/workflow-harness/ --base-url http://localhost: --tenant --cookie-file --pg-url --force --debug
- If you want to validate the harness itself
without a running server/DB, there are stubbed
tests under:
- tools/workflow-harness/tests/runner- stubbed.test.cjs:1
- (plus other harness tests in tools/workflow- harness/tests/)
Why we sometimes fix “product code” while iterating fixtures
- The fixture suite is intentionally “business- valid”: it’s supposed to exercise real API paths and real DB constraints.
- When a fixture exposes a real contract mismatch (example: project creation missing required fields), the correct fix is to fix the product API/service, not to weaken the fixture. The fixture suite is acting as an integration-level regression net.
When all tests are run and work, ONLY THEN, output DONE.