# State layout

`@hasna/contracts` is an execution-free library and currently owns no
persistent user state. Installing or running its validation commands does not
create a home directory.

If the package gains non-authoritative user-level state in the future, it must
use the canonical Hasna home layout (the 2026-09-04 home-layout ruling,
`~/.hasna/<name>/` on every platform), with the kind as the subdirectory:

```text
~/.hasna/contracts/config   (HASNA_HOME replaces ~/.hasna)
~/.hasna/contracts/state
~/.hasna/contracts/cache
```

XDG is never a Hasna root. `XDG_CONFIG_HOME`, `XDG_STATE_HOME` and
`XDG_CACHE_HOME` are not consulted for any path, and `~/.config/hasna/` is a
retired location, not a fallback. Only the explicit `HASNA_HOME` and
`HASNA_CONFIG_HOME` overrides relocate these roots (XDG *semantics* apply to
those two variables — absolute paths only, blank means unset — but the XDG
variables themselves are never read).

The legacy roots `~/.contracts` and `~/.open-contracts` are not operational read
paths. The package never copies, moves, rewrites, or deletes them. There is no
automatic migration because the current package has no global store or
installer-owned data to migrate. Any files found at those paths must be audited
before manual removal rather than treated as `@hasna/contracts` data.

## Intentional project-local paths

These paths remain relative to the consuming project or target repository. They
must not be redirected into a package-global root.

| Path | Owner and purpose |
| --- | --- |
| `src/generated/storage-kit/.storage-kit-manifest.json` | Deterministic `vendor-kit` output tracked in the target repository alongside the generated storage kit. |
| `.hasna/loops/runs/**` | OpenLoops run artifacts described by the integration contract. They are owned by OpenLoops, not by `@hasna/contracts`. |

### Project manifests (owned by `projects`, not project-local)

Project-manifest metadata (manifest root, dashboard render manifest, snapshots,
evidence, private) has a single canonical convention: `projects`' per-workspace
home store, `~/.hasna/projects/workspaces/<workspace_id>/`. Project folders must
not contain a project-layout directory. Inside a project manifest, the layout
fields are REQUIRED relative-path inputs to the project-manifest schemas —
`@hasna/contracts` validates but never defaults them; `projects` supplies them.

Other dotdir references are declarations or negative fixtures, not Contracts
state. In particular, `.codewith` and other `.hasna/<app>` paths in the secure
local-store policy belong to their named packages, while `.hasna/cloud` appears
in the no-cloud scanner as a forbidden legacy runtime path.

## Credential resolution paths (read, never owned)

`@hasna/contracts` does not own these directories, but its credential resolver
reads only the owner-safe app credentials file (see CONTRACT.md §3a; the
2026-09-04 home-layout ruling, `~/.hasna/<name>/` on every platform):

```text
~/.hasna/<name>/config/credentials             (HASNA_HOME replaces ~/.hasna)
<HASNA_CONFIG_HOME>/<name>/credentials         (when HASNA_CONFIG_HOME is set)
~/.hasna/<name>/config/credentials-<profile>   (profiles)
```

Files must be regular, current-user-owned, and mode 0400 or 0600; unsafe files
fail closed. `XDG_CONFIG_HOME` is not consulted. Retired `~/.hasna/fleet-env/`,
`~/.hasna/cloud/`, `~/.config/hasna/` and `*-cloud.env` files are never
consulted. On macOS the login keychain item `hasna.credentials.<name>.api-key`
(and `.api-url`) is read, fresh per call, before the file. `@hasna/contracts`
never writes, copies, moves, or deletes these files or items.
Explicit legacy migration tooling may preserve/import old data, but ordinary
clients never use it as an authoritative dataset.

The repository ignores `.hasna/` so local project metadata is not accidentally
committed. The generated storage-kit manifest is outside that ignored directory
and is intentionally checked into each target repository.
