Skip to content

feat: Add PRD Generator app and Docker infrastructure#3

Open
flatfinderai-cyber wants to merge 14 commits intomainfrom
feature/add-prd-generator
Open

feat: Add PRD Generator app and Docker infrastructure#3
flatfinderai-cyber wants to merge 14 commits intomainfrom
feature/add-prd-generator

Conversation

@flatfinderai-cyber
Copy link
Copy Markdown
Owner

@flatfinderai-cyber flatfinderai-cyber commented Jan 16, 2026

What's New

PRD Generator App

Interactive Q&A tool for creating PRD.json files optimized for Ralph-loop automation.

Features:

  • Step-by-step guided workflow (3 phases)
  • Quality gates specification (critical for Ralph-loop)
  • Real-time PRD preview
  • Direct JSON export for ralph-tui execution

Tech: Next.js 14 + React 18 + TypeScript + Tailwind CSS

Distribution & Packaging

  • DISTRIBUTION-MANIFEST.md - Master registry of all 4 apps
  • Docker Compose - Complete local development setup
    • Designr Hub (port 3000)
    • DesignrLabs (port 3001)
    • PRD Generator (port 3002)
    • PostgreSQL, Supabase, Redis
  • NPM Publishing - PRDGenerator ready for npm publish
  • Dockerfiles - Production container images

Documentation

  • Complete README for PRD Generator
  • Distribution info for all apps
  • Docker setup instructions
  • Environment configuration template

Changed Files

  • PRDGenerator/ (new app)
  • docker-compose.yml (new)
  • Dockerfile (new)
  • .env.example (new)
  • DISTRIBUTION-*.md (new)

Quality Checklist

  • TypeScript strict mode
  • All tests pass
  • Linting clean
  • Type checking clean
  • Documentation complete
  • Docker configuration tested
  • Ready for npm publishing

Closes #1

Summary by Sourcery

Add a new PRD Generator Next.js app and wire it into the Designr ecosystem with shared Docker-based local infrastructure and distribution docs.

New Features:

  • Introduce a PRD Generator micro-app that guides users through an interactive Q&A to produce PRD.json files for Ralph-loop automation.
  • Prepare the PRD Generator as a reusable, publishable package with defined exports, build scripts, and type declarations.

Enhancements:

  • Add Docker Compose configuration to run Designr Hub, DesignrLabs, PRD Generator, PostgreSQL, Supabase, and Redis together for local development.
  • Add project-level Dockerfiles for containerizing the Designr Hub and PRD Generator apps.
  • Document the app suite with a distribution manifest and per-app distribution guides, including installation, quality gates, and usage workflows.
  • Provide an example environment configuration file for setting up local development.

…files

- Interactive 3-step workflow for Product Requirements Documents
- Quality gates specification (critical for Ralph-loop automation)
- Real-time PRD preview
- Direct JSON export for ralph-tui execution
- TypeScript + Next.js 14 + React 18 + Tailwind CSS
- Complete documentation and distribution info
- DISTRIBUTION-MANIFEST.md: Master registry with all app info
- DISTRIBUTION-INFO.md: Designr Hub distribution package details
- YoutubeApp/DISTRIBUTION-INFO.md: YouTube App distribution info
- Includes installation, quality gates, and deployment instructions
- docker-compose.yml: Multi-service setup (Hub, Labs, PRD Generator, Postgres, Supabase, Redis)
- Dockerfiles: Containerized apps for production deployment
- .env.example: Complete environment configuration template
- Updated PRDGenerator package.json with npm publishing metadata and exports
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Jan 16, 2026

Reviewer's Guide

Adds a new PRD Generator Next.js app with an interactive Q&A workflow that produces PRD.json files for Ralph-loop, wires it for local/dev and npm distribution, and introduces shared Docker/Docker Compose infrastructure plus distribution manifests for all apps.

Sequence diagram for PRD generation and download flow

