Skip to content

refactor(serving): split daemon responsibilities and preserve lifecycle ownership - #5244

Closed
joelteply wants to merge 5 commits into
canaryfrom
codex/serving-daemon-by-owner
Closed

joelteply wants to merge 5 commits into
canaryfrom
codex/serving-daemon-by-owner

Conversation

@joelteply

@joelteply joelteply commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Split serving_daemon by responsibility so the serving lifecycle for persistent residents has smaller reviewable owners (architecture phase 1). The facade is 742 lines; extracted files are at most 798. Public paths, 94 tests, 3,640 original full comment lines and 81 diagnostic targets are retained.

Reconcile decisions pass owned inputs to a synchronous launch method; the same ServingOperation remains owned through the existing asynchronous task. Test source contracts include every extracted production owner and exclude tests. The teacher subprocess selector follows its moved test.

Independent source review APPROVE covers the frozen source hashes and operation/guard lifetime, public visibility, comments and test contracts. Source audit and diff checks pass. Rebased onto canary 8e26442 after #5239; direct source census measures 34,293 excess lines across 10 files. CI is required; no local compilation, deployment, latency or learning claim. Reconcile the shared probe macro and remeasure baseline if #5233 lands first.

CI at the prior head caught test helper/trait visibility errors; corrected and independently reviewed. Current head 3676ae9 also restores AdmissionHold, GateRefusal and PromptCacheGrant definitions to their original facade owner, preserving paths and comments without artificial import usage or warning suppressions. Independent source review approves this delta; exact-head core/Windows checks remain pending. Proposed integration is through Fable's reviewed union batch after green; original PR stays open until landed.

@joelteply

Copy link
Copy Markdown
Contributor Author

Independent serving split source review — 2026-10-21

Verdict: SOURCE APPROVE for the frozen file hashes below, based on 437ee5f. No concrete source blocker found. This is not compilation, merge, deployment, performance or learning proof. CI remains required at the eventual exact head.

Read AGENTS.md plan and repository README/CLAUDE guidance. This is phase 1 / No monolith: serving ownership becomes reviewable in smaller units while persistent residents retain the same lifecycle protection.

Reconcile ownership seam

Reviewed reconcile_to_plan -> ReconcileLaunch -> launch_reconciled_plan and ServingOperation acquire/Drop. Acquire is still before reading the planned/intent snapshots. Early returns in the decision half drop the same operation; no new await or asynchronous handoff exists at the extracted synchronous call. The owned struct immediately destructures into locals, including the operation, and the same async move task captures it with an explicit local binding before any await. Abort-before-first-poll therefore still drops the captured operation; normal return/cancellation drops the local after inner lifecycle work. No resource guard is left in the caller across the extracted call: the watch and pending-model mutex guards end in existing blocks; occupancy is value data. Changed drop ordering of moved snapshots/strings does not release engine authority: they are not lifecycle guards. No cloned operation or early explicit release was introduced.

Equivalence and contracts

Inspected and reran the author's read-only audit script (its prefix defines parser/read helpers only). It reports 288 original / 289 resulting function bodies; exactly three changed original bodies: reconcile, teacher fixture selector, production_source. Recomposing reconcile decision body and launch tail equals the original under its documented normalization. Selector differs only by the inserted teacher module. This script is supporting source evidence, not a Rust parser/typechecker; whitespace normalization alone cannot prove arbitrary string equivalence.

Manually inspected the substantive seams and public facade reexports. Original public and crate-visible lifecycle/intent/demand/cache/catalog interfaces retain parent paths; sibling helpers are pub(super), and ServingDaemonModule remains at its original path. The source-contract fixture now includes the facade and all extracted production files, excludes test files, and preserves its anti-self-matching intent. Unchanged academy_batch/footprint remain outside its historical source scope. The public tests::exercise_cancelled_exclusive_teacher entry remains intact. Teacher --exact selector matches the new test module.

