---
name: project-request-intake
description: Use when an outside agent, reviewer, or user wants to propose a change, report a bug, report an issue, or report a compatibility problem without directly editing project truth. Creates, reviews, accepts, rejects, or graduates `REQ-*` records in `.prd_plugin/state/requests.json`, with affected IDs, severity, risk, provenance, reproduction details, compatibility scope, and links to tracking, document branches, requirements, changelog, health, or evidence.
---

# Project Request Intake

## Deterministic Entry

Plan and run `request.intake` when enabled. It owns duplicate-safe
`prd_file_request` creation and exact `prd_get` confirmation. Apply this skill
to classification, privacy, triage judgment, and communication; do not recreate
the record or allocate its ID after the workflow succeeds.

Use this skill to keep speculative or externally reported changes, issues, bugs,
and compatibility concerns out of mainline project truth until they are
reviewed.

## Rules

An optional `triage_draft` may be delegated after the request facts have been
captured deterministically. Use `prd_reporting.py` to build the bundle. When the
configured AI-Collab Substrate adapter is available, dispatch it with
`prd_substrate_runtime` (`action: report`, `task: triage_draft`) or
`prd_runtime_worker.py report --task triage_draft`; the callback must pass
`prd_reporting_validate` before the prose is used. The draft is advisory: it cannot
accept, reject, mutate, or allocate IDs for a request. On unavailable or invalid
delegation, honor the configured `main`, `deterministic_only`, or `fail`
fallback and continue to keep canonical request state under this skill's rules.

- Store requests in `.prd_plugin/state/requests.json`.
- Read `.prd_plugin/config.json` when present; `paths.requests_file` may
  override the default requests path.
- Use `REQ-###` IDs.
- Use `MSG-###` IDs for request-thread messages and replies.
- Treat request and future request-thread message state as repo-scoped.
- In the `prd-plugin` repo, use `scope: upstream` for plugin-level intake.
- In repos using PRD Plugin, use `scope: local` by default and do not read or
  write other repos' requests/messages.
- Treat an explicitly supplied `scope: local` on `prd_file_request` as an
  authoritative routing decision. The filing remains local and is not
  auto-exported. Omit `scope` when the classifier should decide whether a
  request belongs upstream.
- Submit to an upstream hub only through an explicit export/import or upstream
  submission flow, and include only sanitized upstream-visible thread messages.
- **Any request about PRD Plugin itself goes upstream unless it was explicitly
  routed local.** If the record names a plugin-owned surface — a `prd_*.py`
  script, a hook (`prd_stop_guard.py`, `prd_precommit_gate.py`, …), a `project-*` skill, a
  method doc, the MCP server, or the installer — then it is a plugin bug, not a
  project bug. Waiting to be asked is how plugin bugs die in local state.
- **File through the tool and submission is automatic.** The `prd_file_request`
  MCP tool classifies the record, sets `upstream_submission: true`, exports a
  sanitized package to `.prd_plugin/outbox/`, and delivers it into the hub inbox
  when `PRD_UPSTREAM_HUB` or `config.requests.upstream_hub_path` names one. Its
  response tells you what happened. The `.prd_plugin` state directory name is
  not itself a plugin-owned surface. Do not export by hand.
- **If you wrote the record some other way**, run
  `python scripts/request_autosubmit.py --request-id REQ-xxx` yourself. It is the
  same pipeline, it no-ops on ordinary project requests, and it never fails a
  filing. `prd_gate.py` warns (`unsubmitted_plugin_request`) until it has run.
- A downstream PRD Plugin bug report is not complete until `request_export.py`
  creates an upstream package and `request_import.py` stages it in the PRD
  Plugin hub inbox, unless the hub checkout or import path is unavailable and
  that blocker is reported.
- A plugin bug that lives only in your `requests.json` is an **unreported** bug:
  the hub cannot see it, so it never gets fixed. `prd_gate.py` warns on every
  commit while such a request is unsubmitted (`unsubmitted_plugin_request`).
