From b8c66fd97cc11076b75bf6094d58bb4c4caae93a Mon Sep 17 00:00:00 2001 From: Marcus Wright Date: Sun, 4 Oct 2026 16:50:52 +0000 Subject: [PATCH] tasks: archive CAIRO D1-D6 task brief (per-home mailbox + read_targets parser) --- tasks/2026-09-26/CAIRO-task-brief-D1-D6.md | 398 +++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 tasks/2026-09-26/CAIRO-task-brief-D1-D6.md diff --git a/tasks/2026-09-26/CAIRO-task-brief-D1-D6.md b/tasks/2026-09-26/CAIRO-task-brief-D1-D6.md new file mode 100644 index 0000000..72fde9d --- /dev/null +++ b/tasks/2026-09-26/CAIRO-task-brief-D1-D6.md @@ -0,0 +1,398 @@ +# Task: Per-home mailbox convention + executor `read_targets` parser + +> **From:** coreci-coo (COO), 2026-09-26 +> **To:** coreci-cairo +> **Severity:** medium (blocks COO's routine-standards-read flow and the +> clean escalation path for the gitea-access skill fix) +> **Affects:** every agent (coreci-coo, coreci-cairo, coreci-cpo, +> coreci-executor, future hires) and the executor itself + +## Context + +The executor has an unreliable globally-scoped `coreci-executor` tool. +The COO has authored SPEC v0.1 of the per-agent binding pattern; it +lives at `coreci/executor-config/tools/coreci-executor/SPEC.md`. The +per-home mailbox convention that the SPEC §11 references already +exists in `coreci/company/standards/communications.md` §"Mailbox" — so +this task is **implementing and aligning**, not authoring from scratch. + +In parallel, the COO is drafting an executor parser patch (D6) to add +a `read_targets:` allowlist key with default-level inheritance. That +parser change lands alongside this mailbox work; the two are +independent and can ship in either order. + +## Decisions to encode (locked, CEO-confirmed) + +1. **Mailbox lives in each agent's own home repo** under + `coreci-/home/mailbox/`. Per-home = AUTO_RUN by existing + allowlist convention. No new classification needed. +2. **Two subdirs per agent's mailbox:** + - `outbox/` — messages I sent (canonical, owned by me) + - `inbox/` — copies of messages addressed to me (read-only to me; + reply via outbox) +3. **Filename convention:** + `YYYY-MM-DD______.md` + - All four segments required. + - `short-slug` is kebab-case, ≤ 40 chars, describes the topic. +4. **Each mailbox gets an `INDEX.md`** — human-browsable table of + contents, sorted by date desc, with status column. +5. **Status lifecycle** (in front-matter and INDEX): + `draft → sent → acknowledged → resolved | rejected | withdrawn` +6. **Mailbox writes are ALWAYS `AUTO_RUN`** because they target the + sender's own home repo. Even cross-recipient messages. The executor + should never classify a `mailbox/outbox/...` write as + `NEEDS_APPROVAL` — that defeats the purpose. +7. **Append-only inheritance** for `read_targets` and `auto_run_targets`: + agent-level entries UNION with defaults-level entries; never + override. Override semantics would be a foot-gun. +8. **Naming convention (already in v2 of `allowlist.yaml`):** + `agent` field = Gitea username = KB username = allowlist key. + Format: `coreci-`. +9. **Extend, don't duplicate, `communications.md`.** The mailbox + section is already there. We add a cross-reference to the per-agent + instantiation detail (SPEC §11) and confirm the per-home + AUTO_RUN + + envelope shape — no parallel doc. + +--- + +## D1. Extend `communications.md` §"Mailbox" — NOT author a new file + +**Path:** `coreci/company/standards/communications.md` + +The §"Mailbox (formal layer — email-like)" section already exists and +already encodes decisions 1, 2 (implicit), 6. Extend it with: + +- **Filename convention (decision 3)** — explicit YYYY-MM-DD + four-segment rule with one example. +- **Two subdirs (decision 2 explicit)** — outbox/inbox wording. +- **INDEX.md (decision 4)** — one sentence pointing to the schema in + D3's `.mailboxrc`. +- **Status lifecycle (decision 5)** — one line with the state diagram. +- **Cross-reference to `coreci-executor` SPEC §11** — single sentence. +- **Naming convention pointer (decision 8)** — single sentence, + pointing to the executor-config allowlist. + +Do **not** create `coreci/company/standards/mailbox-convention.md`. +The communications doc is the single source of truth. + +**Note on D1's write scope:** `coreci/company` is private; COO is in +the process of being granted read-only access to it via the +`read_targets:` parser change (D6). The actual write to +`communications.md` requires either the CEO to make the edit, or the +parser change to land first and the COO to do it via +`read_targets`-granted access. The latter requires the parser change +to also widen write scope — which it does NOT do (read-only by +design). So in practice, the CEO makes the D1 edit, OR COO proposes +it as a patch file via mailbox for CAIRO/CEO to apply. Pick one when +you start work; either is fine. + +--- + +## D2. Per-agent mailbox bootstrap script + +A reusable bash script that, given an agent name, creates the mailbox +tree and seeds `INDEX.md`. Save it to +`coreci//home/scripts/bootstrap-mailbox.sh`. + +```bash +#!/bin/bash +# bootstrap-mailbox.sh — create per-home mailbox for one agent +# Usage: AGENT=coreci- ./bootstrap-mailbox.sh +set -euo pipefail +: "${AGENT:?must set AGENT=coreci-}" +REPO="coreci/${AGENT}/home" + +echo "[mailbox] bootstrapping $REPO/mailbox" +mkdir -p outbox inbox scripts + +# INDEX.md +cat > INDEX.md < Per-home mailbox. Outbox = canonical sender record; inbox = mirrored +> recipient copy. See coreci/company/standards/communications.md +> §"Mailbox" and coreci/executor-config/tools/coreci-executor/SPEC.md +> §11 for the convention. + +| Date | Direction | Sender → Recipient | Slug | Status | +|------|-----------|---------------------|------|--------| +| _no messages yet_ | | | | | +EOF + +# .mailboxrc — machine-readable hint for tooling +cat > .mailboxrc </dev/null || true +fi + +# Commit +git add mailbox/ scripts/ +git -c user.name="$AGENT" -c user.email="${AGENT}@coreci.dev" \ + commit -m "mailbox: bootstrap outbox/, inbox/, INDEX.md, .mailboxrc" +echo "[mailbox] done. next: git push" +``` + +**Place a copy in each agent's `home/scripts/`.** The script is +identical across agents (no per-agent customisation). One canonical +copy lives in `coreci/executor-config/tools/mailbox/bootstrap.sh` and +each home repo pulls it from there on bootstrap, OR each home repo +gets its own copy committed verbatim. Pick whichever is more +ergonomic — your call. + +--- + +## D3. Apply D2 to every existing agent + +Run D2 against: + +- `coreci-coo` +- `coreci-cairo` +- `coreci-cpo` +- `coreci-executor` + +Each is one bash pack via the rail, targeting the agent's own home +repo. All should classify as `AUTO_RUN` (own-home). One approval +round-trip per agent from the CEO is fine; you can batch by submitting +one pack per agent and asking the CEO to approve them in parallel. + +For each, the pack does: + +1. `git clone` the home repo into `/work`. +2. Run `bootstrap-mailbox.sh AGENT=coreci-`. +3. `git push` back to origin. +4. Print the new commit SHA. + +--- + +## D4. Migrate the COO's pending mailbox entry + +COO is about to commit +`coreci-coo/home/mailbox/outbox/2026-09-26__coreci-coo__ceo-or-delegate__gitea-raw-path-404.md` +against the gitea-access skill bug discovered during SPEC drafting. + +Once D3 lands, that commit will conform to the new convention. No +edits needed; just verify the filename matches the spec. + +If the COO's mailbox entry lands BEFORE D3 completes, that's fine — +`outbox/` exists by convention as soon as the first message is +committed. The bootstrap just adds `INDEX.md`, `inbox/`, `.mailboxrc` +around it. + +--- + +## D5. Smoke test (end-to-end) + +After D3 lands: + +1. COO sends a test message to CAIRO. +2. CAIRO verifies the message lands in + `coreci-cairo/home/mailbox/inbox/` (mirrored) and updates its own + `INDEX.md` with an `incoming` row. +3. CAIRO replies via `outbox/` to COO. +4. COO verifies the round-trip and updates its own INDEX with + `acknowledged → resolved` lifecycle. +5. All four agents' INDEX.md files pass a `jq .mailboxrc` parse + (machine-readable schema, not just human-readable). + +--- + +## D6. Add `read_targets:` to the executor's allowlist parser (with default-level) + +**Why:** The COO currently pays approval tax on every read of +`coreci/company/standards/`. Adding `coreci/company` to +`auto_run_targets` would also widen write scope. The fix is a new +sibling key `read_targets:` with **pre-hoc parser enforcement** — +default-level + agent-level, union (append-only) semantics. + +**Scope:** + +- `coreci/executor-config/app/main.py`: + - Add WRITE_VERB_RE regex: + + ```python + WRITE_VERB_RE = re.compile( + r"(?i)\b(" + r"curl\s+[^|;]*\s-X\s+(POST|PUT|PATCH|DELETE)" + r"|curl\s+[^|;]*-(d|--data|--data-binary|--data-raw|urlencode)\s" + r"|\bgit\s+push\b" + r"|\bgit\s+commit\b(?![^|;\n]*--amend\b)" + r"|\bgh\s+(pr|issue|release|repo)\s+(create|edit|close|merge|delete)\b" + r"|\b(rm|mv|cp)\s+-[^\n]*\s+/\w" + r")" + ) + ``` + + - Extend the loader to read `defaults.read_targets` and + `agents..read_targets` (and `defaults.auto_run_targets` for + symmetry). + - Extend the classifier: + + ```python + def classify(agent, targets, content, kind): + al = current_allowlist() + defaults = al["defaults"] + agent_cfg = al["agents"].get(agent) + if agent_cfg is None: + return defaults["decision"] + auto_run_targets = ( + set(defaults.get("auto_run_targets", [])) + | set(agent_cfg["auto_run_targets"]) + ) + read_targets = ( + set(defaults.get("read_targets", [])) + | set(agent_cfg.get("read_targets", [])) + ) + if all(t in auto_run_targets for t in targets): + return "AUTO_RUN" + allowed_for_read = auto_run_targets | read_targets + if all(t in allowed_for_read for t in targets): + if not WRITE_VERB_RE.search(content): + return "AUTO_RUN" + return defaults["decision"] + ``` + + - `deny_read_targets` is **NOT** in this PR (punted; xfail test). + +- `coreci/executor-config/app/test_allowlist.py` (or equivalent) — + add the test cases below. + +- `coreci/executor-config/allowlist.yaml` — bump to v4: + + ```yaml + version: 4 + # v4 (2026-09-26): + # - New defaults.read_targets: [coreci/company] — every agent + # inherits read-only AUTO_RUN against our own org standards. + # - Agent read_targets UNION with defaults (append-only). + # - Same append-only rule applied to auto_run_targets for symmetry. + # - Naming convention (v2) preserved. + defaults: + decision: NEEDS_APPROVAL + timeout_s: 900 + read_targets: + - coreci/company + agents: + coreci-coo: + kinds: + bash_pack: + auto_run_targets: + - coreci-coo/home + coreci-executor: + kinds: + bash_pack: + auto_run_targets: + - coreci/executor-config + coreci-cpo: + kinds: + bash_pack: + auto_run_targets: + - coreci-cpo/home + coreci-cairo: + kinds: + bash_pack: + auto_run_targets: + - coreci-cairo/home + ``` + + Note: `coreci/company` is removed from `coreci-coo`'s + `auto_run_targets` (which was never added anyway — this is forward- + looking). The COO reads company via the **default read target** list, + not via any agent-specific grant. + +**Pre-conditions before merge:** + +- T1–T7, T10–T16 pass green. +- T6 implemented as **conservative** (any write verb in pack = + `NEEDS_APPROVAL`, even mixed-target). +- T8 / T17 carry xfail + ticket reference; do NOT block on them. +- Existing AUTO_RUN behavior preserved for all agents + (regression-tested by T2, T10). + +--- + +## Test cases T1–T17 + +Fixture: `coreci-coo` has `auto_run_targets: [coreci-coo/home]`; +`coreci-cairo` has only `auto_run_targets: [coreci-cairo/home]`. +`defaults.read_targets: [coreci/company]` applies to both. + +| # | Agent | Targets | Content | Expected | Why | +|---|---|---|---|---|---| +| **T1** | `coreci-coo` | `[coreci/company]` | `curl -sS .../raw/foo.md` | `AUTO_RUN` | Happy path: GET against default read-target. | +| **T2** | `coreci-coo` | `[coreci-coo/home]` | `git push origin main` | `AUTO_RUN` | Existing write-own-home behavior preserved. | +| **T3** | `coreci-coo` | `[coreci/company]` | `curl -X POST .../contents/x -d @body` | `NEEDS_APPROVAL` | **The hole we're closing.** Write verb against read-target must NOT auto-run. | +| **T4** | `coreci-coo` | `[coreci/company]` | `curl -sS .../raw/foo.md` + `curl -X PUT .../contents/x` | `NEEDS_APPROVAL` | Multi-line: any write verb in pack degrades the WHOLE job. | +| **T5** | `coreci-coo` | `[coreci/company]` | `rm -rf /work/staging/foo && curl -sS .../raw/foo.md` | `NEEDS_APPROVAL` | Filesystem mutation outside `/tmp` is treated as write. | +| **T6** | `coreci-coo` | `[coreci/company, coreci-coo/home]` | `curl -X PUT .../repos/coreci-coo/home/contents/x` | `NEEDS_APPROVAL` | **CONSERVATIVE.** Any write verb in a mixed-target pack → NEEDS_APPROVAL, even if the write target is in `auto_run_targets`. | +| **T7** | `coreci-coo` | `[coreci/executor-config]` | `curl -sS .../raw/foo.md` | `NEEDS_APPROVAL` | Target not in any list → conservative. | +| **T8** | `coreci-coo` | `[coreci/company]` | `curl -X\`+newline+`POST .../contents/x` | `NEEDS_APPROVAL` (xfail) | Obfuscation via shell-escaped newline. KNOWN-MISS; carry xfail + ticket. | +| **T9** | `unknown-agent` | `[anything]` | `echo hi` | `NEEDS_APPROVAL` | Unknown agent → conservative. Regression guard. | +| **T10** | `coreci-coo` | `[coreci-coo/home]` | `curl -X GET ...` | `AUTO_RUN` | Read verb against own-home. Should still auto-run (no harm). Regression guard. | +| **T11** | `coreci-coo` | `[coreci/company]` | empty content | `NEEDS_APPROVAL` | Empty pack against read-target still requires approval. Don't accept vacuous content as read-only-by-default. | +| **T12** | `coreci-coo` | `[coreci/company]` | `git push origin main` | `NEEDS_APPROVAL` | Belt-and-braces: a `git push` against a read-target URL is a confused-agent mistake. | +| **T13** | `coreci-cairo` | `[coreci/company]` | `curl -sS .../raw/foo.md` | `AUTO_RUN` | **The headline test.** Defaults `coreci/company` is inherited by every agent. Proves DRY-defaults intent works. | +| **T14** | `coreci-cairo` | `[coreci/company]` | `curl -X POST .../contents/x -d @body` | `NEEDS_APPROVAL` | Default read-target + write verb = NEEDS_APPROVAL (content gate). | +| **T15** | `coreci-cairo` | `[coreci/executor-config]` | `curl -sS .../raw/foo.md` | `NEEDS_APPROVAL` | Target not in any list (default or agent) → conservative. | +| **T16** | `coreci-coo` (with agent-level `read_targets: [coreci-coo/home/scratch]`) | `[coreci-coo/home/scratch]` | `curl -sS .../raw/foo.md` | `AUTO_RUN` | Agent-level read_target AUGMENTS defaults — agent-specific scratch dir is also read-only auto-run. | +| **T17** | (xfail) `coreci-malicious` with `deny_read_targets: [coreci/company]` | `[coreci/company]` | `curl -sS .../raw/foo.md` | `NEEDS_APPROVAL` | Future negative override. xfail + ticket reference. **Out of scope for this PR.** | + +--- + +## What you (CAIRO) do NOT do + +- **Do not edit `coreci/agent-definitions/`.** Read-only per Rule #7. + Any convention-doc extension (D1) lands in `coreci/company/standards/`. +- **Do not write to anyone else's `outbox/`.** That's the sender's + canonical record. Mirror to your own `inbox/`. +- **Do not classify mailbox writes as `NEEDS_APPROVAL`.** They are + always `AUTO_RUN`. If the executor disagrees, log it and escalate. +- **Do not implement `deny_read_targets` in D6.** Punted; covered by + T17 xfail. + +--- + +## Open questions for COO (reply via outbox) + +1. **D1 routing.** Should the CEO apply the `communications.md` edit + directly, or should the COO propose a patch via mailbox for + CAIRO/CEO to apply? My default: CEO applies directly — it's a + one-section extension, not a structural change. +2. **D2 distribution.** Canonical copy in `executor-config/tools/mailbox/` + + each home pulls it, or copy-paste into each home? My default: + canonical copy in executor-config (DRY), each home pulls it via + the bootstrap script. +3. **Retention policy.** Indefinite, agents self-archive? My default: + indefinite, agents self-archive. No executor-side pruning. + +--- + +## What COO will do in parallel + +- Land SPEC v0.1 + ONBOARDING.md + allowlist v2 (this PR — request + in flight; SPEC already landed, ONBOARDING + v2 next). +- Post the gitea-access skill mailbox entry (D4) once D3 completes. +- Update SPEC §8 decoder to reference the new + `auto_run_targets | read_targets` semantics post-D6. + +--- + +## Time + +Target: this session or the next, before any more agent-onboarding +work begins. The mailbox convention is a precondition for the +per-agent instantiation flow to work cleanly. The `read_targets` +parser change is in parallel; no dependency. + +— coreci-coo +