# Task 1974 — uploads/ intake inbox Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Reclassify the account `uploads/` directory from tool-owned scratch to a temporary intake inbox, and add a `stranded-intake` reconcile count so a node file-reference left pointing into `uploads/` is visible.

**Architecture:** Doctrine plus audit change, no code path change. Three prose files are edited (`SCHEMA.md`, `data-manager.md`, `internals.md`), then the `platform-architecture` SKILL.md twin is regenerated from the docs corpus so the no-drift gate passes. Detection-only, mirroring the outbound `output/` rule landed by task 1930.

**Tech Stack:** Markdown doctrine files; Node regen scripts (`docs/scripts/copy-docs.mjs`, `maxy-code/scripts/assemble-architecture-skill.mjs`); the drift gate `maxy-code/platform/scripts/check-architecture-skill-no-drift.mjs`; the bash test `fs-schema-guard.test.sh`.

## Global Constraints

- Zero em-dashes in all shipped prose.
- The 1930 outbound rule must survive intact: the `output/` promote-out sentence and the `scratch-refs` / `stranded` counts stay present. `stranded-intake` is appended to the output contract, never a rename of `stranded`.
- `uploads` stays in the `allowed-top-level` block of SCHEMA.md (files still land there).
- The write-path guard, the admin-UI upload handler, and `memory-ingest` `sourcePath` behaviour are unchanged.
- Reconcile output contract after this task: `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R`.
- Paths are relative to the `maxy-code/` subtree root inside the worktree.

---

### Task 1: SCHEMA.md — reclassify uploads/ as intake inbox

**Files:**
- Modify: `platform/templates/account-schema/SCHEMA.md:31-39`

**Interfaces:**
- Produces: the doctrine text data-manager.md and internals.md refer to. No code symbols.

- [ ] **Step 1: Remove `uploads/` from the "recreated on the next write" list.**

At `SCHEMA.md:36-39`, change the list so `uploads/` is no longer among tool-owned rebuilt dirs:

```
The internal structure of these is owned by the writing tool and is recreated on
the next write: `url-get/`, `output/`, `generated/`, `extracted/`,
any published/served tree (`sites/`, `public/`), `agents/`, `specialists/`, and
platform-internal areas (`cache/`, `secrets/`, `state/`, `logs/`, `tmp/`).
```

- [ ] **Step 2: Add the intake-inbox rule paragraph.**

Immediately after the `output/` promote-out paragraph (ends at `SCHEMA.md:34`), insert a new paragraph. It states uploads is a temporary intake inbox, a file is promoted to its canonical bucket when its node is created, no persisted graph reference points into `uploads/`, and the per-`<attachmentId>` reason it is safe to move out of (unlike `output/`):

```
`uploads/` is a temporary intake inbox, not rebuilt scratch. A file lands at
`uploads/<attachmentId>/` on upload and is promoted to its canonical entity
bucket when its graph node is created: a Person or Organization file to
`contacts/<name>/`, a Project file to `projects/<name>/`, anything else to
`documents/`. A persisted graph reference must never point into `uploads/`.
Unlike `output/`, each upload writes its own `uploads/<attachmentId>/` dir and no
tool walks or rebuilds the subtree, so promoting a file out of it through the
`data-manager` specialist is safe.
```

- [ ] **Step 3: Verify the edit.**

Run:
```bash
cd platform/templates/account-schema
# uploads no longer in the recreated-on-next-write list
awk '/recreated on/,/logs.*tmp/' SCHEMA.md | grep -c 'uploads/' | grep -qx 0 && echo "OK: uploads removed from scratch list"
# intake-inbox rule present
grep -q 'temporary intake inbox' SCHEMA.md && echo "OK: intake rule present"
# uploads still allowed-top-level
grep -A40 'allowed-top-level' SCHEMA.md | grep -qx 'uploads' && echo "OK: uploads still allowed-top-level"
# 1930 output/ rule survives
grep -q 'promoted out of `output/`' SCHEMA.md && echo "OK: 1930 output rule intact"
# zero em-dashes in this file
! grep -q '—' SCHEMA.md && echo "OK: no em-dash"
```
Expected: all five OK lines.

