# workspace-export

Reference for walking a Notion "Export all workspace content" directory.

## What a Notion export looks like

Notion's native export ("Settings → Workspace → Export all workspace content" with "Everything", "Markdown & CSV" format) produces a zip. Unzipped, the top level is a single directory named after the workspace plus a Notion UUID; nested inside is the page tree as the operator structured it.

Two file shapes occur at every depth:

- **Pages** — `Page Title <32-hex-notionId>.md`. The basename is the human title followed by the 32-character hex Notion ID (no dashes). The file body is markdown.
- **Databases** — `Database Name <32-hex-notionId>.csv` paired with `Database Name <32-hex-notionId>/` directory. The CSV is the table view; the directory contains one `.md` per row plus an attachments tree.

Nested page subtrees: a page with children becomes a directory `Page Title <notionId>/` alongside its `.md` file; the directory holds the child pages and their own subdirectories recursively.

## What the walker does

1. **Read the root.** First-level entries are the top-level pages and databases of the workspace.
2. **Recurse depth-first** into each subdirectory, classifying entries on each pass.
3. **Build the candidate manifest** — a flat list of `{kind, path, notionId, parentNotionId, depth}` records, one per page or row. This is the manifest the selective-ingest gate runs against, and the manifest the write loop consumes.
4. **Apply the operator's filter** (top-level subtree, database name, last-edited date) by predicate-matching the manifest before any write begins.
5. **Hand off** the filtered manifest to [page-tree.md](page-tree.md) for pages and [databases.md](databases.md) for database tables and rows. Attachments are handed off as referenced from pages, not walked separately — see [attachments.md](attachments.md).

## Notion ID extraction

The Notion ID is the last 32 hex characters of the basename, after the trailing space. Strip the `.md` / `.csv` suffix and the trailing space, then take the last 32 chars; the remainder is the human title. Notion IDs are stable across exports; they are the natural key the skill MERGEs on.

## What the walker does NOT do

- **Does not** read page or row body content. That happens at write time, per the page/database references.
- **Does not** decide labels. Label selection happens in [databases.md](databases.md).
- **Does not** open attachments. Hashing happens at the attachment-copy step.
- **Does not** retry on partial exports. If a referenced child directory is missing, the parent page is still imported and the gap is logged.

## Failure modes

- `[notion-import:error] reason=basename-no-notion-id file=<path>` — basename does not end in a 32-hex Notion ID. Skip the file, continue the walk.
- `[notion-import:error] reason=orphan-row file=<path>` — a row markdown lives inside a directory whose parent CSV is missing. Skip; the row cannot be typed without the schema.
- `[notion-import:error] reason=zip-not-extracted path=<path>` — the operator pointed at a `.zip`. Unzip first; the skill does not call `unzip`.

The walker never aborts on a single file's failure. Counts of skipped files appear in the completion log.
