---
title: paths
---

## Interfaces

### PlacementInput

What [placementFor](/api/contracts/paths/#placementfor) decides from. `path` is the caller's explicit override.

Every optional field also admits `undefined` explicitly. Under
`exactOptionalPropertyTypes` a bare `path?: string` would refuse
`placementFor({ path: params.path, … })` for a `string | undefined` param, forcing every
tool adapter to strip absent fields by hand. The contract therefore accepts the shape the
MCP and CLI layers actually hold.

#### Properties

##### entities?

```ts
readonly optional entities?: readonly string[];
```

##### memoryType

```ts
readonly memoryType: string;
```

##### path?

```ts
readonly optional path?: string;
```

##### tags?

```ts
readonly optional tags?: readonly string[];
```

##### workspace?

```ts
readonly optional workspace?: string;
```

## Variables

### ARCHIVE\_BUCKET

```ts
const ARCHIVE_BUCKET: "archive" = "archive";
```

The bucket eviction moves into, partitioned by year.

***

### ARCS\_DIR

```ts
const ARCS_DIR: "areas/arcs" = "areas/arcs";
```

Behavioral arcs. Under `areas/` because PARA is fixed at four buckets.

***

### INBOX\_DIR

```ts
const INBOX_DIR: "areas/inbox" = "areas/inbox";
```

Where a memory lands when no rule claims it. `memhtml doctor` reports inbox depth as a
health signal, so an unplaceable memory is visible rather than lost.

***

### MEMORY\_EXTENSION

```ts
const MEMORY_EXTENSION: ".html" = ".html";
```

The file extension every memory carries.

***

### PEOPLE\_DIR

```ts
const PEOPLE_DIR: "resources/people" = "resources/people";
```

The person plane, folded into `resources/` rather than given its own bucket.

***

### TASKS\_SUBDIR

```ts
const TASKS_SUBDIR: "tasks" = "tasks";
```

The directory segment every task file sits under, appended to its workspace's project
directory or to the inbox.

A subdirectory rather than a fifth bucket: PARA is fixed at four, and a task belongs to
whatever the memory beside it belongs to. Keeping tasks in one named segment is what makes
`ls projects/<slug>/tasks` the list operation, which is the design's
CRUDL-without-retrieval contract. The segment name is therefore part of the contract.

## Functions

### archivePathFor()

```ts
function archivePathFor(path, year): string;
```

The archive path a memory moves to on eviction: `archive/<YYYY>/<original-path>`, with the
original path mirrored exactly beneath the year.

`year` is a calendar year (a label, not an offset). Mirroring the whole original path is
what makes the mapping injective and invertible, so `git log --follow` reads through the
move and `diff -M` reports it as `R100` rather than a delete plus an add.

#### Parameters

##### path

`string`

##### year

`number`

#### Returns

`string`

***

### archiveYearOf()

```ts
function archiveYearOf(path): number | undefined;
```

The archive year of an archive path, or `undefined` when the path is not archived.

#### Parameters

##### path

`string`

#### Returns

`number` | `undefined`

***

### isArchivePath()

```ts
function isArchivePath(path): boolean;
```

True when a path sits under the archive bucket with a year partition.

#### Parameters

##### path

`string`

#### Returns

`boolean`

***

### isValidMemoryPath()

```ts
function isValidMemoryPath(path): boolean;
```

True when a path is a usable memory path: rooted in a PARA bucket, ending in `.html`,
carrying no `.` or `..` segment.

[memoryPathViolation](/api/contracts/paths/#memorypathviolation) asked as a boolean, so the predicate and the refusal's reason cannot
disagree about which paths are usable.

#### Parameters

##### path

`string`

#### Returns

`boolean`

***

### memoryPathFor()

```ts
function memoryPathFor(input): string;
```

The full path for a new memory. An explicit valid `path` is authoritative and returned
verbatim in canonical form; otherwise the directory comes from [placementFor](/api/contracts/paths/#placementfor) and
the filename from [filenameFor](/api/contracts/slug/#filenamefor), which date-prefixes an episodic entry.

#### Parameters

##### input

[`PlacementInput`](/api/contracts/paths/#placementinput) & `object`

#### Returns

`string`

***

### memoryPathViolation()

```ts
function memoryPathViolation(path): string | undefined;
```

Why a path is not a usable memory path, or `undefined` when it is one.

The RULE and its explanation in one function, because a refusal that restated the rule in its own
words would be a second copy of it, free to name a clause this function does not check. A
caller that refuses an unusable path quotes this string; [isValidMemoryPath](/api/contracts/paths/#isvalidmemorypath) is the same
question asked as a boolean.

Each clause names the input it saw rather than the rule in the abstract, so a caller holding the
message can act without re-reading the format doc. The traversal clause is the security one: it is
what keeps a caller-supplied path from escaping the memory repo.

#### Parameters

##### path

`string`

#### Returns

`string` | `undefined`

***

### normalizePath()

```ts
function normalizePath(path): string;
```

Reduce a caller-supplied path to the canonical git-tree form: leading slashes dropped
(callers may pass the `<link href>` document-reference form), repeated slashes collapsed,
trailing slash dropped.

The trailing slash is removed by `endsWith`/`slice` rather than by `//+$/`, which is not a
style choice: an unanchored-left `/+$` is quadratic on a long run of slashes followed by a
non-slash, measured 2026-08-18 at 4 ms / 57 ms / 769 ms / 3049 ms for n = 2k / 8k / 32k / 64k.
The collapse above happens to defuse it — after it, no run of two slashes survives, so the
pattern can only ever match one character — but that made the cost of this function depend on
the ORDER of three chained calls, with nothing stating it and nothing checking it. Reordering
them, or dropping the collapse, would reintroduce a multi-second stall on 64 KB of input across
this function's callers, all of which sit on the write path that accepts agent-supplied paths.
A single-character slice cannot be reordered into a hazard.
`packages/contracts/tests/paths.test.ts` asserts the cost curve at adversarial sizes.

#### Parameters

##### path

`string`

#### Returns

`string`

***

### originalPathFor()

```ts
function originalPathFor(archivePath): string | undefined;
```

The pre-eviction path behind an archive path, or `undefined` when the path is not an
archive path. Strips exactly one `archive/<YYYY>/` prefix, so it is the left inverse of
[archivePathFor](/api/contracts/paths/#archivepathfor) even for a memory archived twice.

#### Parameters

##### archivePath

`string`

#### Returns

`string` | `undefined`

***

### paraBucketOf()

```ts
function paraBucketOf(path): "projects" | "areas" | "resources" | "archive" | undefined;
```

The PARA bucket a path sits in, or `undefined` when it sits outside all four.

#### Parameters

##### path

`string`

#### Returns

`"projects"` | `"areas"` | `"resources"` | `"archive"` | `undefined`

***

### placementFor()

```ts
function placementFor(input): string;
```

The directory a memory belongs in, following design §2.1's six rules in order, with the
`task` rule sitting between the arc rule and the person rule. Total: it always returns a
directory rooted in a PARA bucket, so the write path never guesses twice and never fails.

Returns the *directory*, not the full path, because the filename needs a title this input
does not carry. [memoryPathFor](/api/contracts/paths/#memorypathfor) composes the two. An explicit `path` contributes
its directory; when that path is unusable it is ignored rather than propagated, so the
return stays a valid bucket and this function stays total.

Totality is why the refusal cannot live here. A caller that wants an unusable path refused
rather than re-derived asks the store for it (`strictPath` on a `WriteInput`), which gates on
[memoryPathViolation](/api/contracts/paths/#memorypathviolation) before any of this runs and quotes its reason. Refusing here instead
would make placement fallible for every caller, including the sleep phases that place a synthesized
arc and have no caller path to be wrong about.

#### Parameters

##### input

[`PlacementInput`](/api/contracts/paths/#placementinput)

#### Returns

`string`