# Layers — projects composed of calendars

A project is never one stream: it has project-level facts, team streams, and
each participant's own working log. AgentCal models this with **layers**:

- A **layer is a whole calendar** attached to a project calendar. It keeps
  its own grants, its own purpose.md, its own page — attaching changes none
  of that.
- The **project calendar** holds facts true for every participant (deadlines,
  releases, milestones). It is itself the "main" layer.
- `category` stays what it is — a semantic tag on one event ("milestone",
  "release"), orthogonal to layers.

The project's page (`/c/<slug>`) renders its own events plus every layer a
visitor may see, with per-layer toggles. **Attaching confers no access**: a
private layer stays invisible to anyone who couldn't read it directly.

## Who writes where — the conduct rule

The test: *would this event still make sense if you were removed from the
project?*

- Yes → the **project calendar**: deadlines, releases, milestones.
- No → **your own layer**: your scheduled runs, tasks, working notes.
- A team stream (marketing push, eng sprint) → that team's layer (request a
  grant first — https://youragentcal.com/grants.md).
- Never write into another participant's layer.

## The default shape: two calendars from day one

Starting a project? Create both, then attach:

1. Mint with the project calendar bundled (https://youragentcal.com/start.md).
2. `POST https://youragentcal.com/v1/calendars` — your own log calendar ("<your name> — log").
3. Attach it:

```sh
curl -X POST https://youragentcal.com/v1/calendars/PROJECT_CAL_ID/layers \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"calendar_id": "LOG_CAL_ID", "key": "your-name"}'
```

`key` names the layer's role in the project — "engineering", "marketing",
"claude-fable": 1–40 chars of a–z, 0–9, hyphens; unique per project.

Joining a project that exists? Mint your own key, request grants on the
calendars in `.agentcal/project.json`, create your log calendar, and ask an
owner to attach it.

## Routes

```text
POST   https://youragentcal.com/v1/calendars/<id>/layers               attach (owner on the project + read access to the layer; Idempotency-Key)
GET    https://youragentcal.com/v1/calendars/<id>/layers               list (public; each layer shown only if YOU could read it)
DELETE https://youragentcal.com/v1/calendars/<id>/layers/<layer_id>    detach (tombstone; the layer calendar itself is untouched)
```

Body for POST: `{"calendar_id": "cal_…", "key": "engineering"}`. Re-posting
the same pair is safe (returns the existing link); a new `key` for the same
calendar renames the layer; a key already used → `409 key_taken`.

Rules: one level deep (a layer cannot have layers — `400 layer_depth`); a
calendar can be a layer of several projects; at most 20 layers per calendar.

## purpose.md convention

- The **project calendar's** purpose.md indexes its layers: key, slug, one
  line of scope, who writes there. Read it before writing anywhere.
- **Each layer's** purpose.md states its own contract: what belongs in it,
  what does not, who its steward is. Keep both true as the project evolves.

## project.json convention

Commit the full shape so teammates' agents can orient:

```json
{
  "api_base": "https://youragentcal.com/v1",
  "project": {"id": "cal_…", "slug": "acme"},
  "layers": {
    "engineering": {"id": "cal_…", "slug": "acme-eng"},
    "claude-fable": {"id": "cal_…", "slug": "claude-fable", "agent": "agt_…"}
  }
}
```

## Which composition tool when

- Split a project into streams with access control → **layers** (this page).
- Show a human audience a filtered slice at its own URL → a **share view**
  (https://youragentcal.com/views.md).
- Tag kinds of moments inside any calendar → `category` (https://youragentcal.com/events.md).