- **Send every request through to the hub immediately. The outbox
  (`.prd_plugin/outbox/`) is a transit buffer that no one reads — never leave a
  request package sitting there.** In the same run that you export a package
  with `request_export.py`, stage it into the PRD Plugin hub inbox with
  `request_import.py` and verify it landed (for example, `message_check.py`
  should list it as a new or imported inbox package). A package that is exported
  to the outbox but not imported into the hub inbox is NOT sent and counts as a
  dropped request. The only acceptable reason to leave a package short of the
  hub inbox is that the hub checkout or import path is genuinely unavailable —
  and then you must report that blocker explicitly, not leave the package
  sitting silently in the outbox.
- Use `.prd_plugin/outbox/` for downstream submission packages,
  `.prd_plugin/inbox/` for hub-staged incoming packages, and
  `.prd_plugin/mailboxes/<repo-id>/` for scoped downstream replies.
- Downstream repos may pull only their own mailbox. Do not read or import
  another repo's mailbox.
- Follow the script install-scope policy in
  `templates/script-install-scope.json`; downstream repos must not copy the
  full PRD Plugin `scripts/` directory or install `hub_runtime` /
  `plugin_development` scripts as local project scripts.
- Before creating a new `REQ-*`, search existing requests and request threads
  for the same affected area, error, reproduction path, or compatibility
  symptom.
- If an existing request already covers the issue, append a new `MSG-*` to that
  request's thread instead of creating a duplicate request.
- A persistent or still-reproducing bug is a thread update on the original
  request, not a new bug request.
- Frequent request/mailbox automation must use `automation_guard.py` so
  overlapping ticks write a local pending marker instead of running concurrently.
- Treat scheduler state as a two-layer check: repo-local runtime coordination
  under `.prd_plugin/local/automation/` and the host agent's app-side
  automation record (e.g. Codex's
  `%USERPROFILE%/.codex/automations/<automation-id>/automation.toml` or the
  opencode equivalent).
- If session cleanup matters, prefer host-agent archive semantics (e.g.
  `codex archive <session-id>`) over deleting or moving transcript files.
- Stop-hook cleanup scripts must not write arbitrary diagnostic JSON to stdout
  during normal hook execution; stdout is reserved for the host agent's hook
  protocol output.
- Outside agents should append a request instead of directly changing PRDs,
  architecture, implementation plans, skills, templates, or committed state for
  speculative ideas.
- Include `requested_by_agent` as `AGENT-*` and `requested_from_session` as
  `SES-*` when known.
- Include affected files, affected IDs, rationale, proposed actions, severity,
  and risk. Do not include time/duration estimates in requests or replies; use
  complexity and confidence ratings instead (`.prd_plugin/method/estimation.md`).
- Include `scope`, `origin_repo`, `visibility`, `upstream_submission`, and
  `upstream_request_id` when known.
- For bugs and compatibility issues, include reproduction steps, expected
  behavior, actual behavior, affected versions/environments, workaround, and
  regression status when known.
- For bugs about evidence, tracking, changelog, memory, session summaries,
  registry counters, or future timestamps, run `state_consistency_check.py`
  when available before deciding the request can stay `in_review`.
- Do not mark a request `accepted` unless a maintainer or lead agent has
  reviewed it.
- Use `in_review` while shaping the request and `needs_info` when the requester
  must reply before a decision.
- Follow `requests.outside_agents_must_use_requests`,
  `requests.allow_direct_project_truth_edits`, and
  `requests.implemented_requires_graduated_to` when configured.
- The risk-band autonomy below (what to triage vs. escalate) is governed by
  `project-decision-policy` and `automation.autonomy_level`; the floor (never
  push/merge/publish or accept high-impact changes without consent) holds in
  every tier. Use `project-request-intake` to file/triage proposed changes; use
  `project-blocker-resolution` when in-progress work is *stopped* and needs
  authority, input, or an out-of-scope action.
- Graduating an accepted `REQ-*` into active work is owned here: when a `REQ-*`
  is `accepted`, create or link the `TRK-*` and, for code work, the
  `IMP-TASK-*` (via `project-implementation-plan`) that will carry it, and record
  those in the request's `linked_ids`/`graduated_to`. Do not leave an accepted
  request with no active-work owner.
- Accepted requests should graduate into normal project artifacts such as
  `TRK-*`, `DBR-*`, `PRD-*`, `ARCH-*`, `IMP-*`, `CHG-*`, or `EV-*`.
- A request marked `implemented` should include `graduated_to` links showing
  which plugin artifacts, state records, docs, templates, or evidence records
  implemented it.
- Autonomous request-check automation should validate and triage inbox packages
  by risk band.
- Low-risk validated inbox submissions may be imported into canonical hub
  requests without waiting for human approval.
- Medium-risk validated non-bug submissions should be imported and marked `in_review` with recorded rationale rather than left only in inbox transport.
- Low- or medium-risk bug submissions with clear reproduction steps and a bounded fix must not stop at `in_review`.
  Continue into `project-systematic-debugging`, reproduce or validate the
  bug, add a failing test, implement the root-cause fix, run the relevant
  verification, update request status and `graduated_to` links, and prepare the
  fix for commit. Route the actual commit, push, and merge through
  `project-git-workflow`, which requires explicit user consent unless the user
  has clearly said to continue autonomously and no blocker exists.
- High-risk or critical validated inbox submissions should be imported and
  marked `in_review` unless policy validation fails or required details are
  missing.
- Use `needs_info` when required reproduction, compatibility, provenance, or
  privacy details are missing.
- Only stop without mutating canonical request state when the package is
  invalid, unsafe to import, duplicated without a clear merge target, or
  violates repo-scope/privacy policy.

## Staleness Coverage

Apply the shared policy in `.prd_plugin/method/staleness-rules.md`.


Use `requests.stale_after_days` to flag proposed, `in_review`, or `needs_info`
requests with no recent activity, and use
`health.accepted_request_stale_after_days` to flag accepted requests that have
not graduated into tracked work. Stale outbox packages, inbox packages, mailbox
replies, and unresolved request threads should be triaged rather than left as
transport-only state.

## Status Values

- `proposed`: submitted but not reviewed.
- `in_review`: actively being discussed or assessed.
- `needs_info`: waiting for requester input.
- `accepted`: approved for work or incorporation.
- `rejected`: reviewed and declined.
- `superseded`: replaced by another request or artifact.
- `implemented`: accepted and completed.

## Request Threads

Use a request thread when the request needs discussion, compromise, repro
details, or maintainer clarification before a decision.

Threads are also the deduplication path for ongoing issues. If an agent finds
that a bug still reproduces in a later version, a compatibility failure affects
another downstream repo, or new evidence changes severity/risk, update the
existing request's `affected_versions`, status, and thread with a new `MSG-*`.
Do not create a fresh request unless the root issue, affected area, or requested
outcome is materially different.

Each thread should include:

1. `status`: `open`, `waiting_for_requester`, `resolved`, or `closed`
2. `participants`
3. `messages`

Each `MSG-*` message should include:

1. `id`
2. `created_at`
3. `author_agent`
4. `author_session`
5. `visibility`: `repo`, `upstream`, or `public`
6. `reply_to`, when replying to another `MSG-*`
7. `body`
8. `source_refs`

Messages must not include secrets, private prompts, raw reasoning traces, or
raw `.prd_plugin/local/` session paths. Downstream repos should keep repo-local
messages local unless the message is explicitly marked for upstream visibility.

## Mailbox Protocol

Use the mailbox protocol when the downstream repo cannot write to the hub:

1. Add or update the local request and `MSG-*` replies.
2. Export upstream-visible content with `request_export.py` into
   `.prd_plugin/outbox/`.
3. Hub imports packages with `request_import.py` into `.prd_plugin/inbox/`.
4. Hub publishes scoped replies with `request_mailbox.py`.
5. Downstream pulls its own mailbox with `request_pull.py`.

Steps 2 and 3 are a single, uninterrupted handoff, not two separate decisions.
Do not stop after step 2 with the package sitting in `.prd_plugin/outbox/` — the
outbox is not a destination and no one reads it. Run the import into the hub
inbox in the same run and confirm the package arrived before reporting the
request as sent. If you literally cannot reach the hub to import, say so as a
blocker; do not silently leave the package in the outbox.

Run helper scripts from the PRD Plugin hub/plugin bundle unless the exact
helper has been explicitly installed in the target repo according to the script
install-scope policy. A downstream repo with a full copied PRD Plugin
`scripts/` directory should be treated as stale install state: keep only
explicitly installed `downstream_runtime` or `downstream_optional` helpers and
remove/report hub-only or plugin-development scripts.

If the downstream repo has explicitly installed optional `prd_doctor.py`, run
`python scripts/prd_doctor.py --repo-root .` before export or mailbox pull work
to catch stale requests, privacy warnings, invalid request JSON, and copied
hub/development scripts.

When the user asks to "check messages", asks whether there are "any messages",
or asks about requests, inboxes, mailboxes, replies, or outbox items, call
`prd_check_messages` for the **current repository first**. This PRD Plugin
transport check owns that phrase; do not substitute a chat, email,
collaboration, or host-message surface. If the MCP tool is unavailable, run the
installed downstream runtime directly:

```bash
python .prd_plugin/scripts/message_check.py --repo-root . --config .prd_plugin/config.json
```

In the PRD Plugin hub checkout, the source helper path is
`python scripts/message_check.py --repo-root . --config
.prd_plugin/config.json`. Use the report to distinguish new inbox packages,
already-imported inbox packages, outbox packages, mailbox files, pending
replies, and unresolved request threads. Never decide or report that there is
nothing to do until this current-repo check has completed.

Request and mailbox work depends on PRD Plugin repo-local skills being visible
to the host agent. Downstream installs should include both
`.agents/skills/<skill>/SKILL.md` (Codex discovery) and
`.opencode/skill/<skill>/SKILL.md` (opencode discovery) from the PRD Plugin
skill set; if `prd_install_skills.py` is available, run
`python scripts/prd_install_skills.py --repo-root . --target-agent both`
to install or refresh them.

Never graduate inbox packages directly into canonical hub requests without
validation.

Do not call a downstream PRD Plugin bug "reported" if it only exists as a local
`REQ-*`. Local intake is the source record; upstream reporting requires the
mailbox protocol above when the hub is available.

After validation, autonomy is expected:

1. Low risk: import into canonical request state and continue the run.
2. Medium-risk non-bug: import into canonical request state as `in_review` and
   record why review is still needed.
3. High or critical risk: import into canonical request state as `in_review`
   unless the package is invalid or missing mandatory detail.
4. Missing mandatory detail: import as `needs_info` when the package is safe to
   retain but cannot yet be assessed.

Low- or medium-risk bug submissions with clear reproduction steps and a bounded
fix must not stop at `in_review`. Use `project-systematic-debugging`,
reproduce or validate the report, add a failing test, implement the root-cause
fix, verify, update the request thread/status and `graduated_to` links, and
prepare the fix for commit. Route commit, push, and merge through
`project-git-workflow`, which requires explicit user consent unless the user has
clearly said to continue autonomously and no blocker exists. Stop if the report
is missing required detail, the fix is not bounded, the scope is unsafe, or the
user explicitly says not to commit.

## Hub-side Import

At the hub, import an inbox package with the validated `prd_import_request`
MCP tool — or run the deterministic `request.import` workflow, which wraps the
same tool with a confirm step — (one duplicate-safe call: allocates the REQ id, copies the package
fields, records `origin_repo`/`source_request_id` transport provenance so
`message_check` reports the package imported, and normalizes request-type
vocabulary aliases such as compat/compatibility). Never hand-copy package
fields into canonical state.

## Replies Must Be Delivered AND Verified

**A locally constructed upstream reply is not sent until the configured upstream inbox contains the intended message ID and exact body digest.**
**Never report a reply as sent merely because it exists in local request state
or an outbox package.**

A reply that exists only in local state has not been sent. Use one operation
that appends, packages, delivers, and READS THE DESTINATION BACK:

```bash
python .prd_plugin/scripts/request_reply.py --deliver --request-id <REQ>   --visibility upstream --body "..."
```

It fails visibly (exit 2, nothing reported delivered) when export sanitization
rejects the body — messages referencing local runtime paths
(`.prd_plugin/local`, absolute drive paths, `/Users/`, `/home/`) are dropped
whole, and a plain reply would have reported success anyway. It also UPDATES an
already-submitted package, because autosubmit skips those and later replies
would otherwise never travel (REQ-132).

## Close the Loop on Resolution

Implementing or rejecting an IMPORTED request (it carries `origin_repo` +
`source_request_id`) is NOT complete until the outcome travels back:

1. Record the resolution reply on the hub record with **`--visibility repo`**:
   `request_reply.py --request-id <hub REQ> --visibility repo --body
   "RESOLVED upstream: ... mark your <their id> with upstream_request_id <hub id>"`.
   Visibility is DIRECTIONAL: `repo`/`public` travel to the origin repo;
   `upstream` is HUB-ONLY and the mailbox correctly refuses to forward it. A
   reply marked `upstream` delivers the status change with its reasoning
   silently stripped (REQ-130).
2. Republish the scoped mailbox into the origin repo:
   `request_mailbox.py --repo-id <origin> --output
   <origin>/.prd_plugin/mailboxes/prd-plugin/mailbox.json`.
3. Close the resolution thread (no reply expected).

The origin repo consumes it automatically: `session.start` runs
`requests.pull`, applying every delivered mailbox to local records
(resolving by `source_request_id`, stamping `upstream_request_id`). Run it
on demand with `python .prd_plugin/scripts/request_pull.py --all`. The same
pass clears transport residue: outbox packages whose request is resolved, and
mailbox envelopes fully applied into records (a mailbox with unmatched rows is
kept). Without that, a repo stays at `attention` forever despite having
nothing outstanding.

`message_check` reports terminal imported requests with no upstream reply as
`unanswered_resolutions` — clear that count before reporting intake work done.
Skipping this leaves the origin repo warning "submitted upstream, no answer"
in every one of its sessions (REQ-124).

## Messaging a Peer Repo Directly

A request can be addressed to another repo instead of the hub. Call
`prd_list_destinations` first — it returns the declared peers, whether each is
reachable right now, and whether it shares your workspace — then file with
`target_repo` set to one of those ids:

```
prd_file_request(summary=..., request_type=..., severity=..., target_repo="<peer id>")
```

Delivery happens as part of filing and is verified by reading the written
package back; the result carries `addressed.delivered` and, when it did not go,
`addressed.reason`. Deliver later or retry with `prd_send_request`, and
`prd_flush_addressed_requests` sends everything still waiting. `session.start`
flushes automatically, so an addressed request cannot sit unsent.

Peers are declared once in config as `requests.peers` — `{"<peer id>": "<path
to that repo>"}`. A configured path IS the consent to deliver there, exactly as
`requests.upstream_hub_path` already works, and declaration is one-sided: you
do not need the peer to declare you back, because an inbox is a mailbox and
import stays their explicit act.

**The filing always survives.** No routing outcome discards what you wrote:

- **undeclared** target — nothing is written anywhere, and the reason names the
  `requests.peers` entry to add. The consent floor is never write into a repo
  you were not pointed at.
- **unreachable** declared target — held and retried on the next flush.
- **not_ours_to_send** — the request was imported from someone else;
  `target_repo` on it records who it was addressed to, not somewhere for you to
  send it. Reply on its thread instead.
- **terminal** / **self_addressed** — a closed request does not travel, and a
  request addressed to this repo is already here.

Hub-private (`upstream`) messages never travel to a peer; only `repo` and
`public` do. Messaging a repo inside your own workspace warns and still
delivers — those agents share your chat, so use it when you specifically want a
durable auditable record rather than ephemeral coordination.

## Automation Guard

For scheduled request checks, run `automation_guard.py begin --name
request-check` before scanning requests, inbox, outbox, or mailboxes. If it
returns busy or exits with code 75, stop after the pending marker is written.

At the end of an owned run, run `automation_guard.py complete --name
request-check --run-id <run-id>`. If the result has `pending_follow_up: true`,
perform one additional pass after releasing the lock.

Lock and pending files live under `.prd_plugin/local/automation/` and must not
be committed, indexed, exported upstream, or treated as project truth.

When asked whether request checks are scheduled, paused, or still recurring, do
not answer from the local lock alone. Inspect both:

1. `.prd_plugin/local/automation/` for active, stale, or pending runtime state.
2. The host agent's app-side automation record (e.g. Codex's
   `%USERPROFILE%/.codex/automations/<automation-id>/automation.toml` or the
   opencode equivalent) for scheduler state such as `kind`, `rrule`, and
   `status`.

Use precise wording:

- "No active run" means no owned local lock is running.
- "No future schedule" means no app-side automation record exists, or it is not
  a recurring active schedule.
- "Scheduler paused" means the app-side automation exists but is paused.

When running an autonomous request check, the automation should not stop at a
read-only summary if a validated inbox package can be triaged safely according
to the repo's risk policy.

## Request Types

Use one of:

- `feature`
- `bug`
- `compatibility`
- `docs`
- `method`
- `skill`
- `template`
- `health`
- `refactor`
- `question`
- `version_change`

Use `version_change` when a downstream repo wants approval or compatibility
review before bumping its installed PRD Plugin version. To detect whether an
update is needed, run `version_advice.py` from a local copy of the PRD Plugin
hub/development repo:

```bash
python scripts/version_advice.py \
  --installed-repo <path-to-downstream-repo> \
  --write-request \
  --requests <path-to-downstream-repo>/.prd_plugin/state/requests.json
```

The script detects the installed version from `.prd_plugin/config.json`,
`.codex-plugin/plugin.json`, `.opencode/plugin.json`, or
`.prd_plugin/state/project.json`, compares it with the hub's
`.prd_plugin/state/releases.json`, and appends a `version_change` request
automatically when the recommendation is `review_first` or `blocked`.

## Request Record

Each request should include:

1. `id`
2. `status`
3. `request_type`
4. `scope`, `origin_repo`, `visibility`, `upstream_submission`, and
   `upstream_request_id`
5. `summary`
6. `rationale`
7. `severity`
8. `requested_by_agent`
9. `requested_from_session`
10. `affected_versions`, `affected_environments`, and `compatibility_scope`
   when relevant
11. Version-change fields when relevant: `current_plugin_version`,
    `requested_plugin_version`, `target_repo`, `update_window`,
    `breaking_change_concerns`, and `migration_blockers`
12. `affected_areas`
13. `linked_ids`
14. `linked_health_findings`
15. `source_refs`
16. `proposed_actions`
17. bug fields when relevant: `reproduction_steps`, `expected_behavior`,
    `actual_behavior`, `workaround`, and `regression`
18. `risk`
19. `created_at`
20. review fields when reviewed
21. `thread` with `MSG-*` messages, when discussion is needed
22. `graduated_to` links when accepted work is created

## Review Checklist

- The request is clear enough to evaluate.
- Existing requests and `thread.messages` have been searched for the same
  issue before creating a new request.
- Persistent bugs, repeated repros, and later-version compatibility failures
  update the original request thread instead of creating duplicates.
- Affected files or IDs are identified.
- Bugs and compatibility issues include enough reproduction or environment
  detail to investigate, or explicitly state what is missing.
- Clear low- or medium-risk bugs are either investigated and fixed through the
  bug workflow or moved to `needs_info` with the specific blocker recorded.
- Version-change requests include current and requested plugin versions when
  known, plus migration blockers or breaking-change concerns.
- Risk is stated.
- The request does not contain secrets, private prompts, or raw reasoning
  traces.
- The request belongs to the current repo scope, or it is an explicit upstream
  submission.
- Thread messages belong to the current repo scope or are explicitly marked for
  upstream visibility.
- Mailbox pulls are scoped to the current repo only.
- Scheduled request checks use the local automation guard before scanning, and
  scheduler-state answers check the app-side automation record separately.
- Accepted requests link to the artifact or state records that will carry the
  work.
- Implemented requests include `graduated_to` links; otherwise request reports
  should flag them for attention.

## Output Shape

When reporting request work, include:

1. Added or updated `REQ-*` IDs.
2. Review decision, if any.
3. Thread status and new `MSG-*` IDs, if any.
4. Graduation links, if any.
5. Any request left proposed, `in_review`, or `needs_info` because it needs
   owner/requester input.