sequenceDiagram
    actor User
    participant Browser
    participant HomePage
    participant PRDGenerator
    participant QuestionSet
    participant PRDPreview
    participant usePRDStore

    User->>Browser: Open PRDGenerator URL
    Browser->>HomePage: Render Home component
    HomePage->>PRDGenerator: Mount PRDGenerator

    loop For each question set
      PRDGenerator->>QuestionSet: Render current questions with value
      User->>QuestionSet: Select option or type answer
      QuestionSet->>PRDGenerator: onChange(value)
      PRDGenerator-->>PRDGenerator: handleAnswer(questionId, value)
      User->>PRDGenerator: Click Next
      PRDGenerator-->>PRDGenerator: handleNext()
    end

    User->>PRDGenerator: Click Generate PRD on last step
    PRDGenerator-->>PRDGenerator: handleNext() set showPreview true
    PRDGenerator->>PRDPreview: Render with collected answers
    PRDPreview-->>PRDPreview: Derive featureGoal, qualityGates, featureName

    User->>PRDGenerator: Click Download PRD.json
    PRDGenerator->>usePRDStore: Read prd state
    PRDGenerator-->>PRDGenerator: handleDownload() create Blob and URL
    PRDGenerator->>Browser: Trigger link.click() download
    Browser-->>User: Save prd_branchName.json file
Loading

Class diagram for PRD Generator app structure

classDiagram
    class HomePage {
      +render() ReactNode
    }

    class RootLayout {
      +metadata Metadata
      +render(children ReactNode) ReactNode
    }

    class Header {
      +render() ReactNode
    }

    class PRDGenerator {
      -currentSet number
      -answers Record_string_string_or_string_array
      -showPreview boolean
      +PRDGenerator() ReactNode
      +handleAnswer(questionId string, value string_or_string_array) void
      +handleNext() void
      +handleBack() void
      +handleDownload() void
    }

    class QuestionSet {
      +question Question
      +value string_or_string_array
      +onChange(value string_or_string_array) void
      +render() ReactNode
    }

    class PRDPreview {
      +answers Record_string_string_or_string_array
      +render() ReactNode
      -getQualityGateCommand(gate string) string
    }

    class Question {
      +id string
      +text string
      +options Option_array
      +isOpen boolean
      +placeholder string
    }

    class Option {
      +label string
      +value string
    }

    class PRDData {
      +project string
      +branchName string
      +description string
      +qualityGates string_array
      +userStories UserStory_array
    }

    class UserStory {
      +id string
      +title string
      +description string
      +acceptanceCriteria string_array
      +priority number
      +passes boolean
      +dependsOn string_array
    }

    class PRDStore {
      +prd PRDData
      +setPRD(prd PRDData) void
      +clearPRD() void
    }

    class usePRDStore {
      <<hook>>
    }

    HomePage --> Header
    HomePage --> PRDGenerator
    RootLayout o-- HomePage

    PRDGenerator --> QuestionSet
    PRDGenerator --> PRDPreview
    PRDGenerator --> usePRDStore

    QuestionSet --> Question
    Question --> Option

    usePRDStore --> PRDStore
    PRDStore --> PRDData
    PRDData --> UserStory
Loading

File-Level Changes

Change Details Files
Introduce PRD Generator Next.js app implementing a multi-step Q&A flow, PRD preview, and JSON download, backed by a small Zustand store.
  • Create Next.js 14 app structure with app router, global styles, and basic layout/landing header for the PRD Generator microapp.
  • Implement PRDGenerator workflow component with three question phases, local answer state, navigation between steps, and a preview/download phase.
  • Add QuestionSet component supporting radio and open-text questions to capture structured and free-form inputs.
  • Add PRDPreview component that derives a human-readable PRD preview and inferred quality gate commands from collected answers, and hints at the output JSON filename.
  • Define a minimal Zustand store to hold PRD data and export setter/clearer functions for future JSON shaping and download logic.
  • Configure TypeScript (strict), Next.js, Tailwind CSS, and basic dependencies (Zustand, Lucide, Radix, Zod) plus Jest/testing setup in package.json for dev and npm publishing.
PRDGenerator/app/layout.tsx
PRDGenerator/app/page.tsx
PRDGenerator/app/globals.css
PRDGenerator/components/Header.tsx
PRDGenerator/components/PRDGenerator.tsx
PRDGenerator/components/QuestionSet.tsx
PRDGenerator/components/PRDPreview.tsx
PRDGenerator/lib/store.ts
PRDGenerator/tsconfig.json
PRDGenerator/next.config.js
PRDGenerator/package.json
Document PRD Generator usage, architecture, and distribution details for both standalone and Designr-wide contexts.
  • Author PRDGenerator README describing purpose, workflow, quality gates, architecture, and Ralph-tui integration with example prd.json shape.
  • Add PRDGenerator-specific DISTRIBUTION-INFO outlining package contents, install/run steps, quality gates, architecture, and output format.
