tasks: archive CAIRO D1-D6 task brief (per-home mailbox + read_targets parser)
This commit is contained in:
@@ -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-<agent>/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__<sender>__<recipient>__<short-slug>.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-<role>`.
|
||||
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/<agent>/home/scripts/bootstrap-mailbox.sh`.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# bootstrap-mailbox.sh — create per-home mailbox for one agent
|
||||
# Usage: AGENT=coreci-<role> ./bootstrap-mailbox.sh
|
||||
set -euo pipefail
|
||||
: "${AGENT:?must set AGENT=coreci-<role>}"
|
||||
REPO="coreci/${AGENT}/home"
|
||||
|
||||
echo "[mailbox] bootstrapping $REPO/mailbox"
|
||||
mkdir -p outbox inbox scripts
|
||||
|
||||
# INDEX.md
|
||||
cat > INDEX.md <<EOF
|
||||
# $AGENT — Mailbox Index
|
||||
|
||||
> 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 <<EOF
|
||||
{
|
||||
"agent": "$AGENT",
|
||||
"schema": 1,
|
||||
"outbox": "outbox/",
|
||||
"inbox": "inbox/",
|
||||
"index": "INDEX.md",
|
||||
"convention_doc": "coreci/company/standards/communications.md#mailbox-formal-layer--email-like"
|
||||
}
|
||||
EOF
|
||||
|
||||
# Move the script into scripts/ if it isn't already there
|
||||
if [ "$0" != "./scripts/bootstrap-mailbox.sh" ] && [ -f "$0" ]; then
|
||||
mv "$0" scripts/bootstrap-mailbox.sh 2>/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-<role>`.
|
||||
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.<name>.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
|
||||
|
||||
Reference in New Issue
Block a user