- [ ] **Step 4: Commit.**

```bash
git add platform/templates/account-schema/SCHEMA.md
git commit -m "task: reclassify uploads/ as intake inbox in account SCHEMA.md (1974)"
```

---

### Task 2: data-manager.md — reconcile case (e), walk uploads/, promotion permission, output contract

**Files:**
- Modify: `platform/templates/specialists/agents/data-manager.md:24` (reconcile brief), `:30` (output contract), and the "What success looks like" area for the promotion permission.

**Interfaces:**
- Consumes: the SCHEMA.md intake-inbox rule from Task 1.
- Produces: the `stranded-intake=R` field that internals.md documents in Task 3.

- [ ] **Step 1: Add reconcile case (e) and include uploads/ in the walk.**

At `data-manager.md:24`, extend the counted list. After case (d), add case (e): a graph file-reference that resolves into `uploads/`, the inbound intake mirror of the `output/` scratch case. State that the walk now includes `uploads/` (a file under `uploads/` with no referencing node folds into `unreachable`). Exact appended clause inside the sentence that enumerates (a) to (d):

```
, and (e) graph file-references that resolve into the `uploads/` intake inbox,
which is a temporary landing area a file should have been promoted out of when
its node was created. The walk now includes `uploads/`; a file under `uploads/`
that no node references counts under (a) unreachable.
```

- [ ] **Step 2: Add the stewardship promotion permission for uploads/.**

In "## What success looks like" (after `data-manager.md:16`, the paired-move paragraph), add a sentence permitting promotion out of `uploads/`:

```
- A file in the `uploads/` intake inbox whose graph node exists is promoted to
  its canonical bucket (Person or Organization to `contacts/<name>/`, Project to
  `projects/<name>/`, else `documents/`), paired with a `database-operator`
  `sourcePath` update in the same pass, exactly as any other move.
```

- [ ] **Step 3: Change the output contract.**

At `data-manager.md:30`, change the reconcile summary line:

```
For the reconcile audit: `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R`.
```

- [ ] **Step 4: Verify the edit.**

Run:
```bash
cd platform/templates/specialists/agents
grep -q 'stranded-intake=R' data-manager.md && echo "OK: contract has stranded-intake"
grep -q 'scratch-refs=P stranded=Q' data-manager.md && echo "OK: 1930 fields survive"
grep -q '(e) graph file-references that resolve into the `uploads/`' data-manager.md && echo "OK: case e present"
grep -q 'promoted to' data-manager.md && grep -q 'sourcePath` update' data-manager.md && echo "OK: promotion permission present"
! grep -q '—' data-manager.md && echo "OK: no em-dash"
```
Expected: five OK lines.

- [ ] **Step 5: Commit.**

```bash
git add platform/templates/specialists/agents/data-manager.md
git commit -m "task: data-manager reconcile counts uploads/ intake drift as stranded-intake (1974)"
```

---

### Task 3: internals.md — document the reclassification and the stranded-intake count

**Files:**
- Modify: `platform/plugins/docs/references/internals.md:411`

**Interfaces:**
- Consumes: the reconcile contract from Task 2.
- Produces: the corpus text the architecture SKILL.md twin regenerates from in Task 4.

- [ ] **Step 1: Append the intake-inbox rule to the placement paragraph.**

At `internals.md:411`, the sentence currently ends: "...is counted by the `data-manager` reconcile audit (`scratch-refs`, `stranded`); making that audit run on a standing periodic cadence is a filed follow-up." Extend it with the inbound mirror. Append after the `scratch-refs`/`stranded` clause:

```
The inbound mirror is the `uploads/` intake inbox: an uploaded file lands at `uploads/<attachmentId>/` and is promoted to its canonical entity bucket when its graph node is created, so a persisted node reference resolving into `uploads/` is intake drift, counted by the same reconcile audit as `stranded-intake`. Unlike `output/`, `uploads/` is never rebuilt by a tool, so the `data-manager` may promote a file out of it. The reconcile output contract is `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R`.
```

- [ ] **Step 2: Verify the edit.**

Run:
```bash
grep -q 'stranded-intake' platform/plugins/docs/references/internals.md && echo "OK: internals mentions count"
grep -q 'intake inbox' platform/plugins/docs/references/internals.md && echo "OK: internals mentions reclassification"
! grep -q '—' platform/plugins/docs/references/internals.md && echo "OK: no em-dash added"
```
Expected: three OK lines. (The last check should already pass before the edit; keep the added prose em-dash-free.)

- [ ] **Step 3: Commit.**

```bash
git add platform/plugins/docs/references/internals.md
git commit -m "task: internals.md documents uploads/ intake inbox and stranded-intake count (1974)"
```

---

### Task 4: Regenerate the architecture skill twin and pass the gates

**Files:**
- Modify (generated): `platform/plugins/admin/skills/platform-architecture/SKILL.md`
- Run: `docs/scripts/copy-docs.mjs`, `maxy-code/scripts/assemble-architecture-skill.mjs`, `platform/scripts/check-architecture-skill-no-drift.mjs`, `platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh`

**Interfaces:**
- Consumes: the edited internals.md from Task 3.
- Produces: a SKILL.md byte-equal to what the gate regenerates.

- [ ] **Step 1: Regenerate the corpus and the skill twin.**

Run from the worktree repo root:
```bash
node docs/scripts/copy-docs.mjs
node maxy-code/scripts/assemble-architecture-skill.mjs --brand maxy-code
```
Expected: `[copy-docs]` and `[platform-arch-assemble] op=write` lines, no error.

- [ ] **Step 2: Run the drift gate.**

Run:
```bash
node maxy-code/platform/scripts/check-architecture-skill-no-drift.mjs --brand maxy-code
```
Expected: exit 0, no drift reported. If it reports drift, the regen in Step 1 did not run or the SKILL.md was not re-written; re-run Step 1.

- [ ] **Step 3: Run the write-guard test (regression boundary).**

Run:
```bash
bash maxy-code/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh
```
Expected: all assertions pass. The guard is unchanged; `uploads/` is still an allowed write, so this stays green.

- [ ] **Step 4: Commit the regenerated twin (and any corpus artefact the gate expects committed).**

```bash
git add platform/plugins/admin/skills/platform-architecture/SKILL.md
git status --porcelain docs/public
# stage docs/public/llms-full.*.txt only if it is a tracked artefact that changed
git commit -m "task: regenerate platform-architecture skill twin for 1974"
```

---

## Land (Phase 6 handles this — not an implementation task)

LANES.md and task-file archival are done in Phase 6c of the sprint, not here. The task's "LANES Ready row" line describes the pre-implementation state; because this sprint implements and archives in one motion, the end state is: task file under `.tasks/archive/`, no Ready row referencing 1974, and the archive sentence naming 1974. This is a deliberate deviation from the task's literal LANES line, recorded in Plan Conformity.

## Self-Review

- **Spec coverage:** SCHEMA.md reclassification (Task 1), data-manager.md case (e) + walk + promotion + contract (Task 2), internals.md + twin regen (Tasks 3-4), LANES (Phase 6c). Every in-scope item maps to a task.
- **Out of scope confirmed untouched:** no auto-promotion hook, no cadence (1931), no CC-native uploads reach, no write-guard edit, no ingest-path edit.
- **Type consistency:** the single shared token is the output-contract string; it reads `unreachable=N broken-refs=M scratch-refs=P stranded=Q stranded-intake=R` identically in Tasks 2 and 3.
- **Placeholder scan:** none; every edit carries its literal text.