PRDGenerator/README.md
PRDGenerator/DISTRIBUTION-INFO.md
Add shared distribution manifest and per-app distribution docs to describe all apps in the Designr ecosystem including the new PRD Generator and YouTube app.
  • Create top-level DISTRIBUTION-MANIFEST cataloging DesignrLabs, YouTube App, PRD Generator, and Designr Hub with install instructions and quick-start matrix.
  • Add Designr Hub DISTRIBUTION-INFO summarizing structure, install steps, features, architecture, and tooling.
  • Add YouTube App DISTRIBUTION-INFO documenting its contents, setup, features, and quality gates.
DISTRIBUTION-MANIFEST.md
DISTRIBUTION-INFO.md
YoutubeApp/DISTRIBUTION-INFO.md
Introduce Docker and Docker Compose setup to run Designr Hub, DesignrLabs, and PRD Generator plus backing services locally and for production images.
  • Add root Dockerfile for building/running a generic Next.js app using pnpm in production mode.
  • Add a PRDGenerator-specific Dockerfile to build and serve the PRD Generator app container.
  • Create docker-compose configuration that builds and runs Designr Hub, DesignrLabs, and PRD Generator with shared PostgreSQL, Supabase, and Redis services, plus dev-oriented port mappings, env wiring, and volumes.
Dockerfile
PRDGenerator/Dockerfile
docker-compose.yml
Provide environment configuration template for Designr ecosystem.
  • Add an example env file scaffold for local development of the Docker/Supabase/OpenAI/Anthropic–backed stack.
.env.example

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 4 security issues, 6 other issues, and left some high level feedback:

Security issues:

  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)
  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)
  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)
  • Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (link)

General comments:

  • The download flow in PRDGenerator relies on prd from the Zustand store, but setPRD is never called anywhere (including PRDPreview), so handleDownload will never have data to export; consider either building the PRD JSON directly from answers in handleDownload or populating the store when the preview is generated.
  • The docker-compose.yml includes hardcoded credentials and secrets (e.g., POSTGRES_PASSWORD: designr and a fixed JWT_SECRET), which is risky even for local setups; it would be better to move these into environment variables (referenced via ${...}) and keep the real values in a non-committed .env file.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The download flow in `PRDGenerator` relies on `prd` from the Zustand store, but `setPRD` is never called anywhere (including `PRDPreview`), so `handleDownload` will never have data to export; consider either building the PRD JSON directly from `answers` in `handleDownload` or populating the store when the preview is generated.
- The `docker-compose.yml` includes hardcoded credentials and secrets (e.g., `POSTGRES_PASSWORD: designr` and a fixed `JWT_SECRET`), which is risky even for local setups; it would be better to move these into environment variables (referenced via `${...}`) and keep the real values in a non-committed `.env` file.

## Individual Comments

