Skip to content

fix(spec): address map-file schema review comments - #218

Merged
macalbert merged 10 commits into
mainfrom
fix/map-file-schema-review
May 4, 2026
Merged

fix(spec): address map-file schema review comments#218
macalbert merged 10 commits into
mainfrom
fix/map-file-schema-review

Conversation

@macalbert

@macalbert macalbert commented May 3, 2026

Copy link
Copy Markdown
Owner

Pull Request

What does this PR do?

Addresses review comments from #217 (merged prematurely):

  1. Schema fix (AWS default): Remove
    equired: [provider]\ from AWS if/then so validation applies when provider is absent (default=aws)
  2. Schema fix (reserved keys): Reject unknown $-prefixed keys via \patternProperties: false\ (except \ and )
  3. Parser fix (all stacks): Update parsers to skip all $-prefixed keys instead of exact-matching \ — prevents \ from leaking into variable mappings
  4. Schema tests: Add contract tests validating schema behavior with ajv

Related issues

Type of change

  • Feature
  • Bugfix
  • Refactor
  • Documentation
  • Other

Checklist

  • Tests added/updated (if needed)
  • Docs updated (if needed)
  • Lint/format pass

Notes for reviewer

Schema fixes are committed. Parser fixes and schema tests are in progress via TDD.

Summary by CodeRabbit

  • New Features

    • Added map-file JSON Schema support and example $schema references across docs and samples.
  • Bug Fixes

    • Map files now exclude all reserved keys prefixed with $ (including $schema) from variable mappings.
  • Tests

    • Added extensive schema validation suites for AWS, Azure, GCP, HashiCorp, and file providers.
    • Added/updated parser tests to verify reserved-key exclusion.
  • Documentation

    • Changelogs, CLI/docs, and SDK READMEs updated with schema guidance and release entries.

- Remove required constraint from AWS if/then so validation applies
  when provider is absent (default=aws)
- Reject unknown dollar-prefixed keys via patternProperties: false
  (except schema and config)

Parser fixes and schema tests follow in subsequent commits.

Ref: #217
Copilot AI review requested due to automatic review settings May 3, 2026 09:36
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • .github/agents/content-designer.agent.md is excluded by none and included by none

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 45bd09ca-f7d3-4ccc-9ffb-e86e83f0a771

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR treats all top-level $-prefixed keys as reserved: only $config (when an object) is parsed into provider config; all other $... keys (including $schema) are excluded from mappings. JSON Schema, SDK parsers (Node/.NET/Python), CLI/e2e mapping readers, tests, docs, and changelogs are updated accordingly.

Changes

Reserved Control Key Filtering

