> ## Documentation Index
> Fetch the complete documentation index at: https://usefused.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# harnest.authoring

> Public Python signatures, fields, and methods.

[All Python modules](https://usefused.com/docs/harnest/reference/api/overview.md) · [Import and compatibility guidance](https://usefused.com/docs/harnest/build/python-imports.md)

## Pack configuration

| Property | Required | Contract |
| - | - | - |
| `name` | Yes | A unique kebab-case identifier beginning with a letter, up to 63 characters. `harnest` is reserved. |
| `schema_version` | Yes | A positive integer identifying the company configuration schema. Track it separately from your package release version. |
| `options` | No | A Pydantic model for caller-supplied inputs. |
| `templates` | No | A directory containing inert UTF-8 [templates](https://usefused.com/docs/harnest/build/project-packs/changes.md#templates). |
| `config_file` | With `config_model` | The project-relative company configuration path. |
| `config_model` | With `config_file` | A Pydantic model that validates the resulting configuration mapping. |

## File operations

| Method | Result |
| - | - |
| `context.files.read_text(path)` | Read an existing staged UTF-8 file. Missing files raise `ProjectError`. |
| `context.files.write_text(path, content, policy=...)` | Propose file creation or a managed replacement. |
| `context.files.from_template(path, template=..., values=..., policy=...)` | Read an inert UTF-8 template and propose its rendered content. |
| `context.files.from_file(path, source=..., policy=...)` | Copy a packaged file unchanged, including binary documents and assets. |
| `context.files.delete(path)` | Remove an unchanged file owned by the pack. An absent file is a no-op. |

## YAML operations

| Method | Result |
| - | - |
| `context.yaml.read(path)` | Return a detached mapping from staged YAML; return `{}` for an absent file. |
| `context.yaml.set(path, key=(...), value=..., policy=...)` | Set a JSON-compatible value while retaining unrelated keys. |
| `context.yaml.rename_key(path, source=(...), destination=(...))` | Move the current value to an unoccupied destination. |

## Initializer contract

```python theme={null}
@pack.initialize
def initialize(context: ProjectContext) -> ChangePlan:
    ...
```

| Rule | Behavior |
| - | - |
| Registration | One initializer per pack. Registering another raises `ProjectError`. |
| Function | A synchronous callable accepting one `ProjectContext` and returning `ChangePlan`. |
| Timing | Runs during init planning, including `--dry-run`. It does not write to the live project. |
| Version | New projects start at the installed pack's current `schema_version`. |
| Upgrades | Never runs during upgrade. Use [`@pack.migration`](https://usefused.com/docs/harnest/build/project-packs/migrations). |
| Optional hook | A pack can omit its initializer. Any declared configuration model must still validate. |

## Hook context

| Member | What it provides |
| - | - |
| `context.name` | The requested project directory's final path component. |
| `context.options` | Validated Pydantic options, or `None` if the pack declares none. |
| `context.files` | Read staged UTF-8 files and propose file/template operations. |
| `context.yaml` | Read staged mappings and propose field changes. |

## Migration contract

```python theme={null}
@pack.migration(from_version=1, to_version=2)
def migrate(context: ProjectContext) -> ChangePlan:
    ...
```

| Parameter or rule | Contract |
| - | - |
| `from_version` | A positive integer schema version. |
| `to_version` | Exactly `from_version + 1`, no greater than `pack.schema_version`. |
| Function | A synchronous callable accepting `ProjectContext` and returning `ChangePlan`. |
| Registration | One migration per source version. Duplicate registrations are rejected. |
| Timing | Runs during upgrade planning, including previews. |
| New projects | Skip historical migrations; their initializer must create the current schema directly. |

## Plan results

| Plan member | Purpose |
| - | - |
| `root` | The resolved target directory. |
| `changes` | Net file changes: `ChangeKind.CREATE`, `UPDATE`, or `DELETE`, with paths and owners. |
| `blockers` | Problems that prevent the entire plan from applying. |
| `render()` | A readable combined summary. |
| `public()` | A JSON-compatible summary without file contents or option values. |

## Exports

| Name | Kind |
| - | - |
| [`ChangeKind`](#changekind) | Class |
| [`ChangePlan`](#changeplan) | Class |
| [`ProjectCLI`](#projectcli) | Class |
| [`ProjectChange`](#projectchange) | Class |
| [`ProjectContext`](#projectcontext) | Class |
| [`ProjectError`](#projecterror) | Class |
| [`ProjectPack`](#projectpack) | Class |
| [`ProjectPlan`](#projectplan) | Class |
| [`ProjectPlanner`](#projectplanner) | Class |
| [`WritePolicy`](#writepolicy) | Class |
| [`apply_project_plan`](#apply_project_plan) | Function |

## ChangeKind

Filesystem effects exposed in a reviewable project plan.

```python theme={null}
ChangeKind(*values)
```

| Member | Value |
| - | - |
| `CREATE` | `'create'` |
| `UPDATE` | `'update'` |
| `DELETE` | `'delete'` |

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import ChangeKind
>
> value = ChangeKind.CREATE
> ```

## ChangePlan

An ordered collection of file and YAML operations returned by a pack hook.

```python theme={null}
ChangePlan(*operations: _Operation) -> None
```

Fields (`—` means no declared default):

| Field | Type | Default |
| - | - | - |
| `operations` | `tuple[_Operation, ...]` | `—` |

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `*operations` | `_Operation` | Optional |
>
> ```python theme={null}
> from harnest.authoring import ChangePlan
>
> plan = ChangePlan()
> ```

## ProjectCLI

Embed init/upgrade planning and custom commands in a company-owned executable.

```python theme={null}
ProjectCLI(
    name: str,
    packs: Sequence[ProjectPack],
    *,
    harnest_command: Sequence[str] = ('harnest',),
) -> None
```

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `name` | `str` | Required |
> | `packs` | `Sequence[ProjectPack]` | Required |
> | `harnest_command` | `Sequence[str]` | `('harnest',)` |
>
> ```python theme={null}
> from harnest.authoring import ProjectCLI
>
> cli = ProjectCLI(name=..., packs=...)
> ```

### ProjectCLI.add\_command

```python theme={null}
ProjectCLI.add_command(name: str, handler: Callable[[argparse.Namespace], int], *, help: str = '') -> argparse.ArgumentParser
```

Register a company command and return its parser for company-specific flags.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `name` | `str` | Required |
> | `handler` | `Callable[[argparse.Namespace], int]` | Required |
> | `help` | `str` | `''` |
>
> ```python theme={null}
> from harnest.authoring import ProjectCLI
>
> cli: ProjectCLI
> result = cli.add_command(name=..., handler=...)
> ```

### ProjectCLI.add\_studio

```python theme={null}
ProjectCLI.add_studio(
    *,
    command: list[str] | tuple[str, ...],
    packs: Sequence[Path] = (),
    trusted_ui: Sequence[str] = (),
    init_args: Sequence[str] = (),
) -> None
```

Expose Studio and core commands through an explicit company executable argv.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `command` | `list[str] \| tuple[str, ...]` | Required |
> | `packs` | `Sequence[Path]` | `()` |
> | `trusted_ui` | `Sequence[str]` | `()` |
> | `init_args` | `Sequence[str]` | `()` |
>
> ```python theme={null}
> from harnest.authoring import ProjectCLI
>
> cli: ProjectCLI
> cli.add_studio(command=...)
> ```

### ProjectCLI.run

```python theme={null}
ProjectCLI.run(
    arguments: Sequence[str] | None = None,
    *,
    stdout: TextIO | None = None,
    stderr: TextIO | None = None,
) -> int
```

Execute one command and return a process exit code; never exit the caller.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `arguments` | `Sequence[str] \| None` | `None` |
> | `stdout` | `TextIO \| None` | `None` |
> | `stderr` | `TextIO \| None` | `None` |
>
> ```python theme={null}
> from harnest.authoring import ProjectCLI
>
> cli: ProjectCLI
> result = cli.run()
> ```

## ProjectChange

A path-level change summary which never exposes configuration contents.

```python theme={null}
ProjectChange(kind: ChangeKind, path: str, owners: tuple[str, ...]) -> None
```

Fields (`—` means no declared default):

| Field | Type | Default |
| - | - | - |
| `kind` | `ChangeKind` | `—` |
| `path` | `str` | `—` |
| `owners` | `tuple[str, ...]` | `—` |

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `kind` | `ChangeKind` | Required |
> | `path` | `str` | Required |
> | `owners` | `tuple[str, ...]` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectChange
>
> change = ProjectChange(kind=..., path=..., owners=...)
> ```

## ProjectContext

Pack hook inputs and read-only staged project views; callbacks are trusted code.

```python theme={null}
ProjectContext(name: str, options: BaseModel | None, files: ProjectFiles, yaml: ProjectYAML) -> None
```

Fields (`—` means no declared default):

| Field | Type | Default |
| - | - | - |
| `name` | `str` | `—` |
| `options` | `BaseModel \| None` | `—` |
| `files` | `ProjectFiles` | `—` |
| `yaml` | `ProjectYAML` | `—` |

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `name` | `str` | Required |
> | `options` | `BaseModel \| None` | Required |
> | `files` | `ProjectFiles` | Required |
> | `yaml` | `ProjectYAML` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context = ProjectContext(name=..., options=..., files=..., yaml=...)
> ```

## ProjectError

A project cannot be safely planned, validated, or changed.

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import ProjectError
>
> try:
>     ...
> except ProjectError as error:
>     ...
> ```

## ProjectPack

Company-owned scaffolding and consecutive schema migrations, never auto-discovered.

```python theme={null}
ProjectPack(
    name: str,
    schema_version: int,
    *,
    options: type[BaseModel] | None = None,
    templates: str | Path | None = None,
    config_file: str | None = None,
    config_model: type[BaseModel] | None = None,
) -> None
```

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `name` | `str` | Required |
> | `schema_version` | `int` | Required |
> | `options` | `type[BaseModel] \| None` | `None` |
> | `templates` | `str \| Path \| None` | `None` |
> | `config_file` | `str \| None` | `None` |
> | `config_model` | `type[BaseModel] \| None` | `None` |
>
> ```python theme={null}
> from harnest.authoring import ProjectPack
>
> pack = ProjectPack(name=..., schema_version=...)
> ```

### ProjectPack.initialize

```python theme={null}
ProjectPack.initialize(function: Hook) -> Hook
```

Register the single initializer used only when creating a new project.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `function` | `Hook` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectPack
>
> pack: ProjectPack
> @pack.initialize
> def my_function(context):
>     ...
> ```

### ProjectPack.migration

```python theme={null}
ProjectPack.migration(*, from_version: int, to_version: int) -> Callable[[Hook], Hook]
```

Register one consecutive migration; upgrades never rerun initialization.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `from_version` | `int` | Required |
> | `to_version` | `int` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectPack
>
> pack: ProjectPack
> @pack.migration(from_version=..., to_version=...)
> def my_function():
>     ...
> ```

## ProjectPlan

A combined immutable snapshot plan; apply refuses blockers or changed inputs.

Obtain this object from the owning Harnest API; its constructor includes runtime bindings.

Fields (`—` means no declared default):

| Field | Type | Default |
| - | - | - |
| `root` | `Path` | `—` |
| `changes` | `tuple[ProjectChange, ...]` | `—` |
| `blockers` | `tuple[str, ...]` | `—` |

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import ProjectPlan
>
> plan: ProjectPlan  # returned by Harnest, not constructed directly
> ```

### ProjectPlan.public

```python theme={null}
ProjectPlan.public() -> dict[str, Any]
```

Return a machine-readable preview without file contents or option values.

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import ProjectPlan
>
> plan: ProjectPlan
> result = plan.public()
> ```

### ProjectPlan.render

```python theme={null}
ProjectPlan.render() -> str
```

Render the same combined plan used by interactive and automated clients.

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import ProjectPlan
>
> plan: ProjectPlan
> result = plan.render()
> ```

## ProjectPlanner

Plan init and upgrade using explicitly supplied trusted project packs.

```python theme={null}
ProjectPlanner(packs: Sequence[ProjectPack], *, harnest_command: Sequence[str] = ('harnest',)) -> None
```

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `packs` | `Sequence[ProjectPack]` | Required |
> | `harnest_command` | `Sequence[str]` | `('harnest',)` |
>
> ```python theme={null}
> from harnest.authoring import ProjectPlanner
>
> planner = ProjectPlanner(packs=...)
> ```

### ProjectPlanner.plan\_init

```python theme={null}
ProjectPlanner.plan_init(
    directory: str | Path,
    *,
    options: Mapping[str, Mapping[str, Any]] | None = None,
    framework: str | None = None,
    minimal: bool = False,
    template: str | None = None,
    template_sha256: str | None = None,
    mode: str | None = None,
    example: bool = False,
) -> ProjectPlan
```

Prepare a scaffold or template, then apply pack hooks in a disposable project.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `directory` | `str \| Path` | Required |
> | `options` | `Mapping[str, Mapping[str, Any]] \| None` | `None` |
> | `framework` | `str \| None` | `None` |
> | `minimal` | `bool` | `False` |
> | `template` | `str \| None` | `None` |
> | `template_sha256` | `str \| None` | `None` |
> | `mode` | `str \| None` | `None` |
> | `example` | `bool` | `False` |
>
> ```python theme={null}
> from harnest.authoring import ProjectPlanner
>
> planner: ProjectPlanner
> plan = planner.plan_init(directory=...)
> ```

### ProjectPlanner.plan\_upgrade

```python theme={null}
ProjectPlanner.plan_upgrade(
    directory: str | Path,
    *,
    options: Mapping[str, Mapping[str, Any]] | None = None,
) -> ProjectPlan
```

Compose core migration and pack migration proposals without live writes.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `directory` | `str \| Path` | Required |
> | `options` | `Mapping[str, Mapping[str, Any]] \| None` | `None` |
>
> ```python theme={null}
> from harnest.authoring import ProjectPlanner
>
> planner: ProjectPlanner
> plan = planner.plan_upgrade(directory=...)
> ```

## WritePolicy

Choose whether to fill a missing value or update pack-owned content.

```python theme={null}
WritePolicy(*values)
```

| Member | Value |
| - | - |
| `IF_MISSING` | `'if_missing'` |
| `MANAGED` | `'managed'` |

> [accordion] Example
>
> ```python theme={null}
> from harnest.authoring import WritePolicy
>
> value = WritePolicy.IF_MISSING
> ```

## apply\_project\_plan

Reject stale inputs, back up originals, and roll back failed source writes.

```python theme={null}
apply_project_plan(plan: ProjectPlan) -> Path | None
```

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `plan` | `ProjectPlan` | Required |
>
> ```python theme={null}
> from harnest.authoring import apply_project_plan
>
> result = apply_project_plan(plan=...)
> ```

## ProjectContext.files

Access this facade through `ProjectContext.files`; its implementation type is not a public import.

Read snapshot files and declare text/template changes without writing them.

### ProjectContext.files.read\_text

```python theme={null}
ProjectContext.files.read_text(path: str) -> str
```

Read an existing staged UTF-8 file.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> result = context.files.read_text(path=...)
> ```

### ProjectContext.files.write\_text

```python theme={null}
ProjectContext.files.write_text(
    path: str,
    content: str,
    *,
    policy: WritePolicy = <WritePolicy.IF_MISSING: 'if_missing'>,
) -> _Operation
```

Propose text creation or a baseline-checked managed replacement.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
> | `content` | `str` | Required |
> | `policy` | `WritePolicy` | `<WritePolicy.IF_MISSING: 'if_missing'>` |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.files.write_text(path=..., content=...)
> ```

### ProjectContext.files.from\_template

```python theme={null}
ProjectContext.files.from_template(
    path: str,
    *,
    template: str,
    values: Mapping[str, str] | None = None,
    policy: WritePolicy = <WritePolicy.IF_MISSING: 'if_missing'>,
) -> _Operation
```

Render an inert UTF-8 dollar template using only explicit substitutions.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
> | `template` | `str` | Required |
> | `values` | `Mapping[str, str] \| None` | `None` |
> | `policy` | `WritePolicy` | `<WritePolicy.IF_MISSING: 'if_missing'>` |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.files.from_template(path=..., template=...)
> ```

### ProjectContext.files.from\_file

```python theme={null}
ProjectContext.files.from_file(
    path: str,
    *,
    source: str,
    policy: WritePolicy = <WritePolicy.IF_MISSING: 'if_missing'>,
) -> _Operation
```

Copy a packaged document or binary asset unchanged through tracked file ownership.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
> | `source` | `str` | Required |
> | `policy` | `WritePolicy` | `<WritePolicy.IF_MISSING: 'if_missing'>` |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.files.from_file(path=..., source=...)
> ```

### ProjectContext.files.delete

```python theme={null}
ProjectContext.files.delete(path: str) -> _Operation
```

Propose removal only if the pack still owns unchanged generated content.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.files.delete(path=...)
> ```

## ProjectContext.yaml

Access this facade through `ProjectContext.yaml`; its implementation type is not a public import.

Declare field-level YAML edits; unrelated values survive serialization.

### ProjectContext.yaml.read

```python theme={null}
ProjectContext.yaml.read(path: str) -> dict[str, Any]
```

Return a detached configuration mapping suitable for migration decisions.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> result = context.yaml.read(path=...)
> ```

### ProjectContext.yaml.set

```python theme={null}
ProjectContext.yaml.set(
    path: str,
    *,
    key: tuple[str, ...],
    value: Any,
    policy: WritePolicy = <WritePolicy.IF_MISSING: 'if_missing'>,
) -> _Operation
```

Propose a JSON-compatible field value with explicit overwrite policy.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
> | `key` | `tuple[str, ...]` | Required |
> | `value` | `Any` | Required |
> | `policy` | `WritePolicy` | `<WritePolicy.IF_MISSING: 'if_missing'>` |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.yaml.set(path=..., key=..., value=...)
> ```

### ProjectContext.yaml.rename\_key

```python theme={null}
ProjectContext.yaml.rename_key(path: str, *, source: tuple[str, ...], destination: tuple[str, ...]) -> _Operation
```

Move a user's existing value without overwriting an occupied destination.

> [accordion] Parameters and example
>
> | Parameter | Type | Default |
> | - | - | - |
> | `path` | `str` | Required |
> | `source` | `tuple[str, ...]` | Required |
> | `destination` | `tuple[str, ...]` | Required |
>
> ```python theme={null}
> from harnest.authoring import ProjectContext
>
> context: ProjectContext
> operation = context.yaml.rename_key(path=..., source=..., destination=...)
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.