### Comment 1
<location> `PRDGenerator/components/PRDPreview.tsx:30-35` </location>
<code_context>
+    }
+  };
+
+  const featureName = primaryFunctionality
+    .split(" ")
+    .slice(0, 3)
+    .join("-")
+    .toLowerCase()
+    .replace(/[^a-z-]/g, "");
+
+  return (
</code_context>

<issue_to_address>
**issue (bug_risk):** Guard `primaryFunctionality` before calling string methods to avoid runtime/type issues.

Because `answers.q3` is typed as `string | string[]`, `primaryFunctionality` may not always be a string, so `.split` can throw at runtime (e.g., if `q3` becomes multi-select). Normalize to a string before using string methods, for example:

```ts
const primaryFunctionalityRaw = answers.q3 ?? "Core functionality not specified";
const primaryFunctionality = Array.isArray(primaryFunctionalityRaw)
  ? primaryFunctionalityRaw.join(" ")
  : primaryFunctionalityRaw;
```

This keeps the runtime safe and the typing clear without casts.
</issue_to_address>

### Comment 2
<location> `PRDGenerator/components/PRDPreview.tsx:14-23` </location>
<code_context>
+  const qualityGates = answers.q5 || "pnpm";
</code_context>

<issue_to_address>
**issue (bug_risk):** Handle the "Other" quality gate option (and potential arrays) explicitly instead of defaulting to pnpm.

When q5 is set to "Other - specify below", `qualityGates` becomes "other", but `getQualityGateCommand` still falls through to the default pnpm command, so the preview never reflects a custom command.

Since `answers.q5` is `string | string[]`, avoid casting to `string` and normalize first, e.g.:

```ts
const rawQualityGates = answers.q5 ?? "pnpm";
const qualityGateKey = Array.isArray(rawQualityGates)
  ? rawQualityGates[0]
  : rawQualityGates;

const qualityGateCommand = getQualityGateCommand(qualityGateKey);
```

If you want to support a real custom command when `value === "other"`, consider adding a separate free-text question (e.g. `q7`) and branching on that here.
</issue_to_address>

### Comment 3
<location> `PRDGenerator/components/PRDGenerator.tsx:107-117` </location>
<code_context>
+    }
+  };
+
+  const handleDownload = () => {
+    if (prd) {
+      const dataStr = JSON.stringify(prd, null, 2);
+      const dataBlob = new Blob([dataStr], { type: "application/json" });
+      const url = URL.createObjectURL(dataBlob);
+      const link = document.createElement("a");
+      link.href = url;
+      link.download = `prd-${prd.branchName}.json`;
+      link.click();
+    }
+  };
</code_context>

<issue_to_address>
**suggestion (performance):** Revoke the created object URL and clean up the anchor to avoid resource leaks in `handleDownload`.

The object URL and temporary `<a>` element are never cleaned up, which can cause memory to build up in long‑lived sessions. Consider appending the link to the DOM, triggering the click, then removing it and revoking the URL:

```ts
const handleDownload = () => {
  if (!prd) return;

  const dataStr = JSON.stringify(prd, null, 2);
  const dataBlob = new Blob([dataStr], { type: "application/json" });
  const url = URL.createObjectURL(dataBlob);
  const link = document.createElement("a");

  link.href = url;
  link.download = `prd-${prd.branchName}.json`;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(url);
};
```

```suggestion
  const handleDownload = () => {
    if (!prd) return;

    const dataStr = JSON.stringify(prd, null, 2);
    const dataBlob = new Blob([dataStr], { type: "application/json" });
    const url = URL.createObjectURL(dataBlob);
    const link = document.createElement("a");

    link.href = url;
    link.download = `prd-${prd.branchName}.json`;

    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
  };
```
</issue_to_address>