Layer / File(s) Summary
Schema Definition
src/website/public/schema/map-file.v1.json
Top-level patternProperties changed to `^$(?!schema$
Data Shape / Validation Helpers
src/sdks/nodejs/src/application/map-file-parser.ts
Adds parseRawJson and parseConfig helpers; parse verifies root is a non-array object, normalizes/validates $config.provider, and encapsulates config parsing/validation.
Core Parser Behavior
src/envilder/core/infrastructure/variableStore/FileVariableStore.ts, src/sdks/dotnet/Application/MapFileParser.cs, src/sdks/python/envilder/application/map_file_parser.py
Parsing logic changed to skip any top-level keys starting with $; $config is only deserialized when it is an object. FileVariableStore now filters non-$ entries and only includes string values in mappings.
CLI / E2E Readers
e2e/cli.test.ts, e2e/gha.test.ts, e2e/sample/*
Readers updated to derive mappings by filtering out keys that start with $ (instead of only omitting $config). Sample map files now include "$schema" referencing the published schema.
Tests — Unit
tests/envilder/core/.../fileVariableStore.test.ts, tests/sdks/dotnet/Application/MapFileParserTests.cs, tests/sdks/nodejs/application/map-file-parser.test.ts, tests/sdks/python/application/test_map_file_parser.py
New/updated tests assert $schema and other $-prefixed keys are excluded from mappings; added tests ensure only string-valued mappings are retained; .NET tests refactored to share parser instance.
Tests — Schema Validation
tests/spec/map-file-schema*.test.ts (multiple files)
Comprehensive Ajv (2020) schema test suites added for providers (aws, azure, gcp, hashicorp, file) and general map-file cases; cover positive and negative provider-specific fields and unknown $-prefixed keys.
Docs / Changelogs / SDK Metadata
docs/*, src/sdks/*/README.md, src/sdks/nodejs/package.json, src/sdks/python/pyproject.toml, src/sdks/dotnet/Envilder.csproj
Added "$schema" examples, documented reserved-key filtering fix in changelogs, and bumped SDK package versions (Node/.NET/Python).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • macalbert/envilder#146 — Related changes touching MapFileParser reserved-key handling and parsing behavior.
  • macalbert/envilder#212 — Related architectural/schema work around ignoring top-level $-prefixed keys; this PR implements that rule across SDKs.
  • macalbert/envilder#217 — Related edits to the map-file JSON Schema that this PR further tightens (reserved $-prefixed key handling).

Suggested labels

bug, documentation

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change as fixing the map-file schema based on review comments, which aligns with the core objective of addressing schema and parser issues.
Description check ✅ Passed The description covers most required sections: it explains what the PR does with specific detail about 4 key changes, lists related issues, marks the correct type (bugfix), and includes reviewer notes. However, the checklist items are not actually checked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/map-file-schema-review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Envilder map-file v1 JSON Schema to better align validation behavior with the intended defaults and reserved-key rules, and republishes the same changes to the website-served schema copy.

Changes:

  • Adjust $config.provider=aws conditional so AWS rules also apply when provider is omitted (default aws).
  • Disallow unknown top-level $-prefixed keys (allowing only $schema and $config).
  • Apply the same schema changes to both the source spec and the published website schema.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
spec/map-file.v1.json Fixes AWS-default conditional behavior and rejects unknown $-prefixed keys in the canonical schema.
src/website/public/schema/map-file.v1.json Mirrors the same schema fixes in the website-published copy.

Comment thread spec/map-file.v1.json

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the JSON schema for map files in both the specification and the website public directory. It removes the requirement for the provider property in the AWS validation block, allowing AWS to be the default provider. Additionally, it restricts top-level reserved keys starting with $ to only $schema and $config using a negative lookahead regex. I have no feedback to provide.

macalbert added 2 commits May 3, 2026 12:23
- General: empty, mappings-only, /, $-prefix rejection,
  unknown config fields, metadata, non-string values
- AWS: profile allowed, vaultUrl/projectId/namespace/path forbidden,
  default behavior when provider absent
- Azure: vaultUrl required, profile/projectId/namespace/path forbidden
- GCP: projectId required, profile/vaultUrl/namespace/path forbidden
- HashiCorp: vaultUrl required, namespace allowed,
  profile/projectId/path forbidden
- File: path required, profile/vaultUrl/projectId/namespace forbidden

Also adds ajv + ajv-formats as devDependencies for schema validation.
All four parsers (TS core, Node SDK, .NET SDK, Python SDK) previously
used exact-match on the config key, letting keys like schema leak into
variable mappings. Changed to startsWith prefix check so all reserved
keys are excluded.

- FileVariableStore.getParsedMapping(): filter dollar-prefixed entries
- MapFileParser.parse() (Node SDK): startsWith guard
- MapFileParser.Parse() (.NET SDK): StartsWith guard
- MapFileParser.parse() (Python SDK): startswith guard
- Rebuild GHA bundle (dist/index.js)
Copilot AI review requested due to automatic review settings May 3, 2026 10:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 20 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread src/envilder/core/infrastructure/variableStore/FileVariableStore.ts Outdated
Comment thread .github/agents/tdd-coach.agent.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/sdks/nodejs/application/map-file-parser.test.ts (1)

116-129: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add test coverage for the cross-provider validation error paths.

Once the cross-provider checks are added to parseConfig (see the comment in map-file-parser.ts), two tests are needed here to ensure they exercise the error paths:

+  it('Should_ThrowError_When_ProviderIsAzureAndProfilePresent', () => {
+    // Arrange
+    const json = JSON.stringify({
+      $config: {
+        provider: 'azure',
+        vaultUrl: 'https://my-vault.vault.azure.net',
+        profile: 'production',
+      },
+    });
+    const sut = new MapFileParser();
+
+    // Act
+    const act = () => sut.parse(json);
+
+    // Assert
+    expect(act).toThrow(
+      "Cross-provider conflict: 'profile' is not supported for the Azure provider",
+    );
+  });
+
+  it('Should_ThrowError_When_ProviderIsAwsAndVaultUrlPresent', () => {
+    // Arrange
+    const json = JSON.stringify({
+      $config: {
+        provider: 'aws',
+        vaultUrl: 'https://vault.example.com',
+      },
+    });
+    const sut = new MapFileParser();
+
+    // Act
+    const act = () => sut.parse(json);
+
+    // Assert
+    expect(act).toThrow(
+      "Cross-provider conflict: 'vaultUrl' is not supported for the AWS provider",
+    );
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/sdks/nodejs/application/map-file-parser.test.ts` around lines 116 -
129, Add two unit tests in map-file-parser.test.ts that exercise the
cross-provider validation paths introduced in MapFileParser.parse / parseConfig:
one test where $config.provider is "aws" but the JSON includes a
provider-specific key for "azure" (or another non-aws key) and assert parse
throws the expected cross-provider error message, and a second test where
$config.provider is "azure" but the JSON includes an "aws"-specific key and
assert the corresponding error; locate tests near the existing
Should_ThrowError_When_ProviderIsUnknown case and follow the same
Arrange/Act/Assert pattern using new MapFileParser() and expect(act).toThrow
with the exact error strings produced by parseConfig.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/envilder/core/infrastructure/variableStore/FileVariableStore.ts`:
- Around line 37-40: In the Object.entries loop inside FileVariableStore where
you build the mappings record (variables: mappings, rest), ensure you only
assign runtime string values: check if typeof value === "string" (and value !==
null) before doing mappings[key] = value; skip or ignore any non-string values
so mappings remains Record<string,string> and callers of getMapping() can't
receive non-string data.

In `@src/sdks/nodejs/src/application/map-file-parser.ts`:
- Around line 56-76: In parseConfig, add cross-provider validation: after
computing providerStr and config.provider, if config.provider ===
PROVIDER_MAP['azure'] and obj.profile is a string throw an Error like "profile
is not allowed for provider: azure"; similarly if config.provider ===
PROVIDER_MAP['aws'] and obj.vaultUrl is a string throw an Error like "vaultUrl
is not allowed for provider: aws"; reference the parseConfig function,
providerStr, config.provider and PROVIDER_MAP, and also add unit tests named
Should_ThrowError_When_ProviderIsAzureAndProfilePresent and
Should_ThrowError_When_ProviderIsAwsAndVaultUrlPresent to cover both failure
cases.

In `@src/sdks/python/envilder/application/map_file_parser.py`:
- Around line 50-52: The code currently calls _deserialize_config for every
$config object without rejecting cross-provider fields; modify
_deserialize_config (or validate right before calling it where key ==
_CONFIG_KEY) to determine the effective provider (use the explicit provider
value or default to "aws" if missing) and then enforce cross-provider rules: if
provider == "azure" and the config dict contains "profile" (or "profile" key),
raise a ValueError; if provider == "aws" and the config dict contains "vaultUrl"
or "vault_url", raise a ValueError; ensure the error messages mention the
offending field and provider so that callers of _deserialize_config (referenced
here as config = _deserialize_config(value)) will reject invalid Azure+profile
or AWS+vaultUrl combinations.

---

Outside diff comments:
In `@tests/sdks/nodejs/application/map-file-parser.test.ts`:
- Around line 116-129: Add two unit tests in map-file-parser.test.ts that
exercise the cross-provider validation paths introduced in MapFileParser.parse /
parseConfig: one test where $config.provider is "aws" but the JSON includes a
provider-specific key for "azure" (or another non-aws key) and assert parse
throws the expected cross-provider error message, and a second test where
$config.provider is "azure" but the JSON includes an "aws"-specific key and
assert the corresponding error; locate tests near the existing
Should_ThrowError_When_ProviderIsUnknown case and follow the same
Arrange/Act/Assert pattern using new MapFileParser() and expect(act).toThrow
with the exact error strings produced by parseConfig.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb5fdefd-e6c8-4eb2-96d8-059af5bdafc6

📥 Commits

Reviewing files that changed from the base of the PR and between ede3c78 and 97ea143.

⛔ Files ignored due to path filters (5)
  • .github/agents/tdd-coach.agent.md is excluded by none and included by none
  • github-action/dist/index.js is excluded by !**/dist/**, !github-action/dist/** and included by none
  • package.json is excluded by none and included by none
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml and included by none
  • spec/map-file.v1.json is excluded by none and included by none
📒 Files selected for processing (15)
  • src/envilder/core/infrastructure/variableStore/FileVariableStore.ts
  • src/sdks/dotnet/Application/MapFileParser.cs
  • src/sdks/nodejs/src/application/map-file-parser.ts
  • src/sdks/python/envilder/application/map_file_parser.py
  • src/website/public/schema/map-file.v1.json
  • tests/envilder/core/infrastructure/variableStore/fileVariableStore.test.ts
  • tests/sdks/dotnet/Application/MapFileParserTests.cs
  • tests/sdks/nodejs/application/map-file-parser.test.ts
  • tests/sdks/python/application/test_map_file_parser.py
  • tests/spec/map-file-schema.aws.test.ts
  • tests/spec/map-file-schema.azure.test.ts
  • tests/spec/map-file-schema.file.test.ts
  • tests/spec/map-file-schema.gcp.test.ts
  • tests/spec/map-file-schema.hashicorp.test.ts
  • tests/spec/map-file-schema.test.ts

Comment thread src/envilder/core/infrastructure/variableStore/FileVariableStore.ts Outdated
Comment thread src/sdks/nodejs/src/application/map-file-parser.ts
Comment thread src/sdks/python/envilder/application/map_file_parser.py
macalbert and others added 2 commits May 3, 2026 17:14
…E2E helpers

- Bump CLI/GHA to 0.10.0, Node SDK to 0.2.0, .NET SDK to 0.3.0, Python SDK to 0.4.0
- Update all READMEs and docs with `` field in map-file examples
- Update changelogs for all components
- Add `` to sample map files
- Fix E2E readMappings() helpers to filter all `$`-prefixed keys
- Rebuild GHA bundle
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 3, 2026 15:17

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/cli.test.ts (1)

64-79: ⚡ Quick win

Add $schema to the dynamic test fixtures so the new filter is actually exercised.

The dynamically-written map files created in beforeAll (mapFilePath and mapFileWithConfigPath) contain no $schema key. As a result, the updated startsWith('$') filter in readMappings is never triggered against a $schema value during these E2E runs. If production code accidentally leaked $schema into mappings and tried to resolve its URL as an SSM path, these tests would not catch it.

Adding $schema to both fixtures mirrors the real-world map files shown in the sample fixtures (param-map.json, param-map-with-aws-config.json) and closes the coverage gap for the core fix in this PR.

♻️ Proposed fixture update
     await Promise.all([
       writeFile(
         mapFilePath,
-        JSON.stringify({ TOKEN_SECRET: `${ssmPrefix}/Token` }, null, 2),
+        JSON.stringify(
+          {
+            $schema: 'https://envilder.com/schema/map-file.v1.json',
+            TOKEN_SECRET: `${ssmPrefix}/Token`,
+          },
+          null,
+          2,
+        ),
       ),
       writeFile(
         mapFileWithConfigPath,
         JSON.stringify(
           {
+            $schema: 'https://envilder.com/schema/map-file.v1.json',
             $config: { provider: 'aws' },
             TOKEN_SECRET: `${ssmPrefix}/Token`,
           },
           null,
           2,
         ),
       ),
     ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cli.test.ts` around lines 64 - 79, The dynamic test fixtures written in
