Intake and triage
How signals dropped into `inbox/` get classified by the active profile's taxonomy and routed to the right specialist.
Construct treats every file that lands in inbox/ as a candidate signal: a bug report, a customer comment, an experiment hypothesis, an incident note, a competitor PDF, a campaign brief, a research question. The active org profile decides how that signal is classified and where it routes; the pipeline that moves it through triage is the same across every profile.
This page explains the pipeline once, then walks the taxonomy under each curated profile.
The daemon path (watcher through classification) never calls an LLM. The agent in your editor does the analysis. The triage block is a routing hint, not a verdict.
The pipeline (profile-agnostic)
-
The embed daemon's reactive watcher (
lib/embed/inbox-live-watcher.mjs) picks the file up within a second or two. -
The inbox ingester normalizes it into markdown under
~/.cx/knowledge/and indexes it for retrieval. -
prepareIntakeForIngestedFileruns four deterministic preparation steps:- lane suggestion via
lib/docs-routing.mjs - related-doc retrieval via the hybrid corpus query
- excerpt extraction for the agent to see without reopening the file
- triage via
classifyRdIntake(keyword/heuristic, no LLM) against the active profile's table
- lane suggestion via
-
The result is written to
.construct/intake/pending/<id>.json. -
At the next session start, the hook surfaces a one-line summary per pending packet:
## Pending intake (3) - login-feedback.md → bug / implementation · owner: debugger · next: diagnose - competitor-pricing.md → research / research · owner: business-strategist · next: research - hallucination-trace.md → eval-finding / evaluation · owner: evaluator · next: evaluate
The daemon never calls an LLM. The model spend stays with the agent in the user's editor.
How the active profile shapes triage
Each profile ships a classification table at lib/intake/tables/<profile>.mjs. The table defines:
- the intake types the profile recognizes (e.g.
bug,brief,request) - the stages in this profile's work loop (e.g.
implementationvsdraftvstriage) - the owner role per intake type
- the recommended chain of specialists for the handoff sequence
construct scope show tells you which table is active. construct scope set <id> switches.
Default: rnd (software R&D)
Loop: signal → framing → hypothesis → research → artifact → design → implementation → evaluation → release → operations.
| Intake type | Examples | Primary owner |
|---|---|---|
user-signal | customer feedback, NPS, support tickets, churn | product-manager |
bug | stack traces, errors, regressions, crashes | debugger |
requirement | feature requests, PRD drafts, acceptance criteria | product-manager |
research | competitor scans, market research, pricing teardowns | business-strategist |
experiment | hypotheses, spikes, prototypes, falsifiable plans | rd-lead |
eval-finding | hallucinations, score regressions, judge findings | evaluator |
architecture | ADR drafts, RFC drafts, system-design tradeoffs | architect |
incident | outages, SLO breaches, latency spikes, pages | sre |
launch-asset | release notes, version bumps, ship candidates | release-manager |
ops | runbooks, cron jobs, capacity plans, dependency upgrades | operations |
security | CVEs, vulnerabilities, secret leaks, exploit reports | security |
legal-compliance | GDPR, license audits, DPA reviews | legal-compliance |
unknown | nothing matched; agent decides | orchestrator |
operations
Loop: request → triage → resolve → document → improve.
Intake types: request, bug, incident, ops, security, docs, unknown. Owners map to operator / engineer / sre roles.
creative
Loop: brief → research → draft → review → publish → measure.
Intake types: brief, content-request, asset, experiment, report, legal-compliance, ops, unknown. Owners map to product-lead / content-writer / designer / data-analyst.
research
Loop: question → gather → analyze → synthesize → recommend.
Intake types: question, study, synthesis, report, experiment, ops, unknown. Owners map to researcher / ux-researcher / evaluator / data-analyst.
Custom
Drop a .construct/scope.json with custom: true and an intake.classificationTable path that lives under .construct/. The validator at lib/scopes/validate-custom.mjs enforces the cap structure. See Profile lifecycle.
Triage block schema (same across profiles)
classifyRdIntake returns the base triage object; prepareIntake adds the resolved researchProfile when the active scope maps the classified intake/artifact lane to one. The values vary by profile; the shape does not.
| Field | Purpose |
|---|---|
intakeType | One of the profile's declared types |
rdStage | One of the profile's declared stages (the field name is historical; it's the active profile's stage) |
primaryOwner | Persona name from specialists/org |
recommendedChain | Ordered handoff sequence |
recommendedAction | Verb-led next step (diagnose, draft-prd, create-runbook, etc.) |
risk | low, medium, or high |
requiresApproval | true for high-stakes actions |
confidence | [0, 1], keyword-match density |
rationale | One-line explanation |
researchProfile | Optional scope-level research profile for downstream routing (codebase, market, etc.) |
Classification heuristics
The classifier is deterministic: same input → same output, no LLM. It builds a signal corpus from the filename, the extracted excerpt, and the titles of related docs, then scores it against the active profile's keyword sets. Ties break in favor of higher-stakes classes. security beats research when both match; incident beats architecture.
This is a fast-tier classifier, not a final answer. The agent in the user's editor reads the packet and does the actual analysis. The triage block is a routing hint, not a verdict.
On-disk layout
<project>/.construct/intake/
pending/
2026-05-14T15-22-08-login-feedback.json newly arrived signal
processed/
2026-05-13T11-04-19-payment-postmortem.json agent finished
skipped/
2026-05-12T09-47-30-noise-pdf.json agent intentionally skipped
Each <id>.json carries: id, createdAt, status, intake (sourcePath, outputPath, characters, knowledgeSubdir), triage (the fields above), suggestion (lane), related (top-K artifacts), excerpt, query. Status transitions add processedAt + processedBy + notes or skippedAt + skippedBy + reason.
CLI
construct intake list # ID, type, stage, owner, action
construct intake show <id> # full packet: triage, excerpt, related artifacts
construct intake done <id> # move pending → processed
construct intake done <id> --output=<path> # stamp intake_id + confidence into artifact frontmatter
construct intake done <id> --no-output=rejected|deferred|merged # close without an artifact
construct intake skip <id> [--reason=…] # move pending → skipped, audit trail preserved
construct intake reopen <id> # processed or skipped → pending
construct scope show # which taxonomy is currently active
--output=<path> stamps the artifact at <path> with intake_id, intake_confidence, and intake_rationale in its YAML frontmatter. Every intake-derived artifact carries verifiable provenance back to the source packet. The stamp is refused if the artifact already has a different intake_id. Use intake: none frontmatter in an artifact that intentionally has no intake source.
In solo mode the queue is the filesystem (.construct/intake/). In team and enterprise modes the same CLI talks to a Postgres-backed queue with row-locked worker claims; the contract is identical.
See also
- Profile lifecycle. How the intake taxonomy is designed and validated per profile.
- Persona and skill research. The methodology behind the owners and chains each profile names.
- Intake loop. Implementation deep dive of the 8-step pipeline (that doc is the internals; this one is the operator's view).
- Deployment model. Solo vs team vs enterprise topology.
- Beads and durable state. How triaged work becomes tracked work.
- Gates and enforcement. What the agent must satisfy before closing intake.