Intake
koryph intake bridges external issue trackers into the Koryph planning
funnel. It polls configured sources, creates one planning bead per new issue,
and stamps each bead with mandatory labels so that no issue reaches the
dispatch loop without human or planner review first.
Quick start
Add an intake list to your project's koryph.project.json:
{
"intake": [
{
"provider": "github",
"source": "acme/widgets",
"trigger": "triage"
},
{
"provider": "github",
"source": "acme/backend",
"trigger": "bug",
"limit": 50,
"comment_back": true
},
{
"provider": "jira",
"source": "acme.atlassian.net/ENG",
"trigger": "status = \"To Do\" AND labels = \"koryph\"",
"comment_back": true
},
{
"provider": "linear",
"source": "ENG",
"trigger": "label:triage",
"comment_back": true
}
]
}
Then run:
koryph intake --project <project-id>
All configured sources are polled in one run. The output table groups results by source:
intake acme/widgets
ACTION ISSUE BEAD TITLE NOTE
ingested #42 beads-0abc Fix nil deref in wave scheduler -
skipped #38 beads-0a12 Add prometheus metrics already ingested
ingested 1, skipped 1
intake acme/backend
ACTION ISSUE BEAD TITLE NOTE
ingested #7 beads-0xyz Crash on login commented
ingested 1, skipped 0
intake ENG
ACTION ISSUE BEAD TITLE NOTE
ingested #12 beads-0def Fix flaky retry in poller commented
ingested 1, skipped 0
total across 3 sources: ingested 3, skipped 1
Configured sources vs. single-source mode
Configured sources (recommended): When the project's koryph.project.json
has a non-empty intake list, koryph intake iterates every entry in
order. Per-source settings (trigger, limit, comment_back) apply to that
source only.
Single-source mode (legacy / quick use): When no intake list is
configured, intake falls back to the project's registry remote with CLI flags:
koryph intake --project my-project --label triage --limit 20
intake source schema
Each entry in the intake list is an IntakeSource:
| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | "github" |
Issue-tracker type: "github", "jira", or "linear". |
source |
string | (required) | Target within the provider. GitHub: "owner/repo". JIRA: "<host>/<project-key>". Linear: the team key (e.g. "ENG"). |
trigger |
string | "triage" |
Label (GitHub), JQL predicate (JIRA), or label:/state:/bare-value filter (Linear) that filters open issues. |
limit |
int | 20 |
Maximum number of open issues fetched per run. |
comment_back |
bool | false |
Post the new bead ID back as a comment on each ingested issue. |
mapping |
object | {} |
Reserved for future provider-specific field remapping. Ignored in v1. |
Provider setup
GitHub
Prerequisites:
- The
ghCLI must be onPATHand authenticated (gh auth login). - The
bdCLI must be onPATH.
Source format: "owner/repo" — for example "koryph/koryph".
Trigger: Any GitHub label name. The default is "triage". Combine with
GitHub issue templates that auto-apply labels for zero-touch routing:
# .github/ISSUE_TEMPLATE/bug.yml
labels: ["triage", "bug"]
Comment-back: When comment_back: true, intake posts a comment on each
newly ingested issue:
Tracked as bead beads-0abc for planning.
This links GitHub to Koryph without modifying labels or closing issues.
Comment-back is non-fatal: if the gh issue comment call fails the bead is
still created and the failure reason is recorded in the output table.
JIRA Cloud
Prerequisites:
- A JIRA Cloud account with API token access.
- The
bdCLI must be onPATH. - Two environment variables set before running
koryph intake: JIRA_EMAIL— the Atlassian account email address.JIRA_TOKEN— the API token (generate one here).
Either value may be a 1Password vault reference (starts with op://). When
detected, intake resolves it via op read <ref> before use:
export JIRA_EMAIL="me@acme.com"
export JIRA_TOKEN="op://Personal/Atlassian API Token/credential"
Source format: "<host>/<project-key>" — for example
"acme.atlassian.net/ENG". The host is your Atlassian Cloud subdomain; the
project key is the uppercase identifier shown in your JIRA project settings.
Trigger (JQL): A JQL predicate that is AND-combined with
project = "<key>". For example:
{
"provider": "jira",
"source": "acme.atlassian.net/ENG",
"trigger": "status = \"To Do\" AND labels = \"koryph\"",
"comment_back": true
}
When trigger is empty the full project is polled (up to limit issues).
Provenance key: jira-<host>/<project-key>#<number> — for example
jira-acme.atlassian.net/ENG#42. This key is unique across instances and
projects and is stored as both the bead's --external-ref and a label.
Priority mapping:
| JIRA priority | Bead priority |
|---|---|
| Highest / Blocker | 0 (critical) |
| High / Critical | 1 (high) |
| Medium (default) | 2 (medium) |
| Low / Minor / Lowest / Trivial | 3 (low) |
Type mapping: Issues with issuetype Bug (case-insensitive) set the bead
type to bug. All other types use the default bead type.
Comment-back: When comment_back: true, intake posts an ADF-wrapped
comment on each newly ingested JIRA issue via the REST v3 API. Non-fatal:
if the comment POST fails the bead is still created.
Note: The JQL trigger scope is the configured <project-key>. Multi-project
JQL queries are not recommended in v1 — issues from projects other than the
configured key will receive incorrect provenance keys and may be re-ingested.
Linear
Prerequisites:
- A Linear API key (personal or workspace) with access to the target team.
- The
bdCLI must be onPATH. - One environment variable set before running
koryph intake: LINEAR_API_KEY— the Linear API key.
The value may be a 1Password vault reference (starts with op://). When
detected, intake resolves it via op read <ref> before use:
export LINEAR_API_KEY="op://Personal/Linear API Key/credential"
Source format: the Linear team key — for example "ENG". The workspace
itself is implicit from the API key, so source names only the team within
it.
Trigger: a filter kind and value, case-insensitive on the prefix:
| Trigger form | Meaning |
|---|---|
| (empty) | No extra filter; all open issues in the team. |
label:<name> |
Issues that carry the named label. |
state:<name> |
Issues whose workflow state matches the name. |
<bare-value> |
Treated as a label name (backward-compatible default). |
{
"provider": "linear",
"source": "ENG",
"trigger": "label:triage",
"comment_back": true
}
Priority mapping:
| Linear priority | Bead priority |
|---|---|
| 1 (Urgent) | 0 (critical) |
| 2 (High) | 1 (high) |
| 3 (Medium) | 2 (medium) |
| 4 (Low) | 3 (low) |
| 0 (No priority) (default) | 2 (medium) |
Type mapping: Issues carrying a label named bug (case-insensitive) set
the bead type to bug. All other issues use the default bead type. Native
Linear label names are carried through verbatim as additional bead labels.
Provenance key: linear-<team-key>#<number> — for example linear-ENG#42
for issue ENG-42. This key is stored as both the bead's --external-ref
and a label, so it never collides with GitHub or JIRA provenance keys.
Comment-back: When comment_back: true, intake posts a Markdown comment
on each newly ingested Linear issue via the GraphQL commentCreate mutation.
Non-fatal: if the comment mutation fails the bead is still created.
CLI flags
| Flag | Default | Description |
|---|---|---|
--project |
(required) | Project ID from the local registry. |
--label |
(per-source) | Override trigger label for all sources in this run. |
--limit |
(per-source) | Override issue fetch limit for all sources in this run. |
--dry-run |
false |
Print what would be ingested; mutate nothing. |
--comment |
false |
Override comment-back for all sources in this run. |
When --label, --limit, or --comment are passed on the command line they
override the per-source settings for that run only. They do not modify the
koryph.project.json configuration.
Dry run
koryph intake --project my-project --dry-run
No beads are created and no external state is mutated. The output table shows
would-ingest instead of ingested. Use dry run to preview what a scheduled
intake would produce before enabling it.
Planning funnel and mandatory labels
Every bead created by intake receives two mandatory labels:
| Label | Purpose |
|---|---|
intake |
Marks the bead as originating from an external issue. |
no-dispatch |
Blocks the wave engine from building this bead autonomously. |
The no-dispatch label is the critical gate: intake is a planning input, not
a dispatch trigger. A human or planner agent reviews the bead, decides whether
and how to act, and removes no-dispatch when the issue is ready to build.
In addition, a provenance label is applied to record the source issue:
| Provider | Example key |
|---|---|
| GitHub | gh-acme/widgets#42 |
| JIRA | jira-acme.atlassian.net/ENG#42 |
| Linear | linear-ENG#42 |
Including the owner/repo (or host/project-key for JIRA, or team key for
Linear) in the key guarantees that issues sharing a number across different
configured sources are never conflated. The same value is stored in the
bead's --external-ref field for reliable deduplication across all
configured sources.
Idempotency
Before creating a bead, intake looks up any existing bead whose external-ref
matches the issue's provenance key. If one is found the issue is skipped
regardless of the bead's current status. Re-running intake at any time is safe.
Priority and type mapping
GitHub
Intake reads p0–p3 issue labels and maps them to bead priority:
| GitHub label | Bead priority |
|---|---|
p0 |
0 (critical) |
p1 |
1 (high) |
p2 |
2 (medium, default) |
p3 |
3 (low) |
Issues without a p0–p3 label default to priority 2 (medium).
A bug-labeled issue sets the bead type to bug. All other issues use the
default bead type. Combined with GitHub issue templates that automatically apply
a triage label plus a bug or feature label, intake routes issues into the
right bead type without manual intervention.
JIRA
JIRA priority names are mapped to bead priority (see JIRA Cloud
section above for the full table). The issuetype Bug maps to bead type bug.
Native JIRA labels are carried through verbatim as additional bead labels.
Linear
Linear's numeric priority is mapped to bead priority (see Linear
section above for the full table). A label named bug (case-insensitive)
maps to bead type bug. Native Linear labels are carried through verbatim as
additional bead labels.
Provenance footer
Each bead's description ends with a footer linking back to the source issue:
GitHub:
---
Source: github.com/acme/widgets/issues/42, author @alice, ingested by koryph intake
JIRA:
---
Source: https://acme.atlassian.net/browse/ENG-42, author alice@acme.com, ingested by koryph intake
Linear:
---
Source: linear.app/team/ENG/issue/ENG-42, author alice@acme.com, ingested by koryph intake
What intake never does
- Closes or relabels external issues (v1).
- Creates beads that are immediately dispatchable —
no-dispatchis always applied and must be explicitly removed by a person or planner. - Uses a raw GitHub API token — all GitHub access goes through
gh. - Stores credentials on disk — JIRA and Linear credentials are read from
environment variables only, resolved at runtime (or via
op readfor vault refs).