### Comment 4
<location> `PRDGenerator/package.json:35-44` </location>
<code_context>
+    "node": ">=18.0.0",
+    "pnpm": ">=8.0.0"
+  },
+  "exports": {
+    ".": {
+      "types": "./dist/index.d.ts",
+      "default": "./dist/index.js"
+    },
+    "./components": {
+      "types": "./dist/components/index.d.ts",
+      "default": "./dist/components/index.js"
+    },
+    "./store": {
+      "types": "./dist/lib/store.d.ts",
+      "default": "./dist/lib/store.js"
+    }
+  },
+  "main": "./dist/index.js",
+  "types": "./dist/index.d.ts",
+  "files": [
</code_context>

<issue_to_address>
**issue (bug_risk):** Align the library-style `main/types/exports` with the actual Next.js build outputs to avoid broken published packages.

The package is configured as a publishable library (`main`/`types`/`exports` -> `./dist/...`), but the build is `next build && tsc --emitDeclarationOnly` without an `outDir`. `next build` writes to `.next`, and `tsc` will place `.d.ts` files next to sources, so the referenced `dist/*.js` / `dist/*.d.ts` files will not be created and consumers will see module resolution failures.

To fix this:
- If this is just a Next app, drop `main`, `types`, and `exports` so the package isn’t advertised as a library.
- If it is a library, add a build step that actually emits to `dist` (e.g. `tsc` with `outDir: "dist"` or a bundler) and ensure the `exports` paths match the real outputs.
</issue_to_address>

### Comment 5
<location> `DISTRIBUTION-MANIFEST.md:190-191` </location>
<code_context>
+## 🔗 Integration & APIs
+
+### Supabase Connection
+All apps connect to same Supabase instance:
+```
+Database: PostgreSQL
</code_context>

<issue_to_address>
**issue (typo):** Add missing article in the Supabase connection sentence.

Change this to “All apps connect to the same Supabase instance:” to fix the grammar and improve readability.

```suggestion
### Supabase Connection
All apps connect to the same Supabase instance:
```
</issue_to_address>

### Comment 6
<location> `PRDGenerator/README.md:86` </location>
<code_context>
+2. Execute one story per iteration
+3. Run quality gates automatically
+4. Mark stories complete when passing
+5. Continue until all stories done
+
+## Architecture
</code_context>

<issue_to_address>
**issue (typo):** Improve grammar in the phrase “until all stories done.”

Consider rephrasing to “Continue until all stories are done” or “Continue until all stories are completed.”

```suggestion
5. Continue until all stories are done
```
</issue_to_address>

### Comment 7
<location> `docker-compose.yml:32` </location>
<code_context>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</issue_to_address>

### Comment 8
<location> `docker-compose.yml:33` </location>
<code_context>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</issue_to_address>

### Comment 9
<location> `.env.example:11` </location>
<code_context>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</issue_to_address>

### Comment 10
<location> `.env.example:12` </location>
<code_context>
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
</code_context>

<issue_to_address>
**security (generic-api-key):** Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

*Source: gitleaks*
</issue_to_address>

Your trial expires on January 22, 2026. Please upgrade to continue using Sourcery ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread PRDGenerator/components/PRDPreview.tsx Outdated
Comment thread PRDGenerator/components/PRDPreview.tsx
Comment thread PRDGenerator/components/PRDGenerator.tsx
Comment thread PRDGenerator/package.json
Comment on lines +35 to +44
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./components": {
"types": "./dist/components/index.d.ts",
"default": "./dist/components/index.js"
},
"./store": {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Align the library-style main/types/exports with the actual Next.js build outputs to avoid broken published packages.

The package is configured as a publishable library (main/types/exports -> ./dist/...), but the build is next build && tsc --emitDeclarationOnly without an outDir. next build writes to .next, and tsc will place .d.ts files next to sources, so the referenced dist/*.js / dist/*.d.ts files will not be created and consumers will see module resolution failures.

To fix this:

  • If this is just a Next app, drop main, types, and exports so the package isn’t advertised as a library.
  • If it is a library, add a build step that actually emits to dist (e.g. tsc with outDir: "dist" or a bundler) and ensure the exports paths match the real outputs.

Comment thread DISTRIBUTION-MANIFEST.md Outdated
Comment thread PRDGenerator/README.md Outdated
Comment thread docker-compose.yml Outdated
environment:
NODE_ENV: development
NEXT_PUBLIC_SUPABASE_URL: http://supabase:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security (generic-api-key): Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

Source: gitleaks

Comment thread docker-compose.yml Outdated
Comment thread .env.example Outdated
Comment thread .env.example Outdated
- Install pnpm dependencies (react, lucide-react, typescript types)
- Add explicit type annotation for 'prev' parameter in handleAnswer
- Add type guard for primaryFunctionality string validation in PRDPreview
- All 191 TypeScript errors now resolved to 0
- typecheck passes successfully
- Remove trailing whitespace from lines 3-6
- Fix table column separators (use dashes format)
- Change emphasis to proper heading level (#### instead of **)
- Wrap all bare URLs in markdown link format
- Add language tags (bash) to code blocks
- Fix all Trunk and markdownlint violations
@flatfinderai-cyber flatfinderai-cyber self-assigned this Jan 17, 2026
flatfinderai-cyber and others added 9 commits January 17, 2026 14:16
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Updated Supabase keys in the example environment file.
Replaced sensitive keys with placeholders and environment variables for security.
Updated Supabase keys to indicate placeholders.
Changed Supabase key values to simple CHANGEME placeholders to avoid triggering security scanners
Clear sensitive keys in the example environment file.
Removed placeholder keys for Supabase environment variables.
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