Audit reproduced: all 3,640 original full comment lines retained; 94 test annotations retained; 81 diagnostic calls retained with explicit original serving target on moved calls. The shared probe macro target arm preserves INFO and probe_class and leaves the prior arm intact, matching the previously reviewed llama split arm. git diff --check passes (only Git's CRLF normalization warning). No local compiler or runtime operation was run, and candidate files were not edited.

Reuse assessment: the change extracts existing responsibilities behind the existing module facade and one owned launch input; it introduces no second serving authority or copied policy algorithm. The existing diagnostic macro is extended once for target preservation. CI must cover full core tests, Windows test compilation, warning/hygiene gates and execution of the teacher subprocess fixture.

Exact reviewed bytes (SHA256)

Path SHA256
core/continuum-core/src/modules/serving_daemon.rs 7296362b340e532225adb75eee27c601911648d01bad237471a998c41bfc5370
core/continuum-core/src/modules/serving_daemon/budget.rs ae5a08fe87e3b160dac7324cadf32420a3f7018494deb1b983a26e09d128bb34
core/continuum-core/src/modules/serving_daemon/catalog.rs a91e5f110caa24b76f09e62ad7ffa8db5012a3396afe582dd258a0a50ffcdd61
core/continuum-core/src/modules/serving_daemon/consumer.rs bce9c291ba01ab541e069d3fc45c3f90ee251810f598f186b373fefa217c9fef
core/continuum-core/src/modules/serving_daemon/demand.rs f4f9dc69a7c08e6bf40d59d7a70b371bdabb7c14a35a8a3c1d933b2e35f06ce8
core/continuum-core/src/modules/serving_daemon/device_budget.rs b61087bd1b1f5e0f523fca038d5700d37f1bdf55ba077ccb17dbbc63392abaa4
core/continuum-core/src/modules/serving_daemon/health.rs 393850664ecedf8f2f5e360a0f0552b2395b9c60d01413e42a4cc45c3d251568
core/continuum-core/src/modules/serving_daemon/holds.rs 7f7aa2b9f179085889fbcb9b03314ffe4b5777b4758a9ce6020cab10c95e438c
core/continuum-core/src/modules/serving_daemon/intent.rs 053aa0e8bc9b54859cf22ef56ad3520d05c115c8a182454ba3de63b78cd0e6ac
core/continuum-core/src/modules/serving_daemon/launch_window.rs cdc129335ac2692b05035e7125ee55322abf085e6504115a12c9e1f37bd5f233
core/continuum-core/src/modules/serving_daemon/launch.rs 9bb45560d524ab1b265f14707ca007f8917bfcb317e8ea6aade1e833c7b156c5
core/continuum-core/src/modules/serving_daemon/lifecycle.rs 2080225c66edf4479f8e0094af860b4b8d4371377234cab1fef178a872999532
core/continuum-core/src/modules/serving_daemon/moe_cache.rs 77529f7479690fda262f65f3796f0883ed8d6171be9df6c85f38fb42846b9fb2
core/continuum-core/src/modules/serving_daemon/placement.rs 759b89f6fee7b3aa7f450c1c371bcc6cfdda148ee4d9482f3f260269c2138f0c
core/continuum-core/src/modules/serving_daemon/planning.rs 30161f893ced26eeeb1e264c2ae606fbbf7652a5404bdad3a11a6b698ac7722a
core/continuum-core/src/modules/serving_daemon/policy.rs 448dfdc1dee147689e7c1080ab5a3fc99d5accdbbf8a81458adcd78c851624f4
core/continuum-core/src/modules/serving_daemon/prompt_cache.rs a4fc52a2bc77ec3a9405c1e520da4311b94cde9b0d9456631ccc14ba0d660356
core/continuum-core/src/modules/serving_daemon/publication.rs f7be3a5068fc9da5b15e4ca08dcd7716b7ae3a6a9237cfe51681cff42e37d13b
core/continuum-core/src/modules/serving_daemon/reconcile.rs 94316f641415d218c1bc8e62ad30692217482f28bb73dc0ec9677e86dbf6f72e
core/continuum-core/src/modules/serving_daemon/tests.rs 1a38097e525157465fb45aafbcc082985bebcc19066d466d2910b7debcb6a268
core/continuum-core/src/modules/serving_daemon/tests/a_bad_pin_cannot_brick_the_node.rs 14d6587a4b98f102d70ac7d63600555dc675d26fe1d6474a0cf0cc2b3bcde174
core/continuum-core/src/modules/serving_daemon/tests/budget.rs 835be70b7f8bde77fdad16442fc0e9e4cfdd4baa2632914a74f0ee0946da91b6
core/continuum-core/src/modules/serving_daemon/tests/catalog.rs e78547f1bda21da0e3f6b0b29ce329b4c156db6094678cf7f18f409c701e5e67
core/continuum-core/src/modules/serving_daemon/tests/demand.rs 381dd9c89233eedf9106e6ce1f91050ef930d918d3572930c987086da3b70354
core/continuum-core/src/modules/serving_daemon/tests/engine_admission.rs 07bab27adad47b357490e7d291e8bcd8521c07b12a311f994b10439d6a704841
core/continuum-core/src/modules/serving_daemon/tests/health.rs df42a575ee15ef4e513470d2b218a50f4fd0c9a7afcf4aa4be221f762d0f3181
core/continuum-core/src/modules/serving_daemon/tests/prompt_cache.rs 7b79a7ff66e0329c0140cc030db8b5f6c2f08cb9991ab12bfe4fea91f38ba396
core/continuum-core/src/modules/serving_daemon/tests/publication.rs b877687ae304627a3fcb3cbb49940497e84db9887d716e0c486fa3e4ae8e5722
core/continuum-core/src/modules/serving_daemon/tests/reconciliation.rs 689174128a07bd9edde4157fcd1312c15a0ce09d702aec87f2cab4a2f1836d23
core/continuum-core/src/modules/serving_daemon/tests/rehome.rs f553f366fb2c5089f1e26301241b99fde1fb17dee9d847484b1e5f58acbe4e66
core/continuum-core/src/modules/serving_daemon/tests/teacher.rs 0b81928c79d270bf0131bb1984bcb04622d401caca93e928f86edc7628245b67
core/continuum-core/src/modules/serving_daemon/tick.rs c7ef3d7949a5032fa81c738cf31dd838d08e22209a70826d29454c9cf5035dad
core/continuum-core/src/routing/macros.rs c9c1ad935b029afb11bc627411ca77b6632fdbd9c312dccface64d4cb46e784b

@joelteply
joelteply force-pushed the codex/serving-daemon-by-owner branch from 3c47d2b to 039df9a Compare October 21, 2026 08:58
git grep -n -E AdmissionHold|GateRefusal|PromptCacheGrant -- core apps found only serving_daemon definitions and implementation references, no external named caller. Retain original pub(crate) root definitions for the pure-split visibility contract; children consume those types directly. Removes redundant reexports and artificial super-qualified signature uses; preserves type comments and behavior.
@joelteply

Copy link
Copy Markdown
Contributor Author

Independent source review APPROVE at 3676ae9: AdmissionHold, GateRefusal and PromptCacheGrant retain their original facade paths, fields and ownership semantics. Descendants access the parent definitions through existing super imports; AdmissionHold._operation is private as originally. GateRefusal inherent methods remain in their implementation owner. Independent comment multiset comparison across the three changed files found zero removed or added comment lines. No compilation performed by reviewer; current-head CI is still required. This supersedes the artificial facade-use warning correction.

joelteply added a commit that referenced this pull request Oct 21, 2026
… measurement (#5229-#5233, #5240, #5244) (#5245)

* code/file_engine.rs split by owner: 3,935 lines -> 144 + seven files, each under 1,000

Split by the names already in the file: suggest (nearest paths), ops (read,
write, edit, delete, diff, undo, history), introspect (path resolution,
exists, list_dir, glob), edit_checks (parse checks, notices, repair hints),
apply (applying and reversing an edit), tests/{mod,filesystem}. Pure moves:
children `use super::*`, moved private items become pub(super), the parent
globs them back so every path holds; super::workspace_events becomes
crate::code::workspace_events (one level deeper); gutter_line_number keeps
its pub(crate) path through a re-export.

file_engine tests: 58 before, 58 after, all pass. Lines past 1000:
155,781 -> 152,846.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 18a6f6d)

* Split cognition/resource_admission.rs by owner (metrics, turns, lanes, gate, tests)

Pure move: no behaviour change. Every file is at most 1,000 lines; public paths unchanged via glob re-exports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 251e832)

* Split working_memory.rs by owner (pure move)

working_memory.rs 4138 -> 618 lines (types, snapshot, Drop; re-exports
WorkingMemoryFaculty). Moved verbatim into working_memory/:
  ledger.rs 451 (construction, action statements, fingerprints, receipts)
  continuity.rs 429 (pins, notices, facts, snapshot/restore, claim-gone)
  perception.rs 490 (readers, dispatch, collapse, settlement)
  faculty.rs 213 (WorkingMemoryFaculty + render_trail)
  tests/{mod,actions,custody,faculty,open_views,pins,provenance,rebuilt_marker,trail}.rs
    (all <=423 lines; the two test mods become one tests tree)
Widened to pub(super): push_receipt_head, collapse_results_over_cap,
publish_live_handles. Test files carry #![cfg(test)] so source-hygiene
keeps counting them as test code.

Tests: working_memory filter 49 passed before and after. Lib warnings
122 -> 122, lib-test warnings 74 -> 74. source-hygiene baselines lowered:
lines-over-1000 155781 -> 152643, excess test mods 19 -> 18.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 088a602)

* Split openai_adapter.rs by owner (near pure move)

openai_adapter.rs 3730 -> 267 lines (config/types; re-exports
extract_reasoning). Moved into ai/openai_adapter/:
  runtime.rs 423 (construction, endpoints, runtime models, slot pool)
  lora.rs 198, registry.rs 139, reasoning.rs 107, embedding.rs 72
  provider.rs 482 (AIProviderAdapter impl)
  stream.rs 789 (generate_stream_checked body)
  tests/{mod,lane_transport,request_shaping,lora_page_in,
         prefill_progress_is_liveness,unguaranteed_model_refusal_...}.rs
    (all <=593 lines)

Not a pure move in two places, both forced by the 1000-line cap:
- The AIProviderAdapter impl was a single 1250-line trait impl (one
  790-line method), which cannot span files. generate_stream_checked's
  body moved verbatim to the inherent `stream_checked` in stream.rs;
  the trait method is now a one-line forward.
- A doc link to crate::cognition::embedding in provider.rs became plain
  code formatting so the file-per-upward-import layer ratchet did not
  rise (cognition was named by two files after the split).
Widened to pub(super): endpoints, endpoints_for_model,
targets_local_serving_lane, refresh_runtime_models, resolve_dmr_model_name,
runtime_models_contain, ensure_slot_pool, lora_scale_vector,
match_lora_index, map_finish_reason, and the embedding helpers.

Tests: openai_adapter filter 41 passed before and after. Lib warnings
122 -> 122, lib-test 74 -> 74. source-hygiene lines-over-1000 baseline
152643 -> 149913.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 463d122)

* Split dream_consolidation.rs by owner (pure move + 9 justifications)

dream_consolidation.rs 2817 -> 200 lines (module header, BrainRegion impl,
engram builders, PersonaReflectionSource impl for the registry; re-exports
distiller::* and region::*). Moved into cognition/dream_consolidation/:
  distiller.rs 484 (DistilledFact, Lens, SemanticDistiller)
  region.rs 358 (reflection source, region state, boundaries, entry points)
  consolidate.rs 516 (consolidate_with, review-only, received passes)
  passes.rs 475 (dream/review/supersession passes)
  tests/mod.rs 307 (incl. pub(crate) helpers training_producer imports),
  tests/region.rs 505 (pub(crate) mod region, path kept)
Widened to pub(super): region consts/fields/ReviewBoundaries/DreamPermit/
CONSOLIDATE_EXECUTOR/consolidate_cooldown_elapsed, consolidate_with, the
pass fns, observations_block, union_recall_keys.

The original file's first code-level #[cfg(test)] (a field gate at line 694)
hid ~1,300 lines of production from the unwrap ratchet; split files expose 9
unjustified unwraps. Each now carries a same-line JUSTIFIED comment (comments
only) so the baseline does not rise.

Tests: dream_consolidation filter 18 passed before and after. Lib warnings
122 -> 122, lib-test 74 -> 74. source-hygiene baselines lowered:
lines-over-1000 149913 -> 148096, unwired 60 -> 59.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit afa6700)

* Move the faculty tests out of llm_deliberation_faculty.rs into tests/ by theme (pure move)

Each nested test module becomes its own file; the loose prompt-shaping and
verdict tests group by theme as on the owner split. include_str! paths gain
two ../ for the added directory depth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 28e811b)

* Move the tool-surface selection out of llm_deliberation_faculty.rs (pure move)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 3b34e76)

* Move the request builder out of llm_deliberation_faculty.rs (pure move)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit cfcf416)

* Move verdict production out of llm_deliberation_faculty.rs (pure move)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit e3bda14)

* Move the prompt owner (types, view, messages, fit, system, segments) out of llm_deliberation_faculty.rs (pure move)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 5e3eced)

* Move the reply claim and the hands surface out of llm_deliberation_faculty.rs (pure move)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 9817768)

* Fix a super:: path the move rewrote one level short (pure move)

The depth rewrite skipped 'in super::' meant for pub(in super::..) but also
matched 'for fact in super::perception_facts'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 24e88f6)

* Widen the canary-only fit items the sibling modules use (visibility only)

ShedPreview, ContextComposition's framing fields and the GroundingPiece
notice helpers sit in prompt/ now; private -> pub(in super::super) like their
siblings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit c776905)

* act_observe: split mod.rs by responsibility (no behaviour change)

mod.rs was a 49-line coordinator plus a 2,460-line inline test module. The test
module moves to act_observe/tests/ (declared `#[cfg(test)] mod tests;`):

- tests/mod.rs           shared fixtures: RecordingExecutor, FailingExecutor,
                         ScriptedExecutor, ActThenSpeak, AlwaysAct, CountingSpeaker,
                         body/body_with_wm/admission/acts_of helpers (410 lines)
- tests/chain.rs         causal ActChain linking scenarios (125)
- tests/apply.rs         apply_act scoping, folded/running alias, no-hands/failure (440)
- tests/drive.rs         drive_to_settle / settle_step scenarios + nudge rearm (534)
- tests/credit.rs        directed credit and live room input scenarios (509)
- tests/investigation.rs multi-act investigation, re-awaken, promise/echo gates (507)

Test bodies are moved verbatim (dedented); two relative paths gain one `super::`
(settle::NARRATION_BUDGET, perception::batch_evidence). Uncompiled locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 946a8c0)

* act_observe tests: mark moved test files #![cfg(test)] so ratchets read them as test code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 13a29ca)

* act_observe tests: restore per-file imports (drive.rs lost them)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo
(cherry picked from commit 13e77d7)

* refactor(serving): split llama server by lifecycle responsibility

(cherry picked from commit fc90c20)

* fix serving split imports and ratchet reduced file excess

(cherry picked from commit 08608e7)

* Ratchet measured llama split file excess

(cherry picked from commit e887947)

* refactor(serving): split daemon responsibilities preserving lifecycle ownership

(cherry picked from commit 26ff921)

* fix(serving): preserve trait and shared test helper visibility

(cherry picked from commit 039df9a)

* fix(serving): use retained facade types in owner signatures

(cherry picked from commit 597b0f1)

* refactor(serving): retain contract types in their original facade

git grep -n -E AdmissionHold|GateRefusal|PromptCacheGrant -- core apps found only serving_daemon definitions and implementation references, no external named caller. Retain original pub(crate) root definitions for the pure-split visibility contract; children consume those types directly. Removes redundant reexports and artificial super-qualified signature uses; preserves type comments and behavior.

(cherry picked from commit 3676ae9)

* source-hygiene: baselines measured once on the merged split train

The only Rust file past 1,000 lines is the vendored quantized_llama.rs.
Every changed count names its cause beside it; cargo test -p source-hygiene
passes 43/43 on a fresh build of this tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@joelteply

Copy link
Copy Markdown
Contributor Author

Landed via #5245 (merge-train, canary 741c118): this PR's own commits, cherry-picked unchanged. Its review stands at the head named in #5245's body.

@joelteply joelteply closed this Oct 21, 2026
joelteply added a commit that referenced this pull request Oct 21, 2026
…iblings (#5246)

Card d4909786. the_lived_copy_is_shed_by_the_substrate_not_by_her_identity was
the only data-writing test in training_producer without HomeGuard. Her store
resolves through the process home (`@persona:kimi` -> $HOME/.continuum/
personas/kimi/data/longterm.db). A parallel test that pinned and restored
HOME could move it between this test's ensure-schema and its batch, so the two
calls hit different databases: "Delete failed: no such table: staged_credit".
Seen on #5239 at 815bdba and on #5244 at 3676ae9, while canary passed. It
also wrote into the real ~/.continuum/personas/kimi on every machine that ran
it.

The fix: a tempdir home under HomeGuard::set, held for the whole test. This
is the same pattern its siblings in part_2..part_6 already use. A scan of
every tokio::test in training_producer/tests that touches data found no other
test without the guard.


Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant