The control plane previously accepted intake from exactly one repository
(Ekho-Labs/infra-work). This PR declares intake repositories with typed
capabilities via AICOWORKER_INTAKE_POLICIES: exactly one full
human control surface, plus research-only intake repositories
(Ekho-Labs/data-scraping-central) whose automation-armed
kind:research issues run the paired research lane with no code-writing
authority.
Intake stops being a single hard-coded repository and becomes a typed capability set.
A repository is either the full human control surface or a
research-only lane — and nothing else is accepted.
The control plane accepted intake from exactly one repository,
Ekho-Labs/infra-work, configured through
AICOWORKER_INTAKE_REPOSITORY. Every intake lookup was keyed by issue
number alone.
AICOWORKER_INTAKE_POLICIES declares each intake repository with a typed
capability: exactly one full human control surface, plus
research-only repositories such as
Ekho-Labs/data-scraping-central.
Automation-armed kind:research issues there run the paired research lane
with no code-writing authority, and every intake identity becomes composite:
(intake_repository, intake_issue_number).
A daily expansion loop arms a research issue; the controller runs it as research and only research. Solid edges carry events and data, dashed edges are rejection paths. Hover or focus any node to trace its edges.
opened plus one
issues.labeled event per label — behaviour verified against a live
capture, not assumed.
kind:change issue on a research-only intake repository is blocked at
classification, before any planning, with an explicit diagnostic.
full capability, even if it reached the queue.
Research-only intake is not enforced in one place. Three layers reject independently, so no single missed check grants a research repository code-writing authority.
research policy denies comment wake events and every control label
except agent:ready, agent:pause and
agent:stop.@Ekho-Labs/infrastructure."research-only intake repository: only kind:research work is accepted there"
full capability is rejected with unauthorized, even if it
reached the queue.TargetProfile.Allowed=false) and rejected as change targets by
planner validation.agent:ready claims one paid paired generation
(~$200 cap, Claude + Codex arms).72 files, grouped by the job they do. Each group expands to the per-file reason it changed.
Policy is full or research;
ParsePolicies is fail-closed; the research control-label whitelist
lives here.AICOWORKER_INTAKE_POLICIES replaces
AICOWORKER_INTAKE_REPOSITORY, defaulting to
Ekho-Labs/infra-work:full.full repository.intake_repository column to
intake_issues, received_events, runs and
budget_reservations; composite primary keys and foreign keys;
backfills 'Ekho-Labs/infra-work', then drops the defaults.contracts.IntakeRef is introduced.full intake — defense in depth; a per-run
intake repository for snapshots, fences and halt checks.ResearchOnly=true.RepositoryProfile.ResearchOnly.!ResearchOnly.IntakePolicies configuration.issues.labeled events.kind:change issue there blocks.AICOWORKER_INTAKE_POLICIES documented.go build, go vet and a full go test -race ./...
are green, including the Docker-backed Postgres integration suite.
Verified empirically against a scratch repository capture (2026-08-18):
gh issue create --label fires opened plus one
labeled event per label.
A controller built against Postgres with AICOWORKER_INTAKE_POLICIES
accepted and qualified a signed research-intake webhook; denial came only at the live
GitHub credential check.
AICOWORKER_INTAKE_POLICIES set on the controller and the
publisher (infrastructure-tf-kubernetes).kind:research and agent:ready labels exist in
data-scraping-central.data-scraping-central, confirmed by the
requester.