beforeAll (the writeFile calls that create mapFilePath and
mapFileWithConfigPath) must include a "$schema" key so the readMappings code
path that filters keys with startsWith('$') is exercised; update the JSON
objects passed to those writeFile invocations to add a "$schema" entry (matching
the shape of real fixtures, e.g., a schema URL string) in both the plain map and
the map with $config so the startsWith('$') logic in readMappings runs during
the E2E tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/cli.test.ts`:
- Around line 64-79: The dynamic test fixtures written in beforeAll (the
writeFile calls that create mapFilePath and mapFileWithConfigPath) must include
a "$schema" key so the readMappings code path that filters keys with
startsWith('$') is exercised; update the JSON objects passed to those writeFile
invocations to add a "$schema" entry (matching the shape of real fixtures, e.g.,
a schema URL string) in both the plain map and the map with $config so the
startsWith('$') logic in readMappings runs during the E2E tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e79b7658-8f5a-4031-9f96-0a5a0da2fa7c

📥 Commits

Reviewing files that changed from the base of the PR and between 97ea143 and 646ca8e.

⛔ Files ignored due to path filters (4)
  • README.md is excluded by none and included by none
  • github-action/README.md is excluded by none and included by none
  • package.json is excluded by none and included by none
  • secrets-map.json is excluded by none and included by none
📒 Files selected for processing (18)
  • docs/changelogs/cli.md
  • docs/changelogs/gha.md
  • docs/changelogs/sdk-dotnet.md
  • docs/changelogs/sdk-nodejs.md
  • docs/changelogs/sdk-python.md
  • docs/pull-command.md
  • docs/push-command.md
  • e2e/cli.test.ts
  • e2e/gha.test.ts
  • e2e/sample/param-map-with-aws-config.json
  • e2e/sample/param-map-with-azure-config.json
  • e2e/sample/param-map.json
  • src/sdks/dotnet/Envilder.csproj
  • src/sdks/dotnet/README.md
  • src/sdks/nodejs/README.md
  • src/sdks/nodejs/package.json
  • src/sdks/python/README.md
  • src/sdks/python/pyproject.toml
✅ Files skipped from review due to trivial changes (16)
  • e2e/sample/param-map-with-aws-config.json
  • src/sdks/python/pyproject.toml
  • src/sdks/nodejs/package.json
  • src/sdks/dotnet/Envilder.csproj
  • e2e/sample/param-map.json
  • docs/changelogs/gha.md
  • docs/changelogs/sdk-dotnet.md
  • docs/changelogs/sdk-python.md
  • src/sdks/dotnet/README.md
  • docs/changelogs/sdk-nodejs.md
  • e2e/sample/param-map-with-azure-config.json
  • docs/pull-command.md
  • docs/changelogs/cli.md
  • src/sdks/nodejs/README.md
  • docs/push-command.md
  • src/sdks/python/README.md

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 41 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread src/envilder/core/infrastructure/variableStore/FileVariableStore.ts Outdated
Comment thread tests/spec/map-file-schema.aws.test.ts
Comment thread .github/agents/tdd-coach.agent.md
macalbert added 2 commits May 3, 2026 17:37
getParsedMapping() now skips entries where typeof value !== 'string',
preventing numbers, objects, booleans, or nulls from leaking into
downstream secret resolution.

Closes review comments about unsafe 'value as string' cast.
Copilot AI review requested due to automatic review settings May 4, 2026 21:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 42 out of 44 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Comment thread .github/agents/content-designer.agent.md
References src/sdks/*/README.md (per-SDK READMEs) instead of
non-existent src/sdks/README.md.
@macalbert
macalbert merged commit c41ff9b into main May 4, 2026
16 checks passed
@macalbert
macalbert deleted the fix/map-file-schema-review branch May 4, 2026 22:57
@macalbert macalbert self-assigned this May 4, 2026
@macalbert macalbert added enhancement New feature or request bug Something isn't working labels May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants