Skip to content

v1.91.146.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) - #3272

Merged
capy-ai[bot] merged 7 commits into
mainfrom
capy/merge-trains-with-pattern
Oct 19, 2026
Merged

capy-ai[bot] merged 7 commits into
mainfrom
capy/merge-trains-with-pattern

Conversation

@garrytan

Copy link
Copy Markdown
Owner

Lane T1 of the gate-cost-4x plan (GSTA-49, plan revision 6, approved by /autoplan gate rev 3). It adds merge trains to gstack-ship-queue: a train is a few READY PRs gated together by one full paid gate on the exact tree the queue will produce, instead of one full gate per PR. Everything new is off unless the base revision's .gstack/ship-policy.json declares a train block. Gstack's own policy does not declare one yet, because the workflow side (lane T1b, evals.yml) comes next.

Commands

  • train build --prs <n>@<sha>,…
    • Refuses: fork PRs, moved heads, and trees whose free tests are red, missing, stale or cancelled. Before any paid gate it dispatches and waits for free-tests.yml on train/<nonce>.
    • On a conflict: prints which PR stopped the train, what had already merged, and the exact rebuild command.
    • Ordering: screened PRs first; when 5 or more unscreened PRs are waiting, they form their own train.
    • Bookkeeping: records the pinned heads and every prefix tree modulo stamps before it dispatches through eval-bg's nonce.
  • train status never spends anything. It reconciles a dispatch whose outcome is unknown by its nonce, and rebuilds local state from refs/notes/gstack-trains.
  • train verdict maps every class in the paid report. It returns green only for a complete census in the run's own plan manifest. It reads the green-train record that the train's own workflow run publishes; a user token cannot create check runs.
  • train bisect --spend-cap
    • It runs only the red cases, over prefixes that must be ancestors of the train head.
    • It reserves every case×prefix trial before it dispatches, and a resume launches only the prefixes that have no result yet.
    • Attribution is by pattern:
      • ATTRIBUTED only when the pattern is monotone (green before the PR, red from it on).
      • RED_ON_MAIN only when every prefix is red.
      • Anything else is UNATTRIBUTED.
  • train rebuild is Garry's 2026-10-18 rule: one unchanged rebuild, and only after an all-green bisect. Every other case is refused with its reason.
  • verify accepts a green train record when main's tree after each merge equals the recorded prefix tree. On a mismatch, or when publication is interrupted, it freezes the queue. While frozen it admits one repair PR as a one-PR train, and it clears once main carries a green record.
  • ready --screen | --no-screen [--broad] must state its choice when trains are on, and it prints the recommendation from Garry's rule. Dollar figures are left out until the T0 cost library (v1.91.137.0 feat(evals): gate-cost replay and merge-train cost model (GSTA-49 T0) #3271, now merged) is wired in, in a follow-up.

Results and records. Every command ends with the canonical GSTACK_RESULT: skill=ship-queue … line, preceded by a NEXT: line. The 18 new codes live in lib/result-codes-release.ts, each with a docs/troubleshooting.md anchor. Records are versioned (schema: 1) under the state root and mirrored to the notes ref.

eval-bg. The nonce dedupe key now carries the dispatch's purpose (screen, train, bisect or backstop) and, for screens, the screen policy. A broad screen therefore never adopts a narrow run, and plain lane keys are unchanged. The dispatch, list and await helpers are factored out unchanged. Run lookup now matches the run name's closing bracket exactly.

Test change

test/ship-policy-compat.test.ts: the fail-closed assertion for an unknown policy key named SHIP_QUEUE_KEYS_VERSION as the newest key version. Adding train made that constant stale. The assertion now computes the newest version in POLICY_KEY_INTRODUCED itself, so T2's pr_screen key needs no further edit. It is as strict as before: an unknown key is still POLICY_INVALID, and the message must still name the newest version this gstack reads.

Deviations from the plan

  • Rebuild-once is defined in the train module (REBUILD_ONCE = 1, beside INFRA_REDISPATCH = EVAL_POLICY.infraRedispatch). The EVAL_POLICY entry lands separately.
  • TRAIN_KEYS_VERSION is set to this PR's release slot at stamp time.

Behavior change

None without a train block: ready --pr <n> prints exactly today's block. With screen flags but no train block, it adds one stderr warning that they are ignored. Both paths are tested.

Gate-integrity edits

The merge authority moves from per-PR gates to the train verdict, but only where a policy declares train, so nothing changes for gstack until T1b lands. No threshold, assertion, trial count or case kind changes.

Checks

  • Static checks: bun run typecheck is clean; typecheck:test reports no new diagnostics.
  • New tests: 39 in test/ship-queue-train.test.ts and test/ship-queue-train-logic.test.ts. They run headless fixture repos through the real bin and cover:
    • build: conflict, fork and moved-head refusals, red free tests;
    • bisect patterns, the spend cap, and resume;
    • rebuild-once refusals;
    • tree mismatch, freeze, repair and clear;
    • rebuilding state from a clean state root;
    • the policy gate when it is off.
  • Adjacent tests: ship-policy compat, module-size ratchet, spawnSync tripwire, troubleshooting anchors, state-root ratchet, doctor-components, and 20 adjacent files.
  • Full free suite on Ubicloud on the frozen tree: 19,767 tests, 0 failed.

Open in Capy

…erify (GSTA-49 T1)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@trunk-io

trunk-io Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

😎 This pull request was merged.

garrytan and others added 2 commits October 18, 2026 18:04
…h-pattern

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	test/fixtures/module-size-ratchet.json
…set (gh shim not resolved on windows-latest)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

E2E Evals: ✅ PASS

[test:paid] VERDICT GREEN — lane gate/pr, attempt 1
  rule 108/108 · behavior 0/0 · judge 24/24 · quarantined 0 (0 failing the lane)
  SKIPPED 0 · INFRA 0 · INCOMPLETE 0 · unattributed 0 · ACTION REQUIRED 0
  wall 6m09s · cost $33.02 known + 54 of 132 trial(s) cost unknown

121 executed, 0 reused rule/judge records | 0 manual accepted (unscored; no score-cache credit) | $33.02 rule/judge cost | reconcile exit: 0

Profile: pr / mode dependents; selected behaviors: 108, judges: 24. Deferred to scheduled/release coverage: 139 behaviors and 0 changed prompt files. Deferred checks did not run and receive no PR-pass credit.

Derived dependents (scripts/pr-dependencies.ts):

  • bin/gstack-doctor-components.sh: 246 case(s), 24 judge(s)
  • bin/gstack-ship-queue: 246 case(s), 24 judge(s)
  • lib/result-codes-release.ts: 247 case(s), 24 judge(s)
  • lib/ship-policy.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-gate.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-cli.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-github.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-ready.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-record.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-verdict.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train-verify.ts: 246 case(s), 24 judge(s)
  • lib/ship-queue-train.ts: 246 case(s), 24 judge(s)
  • scripts/lib/windows-curation.ts: 246 case(s), 24 judge(s)
  • test/helpers/ship-queue-train-fixture.ts: 246 case(s), 24 judge(s)
Rule and judge shards
Shard Automated result Manual/unscored Executed Reused Status Cost
e2e-deploy/skill-e2e-deploy--setup-deploy-workflow 1/1 0 1 0 ✅ $0.14
e2e/skill-e2e-coverage-audit 2/2 0 2 0 ✅ $0.3
llm-judge/skill-llm-eval 24/24 0 24 0 ✅ $1.44
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-current 1/1 0 1 0 ✅ $0.96
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-store 1/1 0 1 0 ✅ $1.06
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-completion 1/1 0 1 0 ✅ $1.19
e2e-qa-callers/skill-e2e-qa-callers--ship-exploratory-plan-checks 1/1 0 1 0 ✅ $0.83
e2e/skill-e2e-test-value 3/3 0 3 0 ✅ $0.71
e2e-qa-callers/skill-e2e-qa-callers--review-exploratory-small-cli 1/1 0 1 0 ✅ $0.73
e2e-qa-workflow/skill-e2e-qa-workflow--qa-only-no-fix 1/1 0 1 0 ✅ $0.54
e2e/skill-e2e-shared-libs--shared-libs-read-only 1/1 0 1 0 ✅ $0.24
e2e-docsync-spawned/skill-e2e-docsync-spawned 1/1 0 1 0 ✅ $0.6
e2e-deploy/skill-e2e-deploy--land-and-deploy-first-run 1/1 0 1 0 ✅ $0.29
e2e-deploy/skill-e2e-deploy--land-and-deploy-review-gate 1/1 0 1 0 ✅ $0.3
e2e-review-army/skill-e2e-review-army--review-army-migration-safety 1/1 0 1 0 ✅ $0.29
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-stale-after 1/1 0 1 0 ✅ $0.33
e2e-learnings/skill-e2e-learnings 1/1 0 1 0 ✅ $0.07
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-late-result 1/1 0 1 0 ✅ $0.33
e2e-review-army/skill-e2e-review-army--review-army-json-findings 1/1 0 1 0 ✅ $0.07
e2e-deploy/skill-e2e-deploy--benchmark-workflow 1/1 0 1 0 ✅ $0.22
e2e/skill-e2e-investigate-owned-completion 1/1 0 1 0 ✅ $0.07
e2e/skill-e2e-shared-libs-paths--shared-libs-review-index-flags 1/1 0 1 0 ✅ $0.99
e2e-qa-callers/skill-e2e-qa-callers--ship-exploratory-small-cli 1/1 0 1 0 ✅ $0.76
e2e-review-army/skill-e2e-review-army--review-army-perf-n-plus-one 1/1 0 1 0 ✅ $0.35
e2e-triage/skill-e2e-triage 1/1 0 1 0 ✅ $0.11
e2e-qa-workflow/skill-e2e-qa-workflow--qa-bootstrap 1/1 0 1 0 ✅ $0.08
e2e-third-party-actions/skill-e2e-third-party-actions 5/5 0 5 0 ✅ $0.43
e2e-design/skill-e2e-design--plan-design-review-no-ui-scope 1/1 0 1 0 ✅ $0.07
e2e-deploy/skill-e2e-deploy--canary-workflow 1/1 0 1 0 ✅ $0.11
e2e/skill-e2e-investigate-owned-termination 2/2 0 2 0 ✅ $0.18
e2e/skill-e2e-ship-hook-consent 2/2 0 2 0 ✅ $0.25
e2e-review-army/skill-e2e-review-army--review-army-delivery-audit 1/1 0 1 0 ✅ $0.13
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-timeout-unsettled 1/1 0 1 0 ✅ $0.17
e2e-session-intelligence/skill-e2e-session-intelligence 4/4 0 4 0 ✅ $0.27
e2e-design/skill-e2e-design--design-review-detector-shim 1/1 0 1 0 ✅ $0.14
e2e-plan-tune/skill-e2e-plan-tune 1/1 0 1 0 ✅ $0.13
e2e-safety-pair-agent/skill-e2e-safety-pair-agent 1/1 0 1 0 ✅ $0.1
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-launch-failure 1/1 0 1 0 ✅ $0.15
e2e/skill-e2e-ship-hook-refresh 1/1 0 1 0 ✅ $0.1
e2e-qa-callers/skill-e2e-qa-callers--ship-exploratory-unavailable 1/1 0 1 0 ✅ $0.75
e2e/skill-e2e-plan--plan-review-report 1/1 0 1 0 ✅ $1.26
e2e-retro/skill-e2e-retro 1/1 0 1 0 ✅ $0.35
e2e-qa-functional-fix/skill-e2e-qa-functional-fix 2/2 0 2 0 ✅ $0.99
e2e/skill-e2e-ship-skip 1/1 0 1 0 ✅ $0.23
e2e-qa-workflow/skill-e2e-qa-workflow--qa-quick 1/1 0 1 0 ✅ $0.66
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-stale-before 1/1 0 1 0 ✅ $0.34
e2e-review-attribution/skill-e2e-review-attribution 3/3 0 3 0 ✅ $0.29
e2e/skill-e2e-shared-libs-paths--shared-libs-review-prior-coverage 1/1 0 1 0 ✅ $0.93
e2e/skill-e2e-shared-libs--shared-libs-review-lifecycle 1/1 0 1 0 ✅ $0.85
e2e-safety-codex-consult/skill-e2e-safety-codex-consult 1/1 0 1 0 ✅ $0.27
e2e-deploy/skill-e2e-deploy--land-and-deploy-workflow 1/1 0 1 0 ✅ $0.63
e2e/skill-e2e-workflow 4/4 0 4 0 ✅ $1.05
e2e-review-army/skill-e2e-review-army--review-army-quality-score 1/1 0 1 0 ✅ $0.1
e2e-hermetic/skill-e2e-hermetic-canary 2/2 0 2 0 ✅ $0.02
e2e-browse/skill-e2e-bws 6/6 0 6 0 ✅ $0.29
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-missing-asset 1/1 0 1 0 ✅ $0.4
e2e-design/skill-e2e-design--design-review-plugin-handoff 1/1 0 1 0 ✅ $0.14
e2e-design/skill-e2e-design--design-review-detector-shim-dom 1/1 0 1 0 ✅ $0.23
e2e-safety-ship-evidence/skill-e2e-safety-ship-evidence 1/1 0 1 0 ✅ $0.12
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-missing-marker 1/1 0 1 0 ✅ $0.16
e2e/skill-e2e-shared-libs--shared-libs-unsupported-git 1/1 0 1 0 ✅ $0.17
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-recovery 1/1 0 1 0 ✅ $0.33
e2e-review/skill-e2e-review 2/2 0 2 0 ✅ $0.34
e2e/skill-e2e-shared-libs--shared-libs-review-revalidation 1/1 0 1 0 ✅ $1.78
e2e-qa-functional/skill-e2e-qa-functional 2/2 0 2 0 ✅ $0.82
e2e-cso/skill-e2e-cso 1/1 0 1 0 ✅ $0.39
e2e-qa-callers/skill-e2e-qa-callers--ship-exploratory-late-input 1/1 0 1 0 ✅ $0.92
e2e-skillify/skill-e2e-skillify 3/3 0 3 0 ✅ $0.83
e2e-ship-docsync/skill-e2e-ship-docsync--ship-docsync-failure 1/1 0 1 0 ✅ $0.73
e2e/skill-e2e-shared-libs-paths--shared-libs-review-path-eligibility 1/1 0 1 0 ✅ $1.42
Fail-closed reconciliation
ill-e2e-safety-ship-evidence.test.ts
  slice 13  passed             50s  test/skill-e2e-design.test.ts#design-review-detector-shim-dom
  slice 13  passed             53s  test/skill-e2e-shared-libs.test.ts#shared-libs-unsupported-git
  slice 13  passed             33s  test/skill-e2e-office-hours-auto-mode.test.ts
  slice 13  passed             32s  test/skill-e2e-design.test.ts#design-review-plugin-handoff
  slice 13  passed             47s  test/skill-e2e-bws.test.ts
  slice 13  passed             29s  test/skill-e2e-ship-docsync.test.ts#ship-docsync-missing-marker
  slice 13  passed             23s  test/skill-e2e-review-army.test.ts#review-army-quality-score
  slice 13  passed              5s  test/skill-e2e-hermetic-canary.test.ts
  slice 14  passed             50s  test/skill-e2e-third-party-actions.test.ts
  slice 14  passed             24s  test/skill-e2e-plan-tune.test.ts
  slice 14  passed             21s  test/skill-e2e-triage.test.ts
  slice 14  passed             34s  test/skill-e2e-ship-docsync.test.ts#ship-docsync-timeout-unsettled
  slice 14  passed             35s  test/skill-e2e-ask-user-question-format-compliance.test.ts
  slice 14  passed             24s  test/skill-e2e-ship-docsync.test.ts#ship-docsync-launch-failure
  slice 14  passed             26s  test/skill-e2e-session-intelligence.test.ts
  slice 14  passed             33s  test/skill-e2e-ship-hook-consent.test.ts
  slice 14  passed             25s  test/skill-e2e-investigate-owned-termination.test.ts
  slice 14  passed             26s  test/skill-e2e-design.test.ts#design-review-detector-shim
  slice 14  passed             27s  test/skill-e2e-deploy.test.ts#canary-workflow
  slice 14  passed             28s  test/skill-e2e-ship-hook-refresh.test.ts
  slice 14  passed             18s  test/skill-e2e-design.test.ts#plan-design-review-no-ui-scope
  slice 14  passed             24s  test/skill-e2e-review-army.test.ts#review-army-delivery-audit
  slice 14  passed             21s  test/skill-e2e-safety-pair-agent.test.ts
  slice 14  passed             23s  test/skill-e2e-qa-workflow.test.ts#qa-bootstrap
  slice 15  passed             13s  test/skill-e2e-learnings.test.ts
  slice 15  passed             16s  test/skill-e2e-diagram.test.ts
[test:paid] collector final outcomes: 121 executed, 0 reused; 121 passed, 0 failed, 0 manual accepted (unscored; no score-cache credit) (121 attempt records from 70 collectors; every record counts)
[test:paid] deselected testcases, zero credit (312):
[test:paid]    298  sibling case of a case shard (its own shard runs it)
[test:paid]      8  periodic-tier case (this lane runs gate)
[test:paid]      6  Bun hook placeholder of a skipped describe block (not a test)
[test:paid] VERDICT GREEN — lane gate/pr, attempt 1
  rule 108/108 · behavior 0/0 · judge 24/24 · quarantined 0 (0 failing the lane)
  SKIPPED 0 · INFRA 0 · INCOMPLETE 0 · unattributed 0 · ACTION REQUIRED 0
  wall 6m09s · cost $33.02 known + 54 of 132 trial(s) cost unknown
[test:paid] exercised-path map (shadow, non-blocking): 5 MAP_GAP line(s) over 101 executed case(s) with recorded commands
  MAP_GAP document-release bin/gstack-banned-terms: non-blocking for this PR; the next census refresh widens the map (docs/TESTING_INTERNALS.md#exercised-path-map)
  MAP_GAP plan-review-report bin/gstack-plan-reality: non-blocking for this PR; the next census refresh widens the map (docs/TESTING_INTERNALS.md#exercised-path-map)
  MAP_GAP plan-review-report browse/dist/browse: non-blocking for this PR; the next census refresh widens the map (docs/TESTING_INTERNALS.md#exercised-path-map)
  MAP_GAP plan-review-report design/dist/design: non-blocking for this PR; the next census refresh widens the map (docs/TESTING_INTERNALS.md#exercised-path-map)
  MAP_GAP test-audit-report-only design/dist/design: non-blocking for this PR; the next census refresh widens the map (docs/TESTING_INTERNALS.md#exercised-path-map)
[test:paid] report: every planned shard accounted and passed

Sliced lane: planner → duration-packed executors → fail-closed report. Behavior cases run a pre-registered 3-trial panel (PASS at 2/3 with no contract violation); a PASS 2/3 is shown with its failed trial, never as a clean pass. Reused results retain their original provenance and expiry.

garrytan and others added 2 commits October 19, 2026 07:27
…ain-record verify (GSTA-49 T1)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@github-actions github-actions Bot changed the title feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) v1.91.145.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) Oct 19, 2026
garrytan added a commit that referenced this pull request Oct 19, 2026
…er cleanup, like closetab

PR #3272's Opus gate 39217387697 (slice 8) failed qa-only-no-fix when the agent
stopped the browser inside the guard; the rule counted stop as a third probe
that needed a checkpoint. The prompt now names both cleanup children. A free
replay of the captured three guarded calls passes (and fails on the old rule);
stop before a probe, a disguised probe, stop --all and stop outside the guard
still fail.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@github-actions github-actions Bot changed the title v1.91.145.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) v1.91.146.0 feat(ship-queue): merge trains with pattern bisect and train-record verify (GSTA-49 T1) Oct 19, 2026
capy-ai Bot added a commit that referenced this pull request Oct 19, 2026
…err-to-null forms accepted (#3295)

* fix(evals): qa-only opts in to the read-only grammar; stderr-to-null forms accepted

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(evals): qa-only checkpoint rule counts guarded "$B" stop as browser cleanup, like closetab

PR #3272's Opus gate 39217387697 (slice 8) failed qa-only-no-fix when the agent
stopped the browser inside the guard; the rule counted stop as a third probe
that needed a checkpoint. The prompt now names both cleanup children. A free
replay of the captured three guarded calls passes (and fails on the old rule);
stop before a probe, a disguised probe, stop --all and stop outside the guard
still fail.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(evals): register the qa-only stop-cleanup replay fixture in qa-only-no-fix touchfiles

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(evals): docs-sync observer ignores an unexecuted candidate call that exited 127 at a missing path

PR #3295's gate (run 39223031058, slice 2) failed ship-docsync-current after Opus
called gstack-docs-candidate compare at a path missing the fixture-home segment,
got exit 127 (no such file or directory), and reran it at the declared path. The
observer now ignores such a call only when the absolute binary path does not
exist, the recorded result is exactly the shell's exit-127 not-found message for
that path, and a later call runs the same command at the declared path without
failing. A free replay of all 43 captured tool calls passes and fails on the
previous observer; negative controls cover an existing wrong-path binary, other
exits and messages, a missing/earlier/failing/changed retry, and non-candidate or
relative binaries. GStack Minder-approved.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* v1.91.145.0 chore(release): stamp qa-only read-only opt-in, stderr forms, stop cleanup and docs-sync exit-127 exception

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…h-pattern

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	agents-digest/gstack-AGENTS.md
#	package.json
@capy-ai
capy-ai Bot merged commit 6a4ab69 into main Oct 19, 2026
70 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant