Files
filip131311 fc09612f30 feat(config): recordings.directory — configurable save location for screen recordings (#601)
## Summary

Adds a `recordings.directory` configuration entry (built on the #534
config system) that lets users choose where finished screen recordings
(mp4) are saved, instead of the fixed `<project>/.argent/recordings/`.

- **Schema entry** (`configuration-core`): scopes `project` + `global`,
`prioritize-local` merge (project wins when both are set). Picked up
automatically by `argent config set/get/unset/list`.
- **Value semantics**: absolute path, `~`-prefixed (expands to home), or
relative — relative anchors at the project root (home dir when not in a
project).
- **Client-side wiring** (`argent-tools-client` `durableSaveTarget`):
the wire `saveDir` hint stays `.argent/recordings` (wire contract
unchanged → old/new client-server interop). Past the existing client
allowlist, the recordings hint is remapped to the configured directory.

## Security model unchanged

The allowlist exists because `saveDir` arrives from a possibly-hostile
remote tool-server. That gate still runs first and is unchanged: a
server can only *select* the sanctioned recordings category, never name
a destination. The configured directory comes from the client's own
trusted config files. Exclusive never-overwrite writes (`name (2).mp4`),
the durable download size caps, and the symlink real-path guard all
remain; for a configured dir the realpath check degenerates to "the dir
resolves to itself" (a user-chosen symlink is legitimate, matching how
the default base may be symlinked). A test asserts hostile wire values
(`.git`, `..`, absolute) stay rejected even with config set.

## `argent link` interaction

Materialization always runs on the **client** (co-located → copy from
`hostPath`; remote → download over `/artifacts/:id`), and the config is
read there at materialize time — so with a remote tool-server the mp4
lands on the client host per the *client's* config, on both paths.

## Testing

- New unit tests: 10 in `argent-tools-client` (absolute/`~`/relative
resolution, local-vs-global precedence, both materialization paths,
hostile wire values with config set) + 4 in `configuration-core` (scope
writes, trim, precedence, validation).
- Suites green: tools-client 188, configuration-core 87, tool-server
screen-recording 62, CLI config 16; full workspace vitest 4727 pass (3
pre-existing `scripts/*.test.mjs` node:test files don't parse under root
vitest — unrelated).
- **Live E2E** on an iPhone 15 Pro simulator via the branch tool-server:
real recording captured, then materialized through the branch client
with configs written by the real `argent config` CLI in a sandboxed
HOME/project. 7 scenarios green: default location, global-only (`~`
path), project-only (relative), both-set → project wins (co-located
**and** remote download with Bearer auth), unset fallbacks, `argent
config list` rendering. Remote-downloaded mp4 ffprobe-verified
(byte-exact size, 4.17 s duration); name-collision suffixing observed
live.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-31 12:36:31 +02:00
..