# Rho Improvements

These are recommended changes before the current protocol and CLI shape harden
into v1.

## Current Progress Snapshot

As of the merge of PR #22 (`a4d7063ddf83b9530e25ccf2aade39bd127d0729`) on
2026-06-16, plus the current `.gitattributes` matcher-proof branch, the
implementation is roughly **82% complete** against this file.

Completed or substantially implemented:

- first-class join/admit, request review, approve-request, result release,
  result verification, flow doctor, message send, and request submit commands
- signed governance, signed approval grants, signed run receipts, and
  collaborator-side signed authority-chain verification
- real-input digest commitments without leaking local private input paths
- deterministic GitHub inbox path generation plus migration guidance/command
- sender/admin recipient expansion for protected paths and explicit bundles
- mandatory recipient-envelope context binding for repo-facing encrypted
  envelopes
- sidecar attachments for file-backed results instead of inline YAML payloads
- safe encrypted tar extraction with hostile fixture coverage
- key revocation checks when verification is repo-aware
- removal of non-cryptographic digest fallback and host shell-outs for
  `date`, `uuidgen`, `shasum`, and `file`
- removal of the unused `transcrypt` submodule now that repo encryption is no
  longer being modeled after Transcrypt
- shared segment-aware protected-path glob matching for repo policy,
  encryption filters, doctor, and audit checks
- generated `.gitattributes` encrypted filter patterns are covered by a
  generator/matcher agreement test across mid-path wildcards, recursive
  wildcards, extension wildcards, and non-matching public paths
- focused crypto negative coverage for transparent repo-key envelopes and
  recipient envelopes, including ciphertext mutation, non-recipient open, and
  envelope context mismatch rejection
- Git-filter smudge rejects ciphertext tamper for an active intended recipient
  instead of silently leaving a corrupted protected-path envelope in the
  worktree
- high-level request review rejects tampered encrypted request ciphertext before
  review/signature processing continues
- high-level result verification refreshes protected result envelopes and
  rejects tampered encrypted result ciphertext before provenance checks continue
- high-level message verification refreshes protected message envelopes and
  rejects tampered encrypted message ciphertext before signature checks continue
- remaining ad-hoc line-based YAML field helpers have been removed; shared-root
  request/approval paths now require typed manifests, encrypted-envelope
  detection uses parsed top-level YAML `kind`, and repo-key fallback naming
  parses `rho/repo.yaml` as a typed manifest
- mandatory default `rho repo doctor` governance verification

Remaining high-value work before calling this v1-ready:

- replace the custom X25519/ChaCha20 recipient envelope and repo-key protected
  file format with `age`, or explicitly decide to defer that format migration
- model repo-key transparent protected paths in policy so every generated
  `.gitattributes` entry has one manifest source
- finish deterministic inbox path migration in older fixtures/manual examples
  that still construct handle-only paths directly
- decide and implement participant self-signatures, or explicitly keep
  owner-signed participant bundles as the sole v1 governance model
- add GitHub PR creation/opening to high-level join/result flows through a
  provider-specific module rather than baking GitHub assumptions into core
  workflow logic
- restructure this document into a concise roadmap plus changelog so open work
  remains obvious

The estimate is weighted by user-facing protocol readiness, not line count:
the real GitHub flow now works with signed provenance and encryption checks, but
the remaining `age` migration and parser/path hardening are security-critical
enough that the last quarter should not be treated as optional polish.

## Real GitHub rehearsal findings

This section comes from the live two-identity GitHub rehearsal in:

```text
https://github.com/madhavajay/rho-live-tutorial-test-20260615
```

The real flow completed successfully:

- `madhavajay` initialized the Rho repo and owner inbox policy.
- `madhavajay-test` joined through a fork PR.
- `madhavajay` admitted the collaborator and added encrypted inbox policy.
- `madhavajay` published a mock prices dataset while keeping real data local.
- `madhavajay-test` submitted signed analysis code and an encrypted request PR.
- `madhavajay` reviewed the request, verified the signature, ran the code on
  mock data, then ran the approved real-data action in Gondolin.
- `madhavajay` merged the request PR before releasing the result.
- `madhavajay` released an encrypted signed result PR.
- `madhavajay-test` fetched merged `main`, decrypted the result, verified the
  owner signature, and saw `1113.03`.

Merged PR sequence:

```text
#1 Request Rho access for madhavajay-test
#2 Admit madhavajay-test to Rho project
#3 Request private prices analysis
#4 Release encrypted prices result
```

### 1. Add first-class join and admit commands

The join/admit path currently requires users to compose low-level commands and
remember which files to stage:

```bash
rho add user github/$COLLAB
git add rho/participants/$COLLAB.yaml rho/membership.yaml
rho repo protect-path "rho/messages/inbox/$COLLAB/**" --recipient github/$COLLAB
```

This should become:

```bash
rho repo join github/$COLLAB
rho repo admit github/$COLLAB
```

`rho repo join` should:

- verify or initialize the caller identity
- export the public participant bundle
- update membership as a pending collaborator if needed
- create a branch with a conventional name
- print or optionally open the GitHub PR

`rho repo admit` should:

- verify the participant bundle against the GitHub profile claim
- add or update membership with role `collaborator`
- add the collaborator inbox encryption policy
- run `rho repo doctor`
- commit on an owner review branch

The live run exposed that `rho add user` originally wrote only
`rho/participants/<handle>.yaml`, while `rho repo doctor` required matching
membership. That is now locally patched, but the workflow should still be
higher-level than `rho add user` plus manual policy edits.

Implemented first slice: `rho repo join github/<handle>` now creates a
conventional `<handle>/join-rho` branch, exports the participant bundle through
the existing `add-user` path, updates membership, and prints the stage/commit
next step. `rho repo admit github/<handle>` now creates a
`review/<handle>/admit-rho` branch, ensures the participant is present, adds the
recipient-encrypted inbox policy, installs filters, runs `rho repo doctor`, and
prints the stage/commit next step. Remaining work is GitHub PR creation/opening
and online profile proof verification as part of the high-level command.

### 2. Make GitHub account and commit identity explicit per role

The first owner commit was accidentally authored as `madhavajay-test` because
`gh` had been switched to the collaborator account while accepting the private
repo invite. The fix was to run:

```bash
rho gh switch "$OWNER"
rho commit --amend --no-edit --reset-author
```

Rho should make this harder to get wrong:

- `rho-use github/$OWNER` should warn if active `gh` is a different login.
- `rho commit` should warn when `RHO_IDENTITY` and active `gh` disagree.
- `rho commit --require-identity-match` should fail on mismatch.
- `rho id init github/<handle>` should persist the chosen commit author and
  make future commits independent of the active `gh` account.

The tutorial also needs every role block to call both:

```bash
rho-use github/$ROLE
rho gh switch "$ROLE"
```

Implemented first slice: `rho env switch` / `rho-use` emits a shell warning when
the active `gh` login differs from the selected Rho GitHub identity, and
`rho commit` warns on the same mismatch. `rho commit
--require-identity-match` now strips that Rho-only flag before invoking Git and
fails before committing if `RHO_IDENTITY` and the active `gh` account do not
match or cannot be verified. `rho id init github/<handle>` already persists the
selected commit author when the user accepts the binding prompt.

### 3. Infer repo ids from SSH host aliases

The owner used SSH aliases:

```text
git@github-madhavajay:madhavajay/rho-live-tutorial-test-20260615.git
```

`rho repo init --yes` could not infer the repo id from this remote, so the live
run had to pass:

```bash
rho repo init --repo-id "$OWNER/$REPO" --yes
```

Improve `detect_github_remote` so it handles configured SSH aliases when
`HostName github.com` is present, or add a clearer error that prints the exact
`--repo-id` command to run.

Implemented first slice: repo id inference now delegates GitHub-specific remote
parsing to the provider layer instead of embedding it in `rho repo`. Standard
GitHub remotes and SSH aliases that resolve to `HostName github.com` can infer
`owner/repo`, while unsupported providers still fall back to the explicit
`--repo-id` path. Future providers should add their own provider module rather
than extending the repo command with provider-specific parsing.

### 4. Fix fresh-clone Git filter materialization

Several fresh clones checked out encrypted inbox files before Rho installed the
repo-local Git filters. After `rho repo init --yes`, those files still remained
as `rho_recipient_envelope` YAML in the working tree until they were removed and
checked out again:

```bash
rm "$RESULT_DIR/result.yaml" "$RESULT_DIR/result.rhosig.yaml"
git checkout HEAD -- "$RESULT_DIR/result.yaml" "$RESULT_DIR/result.rhosig.yaml"
```

This happened while verifying the merged result from `main`. It also appeared
during owner review of the encrypted request.

Rho should provide a safe repair command:

```bash
rho repo refresh-protected
```

It should:

- install or verify filters
- find protected files that are still encrypted envelopes in the working tree
- re-materialize only those files through the smudge filter
- avoid touching unrelated dirty files
- print exactly which files changed

`rho repo init --yes` could optionally run this when the repo has no unrelated
dirty changes.

Implemented first slice: `rho repo refresh-protected` now installs the protected
path filters, scans tracked recipient-encrypted files, and smudges encrypted
working-tree envelopes back to readable plaintext when the active `RHO_HOME` has
the recipient key. The committed `HEAD:path` blob remains encrypted.

Remaining follow-up: recipient-envelope clean filters reseal with fresh
randomness, so a refreshed plaintext file may appear modified to Git even though
re-adding it preserves encrypted storage. Rho needs either deterministic
worktree/index reconciliation, a filter protocol that can preserve clean stat
state, or a higher-level workflow that avoids exposing this as accidental user
churn.

### 5. Add first-class request review

Owner review required several manual steps:

```bash
rho crypto verify request.yaml --signature request.rhosig.yaml --identity github/$COLLAB
python3 workspace/sum_prices.py datasets/$UUID/mock/prices-mock.csv
```

Rho should provide:

```bash
rho request review req-$UUID
```

It should:

- locate the request in the owner inbox
- decrypt/refresh it if needed
- verify the collaborator signature
- show request metadata, code path, code digest, dataset id, and requested tier
- run the submitted code against the mock dataset
- emit a review summary suitable for committing or attaching to a PR

Implemented first slice: `rho request review <req-id> --root <repo>` now locates
`rho/messages/inbox/*/<req-id>/request.yaml`, parses the live inbox request
shape, verifies `request.rhosig.yaml` against `request.from` when present,
prints the request metadata, validates the submitted code digest when the
request includes `code_sha256`, and runs the repo-relative Python code path
against either `--mock-dataset` or the conventional
`datasets/<dataset_uuid>/mock/prices-mock.csv`. This first slice left
encrypted working-tree refresh and richer review artifact generation to the
follow-up slices below.

Implemented review-summary slice: `rho request review` now accepts
`--summary-out <path>` and writes a structured `request_review` YAML artifact
with request metadata, signature status, code digest status, mock dataset path,
mock stdout/stderr, and review timestamp.

Implemented encrypted-refresh slice: `rho request review` now detects protected
recipient-encrypted working-tree envelopes for `request.yaml` and
`request.rhosig.yaml`, smudges only those review artifacts with the active local
identity, writes the plaintext back to the working tree, and then continues the
normal signature, digest, and mock-run checks.

### 6. Add first-class protected run approval

The owner-side trusted run required manual construction of:

- execution workspace
- request manifest
- proposed action YAML
- action grant
- Gondolin execution command

The command shape should be:

```bash
rho run approve-request req-$UUID \
  --runner gondolin \
  --private-root <private-datasets-root>
```

It should:

- derive the action id and run id
- map repo-portable request paths to host paths
- validate the code digest
- create the proposal/action grant
- execute in Gondolin
- write a stable run manifest
- print the output path and digest

The live run also showed that Gondolin host mounts must be absolute paths. If
relative paths are accepted by `rho run`, they must be canonicalized before
writing the Gondolin config.

Implemented first slice: `rho run approve-request <req-id> --root <repo>
--private-root <path>` now consumes the live signed inbox request shape,
verifies `request.rhosig.yaml`, validates the submitted code digest, derives
conventional `act-run-real-<id>` and `run-run-real-<id>` identifiers, writes
local execution request/action-grant artifacts under `.rho/`, executes the
reviewed script against the private dataset through the selected runner, and
writes `.rho/runs/<run-id>.yaml` plus stdout/stderr artifacts for `rho result
release`. `--runner gondolin` uses the existing Gondolin config path, and
repo/private roots are canonicalized before execution. Remaining work is
owner-key-signing the grant as an in-repo authority artifact and supporting
non-Python/tool-specific command templates without relying on `--command`.

Implemented tool-command-template slice: tool manifests now support an optional
`command_template` argv array with `CODE_PATH` / `{{code_path}}` and
`DATASET_CSV` / `{{dataset_csv}}` placeholders. `rho run approve-request` uses
`--command` first, then any request-provided command, then the selected tool's
template, and finally the legacy Python fallback. The approve-request e2e now
runs an AWK analysis via `rho/tools/run_real.yaml` without putting a command in
the request. Remaining command-template work is richer tool-specific schemas
for inputs/outputs and runner capabilities beyond simple argv templates.

### 7. Add first-class result release

The final release required manually creating:

```text
rho/messages/inbox/<collab>/req-<uuid>/result.yaml
rho/messages/inbox/<collab>/req-<uuid>/result.rhosig.yaml
```

and relying on Git clean filters to encrypt both files on commit.

This should become:

```bash
rho result release req-$UUID \
  --run-id run-run-real-$UUID \
  --to github/$COLLAB
```

It should:

- require the request PR to be merged or explicitly acknowledged
- read the approved run output
- write a result manifest
- sign it with the owner identity
- place it in the collaborator inbox
- verify staged Git storage is encrypted
- create a conventional result branch
- optionally open the GitHub PR

Implemented first slice: `rho result release <req-id> --run-id <run-id> --to
github/<collab>` now requires the request to exist in the repo unless explicitly
acknowledged, reads a run output from `--output`, `--value`, or
`.rho/runs/<run-id>/stdout.txt`, writes the collaborator inbox `result.yaml`,
signs `result.rhosig.yaml` with the owner identity, stages both files by
default, verifies the staged Git blobs are encrypted, and creates a conventional
`<owner>/result-<req-id>` branch. When a run manifest exists, it also writes and
signs a committed run receipt. Remaining work is GitHub PR opening and requiring
that signed receipt throughout the full flow verification path.

### 8. Codify the PR choreography

The real flow should be represented as first-class state transitions:

```text
join requested -> admitted -> request submitted -> request merged
-> run approved -> result released -> result merged -> result verified
```

The key ordering discovered in the live run:

- Merge the request PR before creating or merging the result PR.
- Rebase the result branch on top of the merged request so Git history shows
  causality.
- Verify result decryption and signature as the collaborator before merging
  the result PR.
- After merge, verify again from a fresh collaborator clone of `main`.

Add `rho repo doctor --flow` or a scenario-specific command that can inspect a
repo and say which step is next.

Implemented first slice: `rho repo doctor --flow` now audits committed signed
run receipts and linked signed results. It does not yet infer the next missing
workflow step, but it does enforce the key request -> run receipt -> result
chain once a result has been released.

Implemented flow-next slice: `rho repo doctor --flow` now reports the next
 workflow command for in-progress states. Repos with requests but no run
receipt point to `rho run approve-request`, and repos with a signed run receipt
but no result point to `rho result release`, while completed result chains keep
the strict receipt/result/signature audit.

### 9. Store shared payloads outside YAML

YAML should describe metadata, routing, signatures, hashes, and policy. It
should not become the default container for large payloads, binary payloads, or
files being shared back and forth.

For binary files, files over a configured size threshold, and normal shared
artifacts, prefer this layout:

```text
rho/messages/inbox/<recipient>/<message-id>/
  message.yaml
  attachments/
    original-report.pdf.rhoenc
    prices-output.csv.rhoenc
```

The payload file should preserve the original filename and extension where
possible, with encryption represented by an added suffix or header. The
encrypted file itself should be an envelope:

```text
rho encrypted file header
metadata: algorithm, recipients, original filename, original MIME type, digest
payload: encrypted bytes
```

The YAML message should reference those payloads:

```yaml
version: 1
message:
  id: msg-...
  from: rho://id/github/alice
  to: rho://id/github/bob
  type: result_available
  attachments:
    - path: attachments/prices-output.csv.rhoenc
      original_name: prices-output.csv
      mime_type: text/csv
      sha256: ...
```

This gives Rho better behavior for:

- PDFs, images, archives, model outputs, and other binary files
- large CSV or JSON result artifacts
- Git diffs, because metadata changes stay readable while payloads stay binary
- streaming and partial file handling
- future object storage or Git LFS integration

Messages may also deserve a concise envelope extension, for example:

```text
request.message
result.message
note.message
```

Implemented first slice:

- `rho result release` now stores file-backed outputs as sidecar attachments
  under `rho/messages/inbox/<recipient>/<request-id>/attachments/` instead of
  embedding them in `result.yaml`.
- `result.yaml` keeps signed metadata, the attachment path, original filename,
  and payload SHA-256. Small `--value` releases remain inline for now.
- Staging verifies that result metadata, signatures, and file attachments are
  protected by the recipient envelope filter.

A `.message` file could be the canonical Rho message envelope for small
text-first messages, while larger attachments remain sidecar files. The rule of
thumb should be:

- small structured metadata: YAML or `.message`
- user-visible text message: `.message`
- binary or large payload: original filename plus encryption envelope
- signed metadata should hash referenced payload files, not inline them

### 10. Reduce public profile editing churn

The GitHub profile claim flow worked, but regenerating identities creates new
profile fragments. During the live run, keys were moved into `~/.rho` to avoid
having to edit profiles again.

Improve this experience:

- default identities should live in `~/.rho`
- `rho id init github/<handle>` should reuse existing keys by default
- the command should clearly say when it is reusing versus generating keys
- `rho id rotate-key` should be the explicit command that creates a new claim
- `rho id status github/<handle>` should show whether the profile claim is
  currently verified

Implemented first slice: `rho id status <rho-id>` now reports whether the
identity is local or imported, whether private keys are present, the active
stored GitHub proof claim/URL, and the stored verification state. This makes it
easier to confirm that `~/.rho` is reusing the expected identity before editing
a public profile again. `rho id init github/<handle>` also now prints whether
default signing/encryption keys were generated or reused.

Implemented rotation slice: `rho id rotate-key <rho-id>` now explicitly creates
a new active GitHub signing key and profile claim, clears the proof verification
state, leaves the previous signing public key in the identity bundle for old
signature verification, and keeps the existing encryption key unchanged so old
inbox envelopes remain decryptable.

### 11. Keep committed storage checks built in

The live run repeatedly checked that plaintext did not leak into Git:

```bash
rho status
rho crypto view <path>
git show HEAD:<path>
```

This should be one command:

```bash
rho repo audit-encryption
```

It should check all protected paths in the index and `HEAD`, fail if plaintext
is detected, and print a concise report.

Implemented first slice: `rho repo audit-encryption` checks policy-protected
recipient-encrypted paths in both the index and `HEAD`, verifies recipient
envelopes contain every expected recipient, and fails on plaintext blobs or
repo-key envelopes where recipient encryption is required.

## 1. Replace the custom X25519 envelope

Do not keep a hand-rolled X25519 recipient envelope as a long-term format.
The current approach makes Rho responsible for envelope structure, KDF choices,
recipient binding, replay semantics, metadata handling, and future
compatibility.

Use a standard format instead:

- Prefer `age` (`age-encryption.org/v1`) for file encryption.
- Consider HPKE (RFC 9180) only if Rho needs lower-level protocol composition.

`age` is a good fit for Rho because it already supports multi-recipient X25519,
identity files, and a mature Rust ecosystem. This should let Rho delete crypto
format code and inherit more security analysis.

## 2. Harden encrypted tar bundle extraction

Encrypted tar files are still attacker-controlled input after decryption.
Any `.request.tar.rhoenc` or `.result.tar.rhoenc` bundle opened into local
staging must be extracted through a safe extractor, not plain `tar -xf`.

The extractor must reject:

- absolute paths
- `..` path components
- symlinks
- hardlinks
- device files
- FIFOs and other special files

Add hostile fixture coverage in scenario tests before treating encrypted tar
bundles as a stable protocol surface.

Implemented first slice: `rho crypto extract-tar <path.tar> --out-dir <dir>`
now performs fail-closed ustar extraction for regular files and directories
only. It rejects absolute paths, parent-directory traversal, symlinks,
hardlinks, device files, FIFOs, and unsupported metadata entry types such as
PAX headers. The branch-PR fixture now uses this extractor after decrypting
request/result bundles instead of `tar -xf`, and hostile tar coverage verifies
the rejection paths.


## 4. Add explicit key revocation

Key rotation by leaving old keys unused is not enough for compromise. Add a
repo-committed revocation policy such as:

```yaml
version: 1
revocations:
  - identity: rho://id/github/example
    key_id: rho://key/github/example/ssh-ed25519-1
    reason: compromised
    revoked_at: 2026-05-01T00:00:00Z
    revoked_by: rho://id/github/admin
```

Verification should reject signatures from revoked keys according to the
revocation policy. Keep v1 simple: an admin-maintained deny list is enough.

Implemented first slice: `rho repo init` now creates
`rho/policy/revocations.yaml`, `rho repo doctor` requires it, and
`rho crypto verify <path> --signature <sig> --identity <id> --repo-root <repo>`
rejects signatures whose `(signer, key_id)` pair appears in that repo policy.
The e2e revocation fixture signs a request, verifies it before revocation, adds
the key to the policy, and confirms verification fails afterward.

## 5. Consolidate membership and admin authority

Membership, roles, admin status, and participant bundle references should not be
duplicated across several independent files.

Keep one authoritative membership file for admitted participants and their
roles/admin rights. Participant public bundles can remain separate files, but
membership entries should reference them by stable identity id and key id.

This avoids consistency bugs such as:

- a member with no participant bundle
- an admin who is not a member
- a participant bundle that is not admitted
- conflicting role or admin state across files

`rho repo doctor` should validate the references rather than reconciling
multiple sources of truth.

Implemented first slice: `rho repo doctor` now treats membership as the
authority for admitted participants, supports `admin` as a first-class role,
rejects duplicate membership entries, rejects participant bundles outside
membership, and keeps `rho/repo.yaml`'s owner tied to an explicit `owner`
membership role. Remaining work is deeper consolidation of role/admin policy
into one schema and migration of older repos that still duplicate authority
state elsewhere.

## 6. Define deterministic inbox paths

Directory names for identity-specific paths must be specified. Avoid short,
invented names like `rho-user1`; they do not survive provider collisions or
handle renames.

Prefer a deterministic mapping from the identity URL, for example:

```text
rho://id/github/madhavajay
-> rho/messages/inbox/id/github/madhavajay/
```

If identity paths become too awkward for filesystems, use a documented
URL-safe encoding or hash. The mapping should be used everywhere messages,
policy patterns, participants, and scenario fixtures reference identity-specific
directories.

Implemented first slice: high-level writers now use the canonical GitHub inbox
mapping `rho/messages/inbox/id/github/<handle>/...` for admitted inbox
policies, released results, and committed run-receipt `result_path` bindings.
Result verification and flow doctor still accept legacy
`rho/messages/inbox/<handle>/...` paths so existing repos can migrate
incrementally. Remaining deterministic-path work is converting older fixtures,
bundle scenarios, and manual examples that still construct handle-only inbox
paths directly.

Implemented migration-guidance slice: `rho repo doctor` now reports non-fatal
`migration:` advisories for admitted GitHub members whose legacy
`rho/messages/inbox/<handle>/` directories still exist, pointing at the
canonical `rho/messages/inbox/id/github/<handle>/` target. This keeps existing
repos passing while making the path migration visible during normal health
checks.

Implemented migration-command slice: `rho repo migrate-inbox-paths` now moves
admitted GitHub member inbox directories from the legacy handle-only layout to
`rho/messages/inbox/id/github/<handle>/`, rewrites matching
`rho/policy/permissions.yaml` patterns, and regenerates `.gitattributes` from
policy. `--dry-run` prints the planned moves without modifying the working tree.

## Findings from auditing the merged live repo

This section comes from auditing the actual merged state of the live rehearsal
repo (cloned `main`, all committed artifacts, and the four merged PRs):

```text
https://github.com/madhavajay/rho-live-tutorial-test-20260615
```

The earlier rehearsal findings (#1-#11) and the protocol-hardening sections
cover workflow ergonomics and crypto-format hygiene. Auditing the committed
result surfaced a deeper class of gaps: the repo is not independently auditable
after the fact, and almost nothing except messages is signed.

### A. Concrete defects in the committed example repo

These inconsistencies are sitting in `main` of the canonical example repo right
now. They are cheap to fix and should be fixed first so the reference repo does
not ship contradictions.

1. **`crypto.yaml` contradicts `.gitattributes`.** `rho/policy/crypto.yaml`
   declares `transparent_git_filters: disabled`, but `.gitattributes` assigns
   `filter=rho-crypt diff=rho-crypt` to the inbox paths. Two sources of truth
   for the same decision, already disagreeing.
2. **`paths.yaml` points at paths that are not used.** `rho/policy/paths.yaml`
   declares `rho://repo/rho/requests/` and `rho://repo/rho/control/outbox/`,
   but the real request lives at
   `rho/messages/inbox/<owner>/req-<uuid>/request.yaml`. Stale aspirational
   config.
3. **`rho.yaml` references a missing file.** It points at
   `rho/resources.yaml`, which does not exist in the repo.
4. **`dataset.yaml` is structurally malformed.** `dataset:` and `variants:`
   are empty keys, while `uuid`, `name`, `mock`, and `real` sit at the top
   level instead of nested underneath. It round-trips only through the
   line-based `yaml_field` helpers in `lib.rs`; it is not valid structured YAML
   and will break any real parser.
5. **`.gitattributes` hardcodes one filter line per handle.** Every new
   collaborator requires a manual `.gitattributes` edit. The filter config is
   not generated from policy.

Fix direction:

- Make crypto policy the single source of truth and **generate `.gitattributes`
  from it** instead of hand-maintaining per-identity lines.
- Generalize `rho repo doctor` to validate **all** manifest cross-references
  (not just membership, see consolidation section) and to flag policy
  contradictions and dangling file references.

Implemented first slice: `rho repo init` now creates the referenced
`rho/resources.yaml` manifest, recipient `rho repo protect-path` and
`rho repo install-filters` now mark `transparent_git_filters: enabled` with
`filter: rho-crypt`, and `rho repo doctor` requires `rho/resources.yaml` and
fails when `.gitattributes` uses `filter=rho-crypt` while crypto policy says
filters are disabled or omits the filter name. Remaining Section A work includes
full cross-reference validation, generated `.gitattributes` as an output of
policy rather than an append-only file, and broader manifest normalization.

Implemented dataset slice: `rho repo doctor` now parses every tracked or
untracked `dataset.yaml`, rejects the deprecated flat top-level dataset fields,
requires the nested `dataset.uuid`, `dataset.owner`, and `dataset.variants`
shape, validates mock payload references, and checks payload paths stay inside
the dataset bundle. The local Git Pi fixture now commits a canonical nested
dataset manifest instead of the old `mock_path` / `real_tier` shape.

Implemented generated-attributes slice: recipient-encrypted Git filter entries
are now rendered as a marked generated block in `.gitattributes` from
`rho/policy/permissions.yaml`. `rho repo protect-path` and
`rho repo install-filters` rewrite that block idempotently and remove legacy
append-only lines for the managed policy patterns. Remaining policy generation
work is to model repo-key transparent protected paths in policy too, so every
`filter=rho-crypt` entry has a single manifest source.

Implemented manifest cross-reference slice: `rho repo doctor` now validates
that `rho.yaml` agrees with `rho/repo.yaml` and points at the expected live
manifest files/directories, rejects duplicate membership identities, accepts
`admin` as an explicit authority role, rejects participant bundles that are not
admitted in membership, and requires dataset owners to be admitted members.
`rho id import` also skips adjacent participant `.rhosig.yaml` files written by
governance signing instead of trying to import them as identity bundles.
Remaining Section A work is broader manifest normalization and modeling
repo-key transparent protected paths in policy too, so every generated
`.gitattributes` entry has a single manifest source.

### B. Commit a signed provenance chain

The merged `main` contains the encrypted request, the encrypted result, and
nothing linking them. Every run/approval/grant manifest defined in `lib.rs`
(`RunManifest`, `ApprovalManifest`, `ActionGrant`, `ControlledAction`) lives in
gitignored `.rho/` and `sandbox/`. So the repo cannot prove after the fact
which code digest was approved, which runner executed it, against which input,
producing which output digest.

For a trusted-compute system this is the most important missing artifact.

Rho should commit an **owner-signed run receipt** that binds:

```text
request_id -> approved code_sha256 -> runner -> input commitment
-> output_sha256 -> result path
```

This receipt is what makes the repo independently auditable. It should be
written and signed by `rho run approve-request` / `rho result release` and
verified by `rho repo doctor --flow` and the collaborator-side verify step.

Implemented first slice: when `.rho/runs/<run-id>.yaml` exists,
`rho result release` now writes `rho/run-receipts/<request-id>/<run-id>.yaml`,
signs it with the owner identity, and stages both receipt files. The receipt
binds the request id, run id, runner, status, tier, code path/digest, dataset
input path/digest when available, output digest, and result path. Remaining work
is to make `rho run approve-request` produce the receipt directly and require
the receipt chain from collaborator-side verification.

Implemented flow-audit slice: `rho repo doctor --flow` now requires at least one
`rho/run-receipts/**/<run-id>.yaml`, validates the receipt links back to a
request and result, checks the bound code/input/output/result fields are
present, verifies the receipt signature, and verifies the linked result
signature. Signature verification with `--repo-root` now falls back to
`rho/participants/*.yaml`, so a fresh collaborator clone can verify owner-signed
flow artifacts without importing the owner into local `RHO_HOME` first.

Implemented collaborator verification slice: `rho result verify <req-id>` now
locates a released result, verifies its context-bound owner signature, verifies
the linked signed run receipt, verifies the linked signed approval grant, checks
the result/receipt/grant digest bindings, and prints the authority chain for
the collaborator.

Implemented approve-request receipt slice: `rho run approve-request` now writes
and signs the committed `rho/run-receipts/<request-id>/<run-id>.yaml` receipt
immediately after a successful approved run. `rho result release` now verifies
and reuses an existing signed receipt instead of overwriting it, while still
creating the receipt for older flows that only have a local `.rho/runs`
manifest.

### C. Sign governance files, not just messages

Only messages carry a `.rhosig.yaml`. `rho/membership.yaml`,
`rho/participants/*.yaml`, `rho/tools/*.yaml`, all of `rho/policy/`, and
`dataset.yaml` are plaintext and writable by any `members`-class actor.

A compromised or malicious collaborator could flip `run_real.yaml`'s
`approval_required: true -> false`, rewrite membership, or swap the mock CSV,
and nothing would detect it. The trust root itself is unsigned.

Rho should require these governance files to be **owner-signed**, with
`rho repo doctor` verifying those signatures. This extends the membership
consolidation section: consolidating is not enough if the consolidated file is
still unsigned.

Implemented first slice: `rho repo sign-governance` now signs the repo
governance manifests with the repo owner key by default and stages adjacent
`.rhosig.yaml` files. `rho repo doctor --governance` requires those signatures,
checks that each signer matches `rho/repo.yaml`'s owner, and verifies the
signature using repo participant bundles. The signed set currently covers
`rho.yaml`, `rho/repo.yaml`, `rho/membership.yaml`, `rho/resources.yaml`,
`rho/policy/*.yaml`, `rho/participants/*.yaml`, `rho/tools/*.yaml`, and every
`dataset.yaml`.

Implemented mandatory-doctor slice: `rho repo doctor` now verifies governance
signatures by default and reports `governance: passed` as part of normal repo
health. `--no-governance` remains available for explicit bootstrap diagnostics,
and `rho repo admit` signs the updated governance set before running its health
check. E2E fixtures that expect a healthy repo now sign governance first, and
the doctor fixture proves unsigned governance fails by default. Remaining work
is deciding whether some participant self-signatures should complement the owner
authorization signature and migrating older live repos.

### D. Bind approval authority to the owner key, not the GitHub merge

Today the approval step is effectively "the owner merged the PR". That rests on
GitHub's merge permissions: anyone with repo merge rights, not necessarily the
Rho owner identity, can admit a member or approve a run.

Approval should be bound to an **owner-key-signed grant committed in-repo**, so
Rho authorization is independent of GitHub's permission model. The merged PR
becomes the transport, not the authority.

Implemented first slice: `rho run approve-request` now signs the local
`.rho/action-grants/<action-id>.yaml` execution grant and writes an owner-signed
committed approval grant at
`rho/approval-grants/<request-id>/<action-id>.yaml`. The committed grant binds
the request, action id, tool id, action type, decision, owner identity, action
digest, local grant digest, repo commit, reviewed code digest, and private input
digest without committing the private input path. `rho repo doctor --flow` now
requires the conventional approval grant for each run receipt, verifies the
grant signature is from `rho/repo.yaml`'s owner, and checks the grant's request,
action, code digest, tier, and private input digest bindings before accepting
the receipt/result chain. Remaining work is to migrate older live repos.

### E. Add context binding / domain separation to crypto

Signatures cover `signed_sha256` and a `namespace`, but nothing binds an
envelope or signature to `{repo_id, request_id, recipient_id, purpose}`.
Without that binding, an encrypted blob plus its signature could be transplanted
into another request slot or another repo and still verify.

The protocol-hardening section cites replay as a reason to adopt `age`, but
**binding is the application layer's responsibility regardless of `age` or
HPKE**. Make it an explicit requirement: every signed/encrypted payload must
commit to its repo, request, recipient, and purpose.

Implemented first slice: `rho crypto sign` and `rho crypto verify` now accept
`--repo-id`, `--request-id`, `--recipient`, and `--purpose` as an explicit
signature context. Context-bearing signatures sign a deterministic Rho payload
containing the file digest and normalized context, so editing the context fields
after signing invalidates the SSH signature. Legacy signatures without context
continue to verify, while callers that supply an expected context reject missing
or mismatched context. Remaining work is threading this required context through
all high-level request/result/message commands and adding equivalent context
binding to recipient envelopes.

Implemented result-release slice: `rho result release` now signs released
results with `purpose: rho.result` and run receipts with
`purpose: rho.run_receipt`, both bound to the repo id, request id, and result
recipient. `rho repo doctor --flow` now verifies those expected contexts while
auditing the receipt/result chain.

Implemented approval-grant slice: `rho run approve-request` now signs local
action grants with `purpose: rho.local_action_grant` and committed approval
grants with `purpose: rho.approval_grant`, both bound to the repo id, request
id, and requester/result recipient. `rho repo doctor --flow` now requires the
committed approval grant context when validating the signed authority chain.

Implemented request-context slice: owner-side high-level request consumers now
require context-bound request signatures. `rho request review` and
`rho run approve-request` verify `request.rhosig.yaml` with `purpose:
rho.request`, bound to the repo id, request id, and owner/request recipient.
Legacy request signatures without context are rejected by these paths.

Implemented request-writer slice: `rho request submit-run <req-id>` now writes
repo-facing run requests directly to the canonical recipient inbox path,
`rho/messages/inbox/id/github/<recipient>/<req-id>/request.yaml`, signs
`request.rhosig.yaml` with `purpose: rho.request` bound to the repo id, request
id, and request recipient, and stages both protected files with `RHO_IDENTITY`
set to the explicit request sender.

Implemented message-writer slice: repo-mode `rho message send --root <repo>`
now writes small text messages to the canonical recipient inbox path, signs
`message.rhosig.yaml` with `purpose: rho.message` bound to the repo id, message
id, and message recipient, and stages the protected message/signature files with
`RHO_IDENTITY` set to the explicit sender. The older `--shared-root` mode
remains for local control fixtures.

Implemented recipient-envelope context slice: policy-driven Git filter
recipient envelopes now carry `crypto.context` with the repo id, repo-relative
path, and `purpose: rho.git_filter`. That context is part of the per-recipient
key derivation, so editing it breaks decryption, and `rho crypto smudge
--repo-root <repo> --path <path>` rejects context-bearing envelopes transplanted
to a different repo/path. Recipient envelopes without context are no longer a
valid repo-facing format.

### F. Encrypt to self (and to all admins)

The request is encrypted only to the owner. The collaborator who sent it can
never decrypt their own sent message again, and a second admin cannot co-read
it. `age` makes multi-recipient trivial.

Rho should **always include the sender, and all current admins, as recipients**
when encrypting a message or request.

Implemented first slice: policy-driven recipient envelopes now expand the
configured protected-path recipients with all repo `owner`/`admin` members and
the active local sender identity when it can be determined from `RHO_IDENTITY`
or a single local identity in `RHO_HOME`. Git-filter encrypted message/request
files are decryptable by the sender and repo owners/admins. The auto-encryption
e2e now proves the destination, sender, and a second owner can all decrypt the
same protected request.

Implemented result-sender slice: `rho result release` now stages protected
result metadata, signatures, and attachments with `RHO_IDENTITY` set to the
explicit result sender. This makes released results decryptable by the sender
even when the sender's `RHO_HOME` contains multiple local identities and the
filter cannot infer a single local sender.

Implemented non-filter bundle slice: `rho crypto seal` and `rho crypto open`
now require `--repo-root <repo> --path <repo-path> [--purpose <purpose>]`.
Recipient envelopes are bound to the repo id, repo-relative path, and purpose,
and the explicit recipient list is expanded with current repo owners/admins
plus the active sender identity. The Git branch PR and recipient crypto
fixtures now prove request/result/envelope payloads are decryptable by both
recipient and sender and are rejected when opened under the wrong repo path.

### G. Bind released results to the real input

`dataset.yaml`'s real variant is just `staged_under_user_folder: true` with no
digest and no commitment. The released result (`1113.03` in the live run) is
tied to nothing reproducible.

Record a **real-input commitment** (a digest of the private file actually used)
in the run receipt from section B. This gives provenance and dispute resolution
without revealing the private data.

Implemented first slice: `rho result release` writes `input_sha256` into the
signed run receipt from the run manifest's `dataset_csv`, and real-tier releases
now fail if the private input cannot be committed. `rho repo doctor --flow`
requires a real-tier input commitment and verifies it against the owner-signed
approval grant, so a released result is linked to the approved private input
digest.

Implemented privacy tightening: newly written run receipts no longer include
the local private `input_path`; they keep only the digest commitment. `rho repo
doctor --flow` accepts receipts without `input_path` and flags legacy receipts
that leak an absolute input path.

## Code review findings (2026-06-16)

This section comes from a direct read of `src/lib.rs` and `src/commands/crypto.rs`
against the roadmap above. The signed-provenance design (Sections B, C, D, G) is
the strongest part of the system and should be preserved: binding approval
authority to an owner-key-signed in-repo grant, with the merged PR as transport
rather than authority, and an auditable `request -> code digest -> runner ->
input commitment -> output digest -> result` chain, is the right architecture for
a trusted-compute system. The findings below are gaps to close before the
protocol hardens into v1, ordered by severity.

### CR1. Remove the non-cryptographic `file_digest` fallback (highest priority)

`file_digest` in `src/lib.rs` shells out to `shasum -a 256` and, when `shasum`
is not on `PATH`, silently falls back to a `DefaultHasher` (SipHash) result
prefixed `fallback-noncryptographic-`. That digest is exactly what binds approved
code (`code_sha256`), signature payloads, and the run-receipt provenance chain
in Sections B/G. Nothing rejects the `fallback-` prefix, so the entire integrity
model can degrade silently to a non-cryptographic, forgeable hash.

`sha2` is already a dependency (it backs the HKDF in `crypto.rs`), so the fix
also removes a process dependency:

- Compute SHA-256 in-process with the `sha2` crate instead of shelling out to
  `shasum`.
- Hard-fail rather than fall back if a digest cannot be computed; never emit a
  non-cryptographic digest into a signed or approval-bearing artifact.

This should land before the rest of the suggested order, because the signed
run-receipt chain (Sections B, C, D, G) is only as trustworthy as the digest
under it.

Implemented CR1 slice: `file_digest` now computes SHA-256 in-process with the
`sha2` crate using a streaming reader. The previous `shasum` shell-out and
`fallback-noncryptographic-*` path are removed, and unit coverage pins the
known SHA-256 digest for `abc` so signed artifacts, approvals, receipts, and
dataset manifests cannot silently downgrade to a non-cryptographic digest.

### CR2. Stop shelling out for primitives

The CLI shells out for primitives in ~20 call sites: `shasum`, `date`
(`now_rfc3339`), `uuidgen` (`uuid_like`), `file` (`mime_type`), `tar`, `git`, and
`ssh-keygen`. Each is a host-environment dependency and a silent-failure surface.
`uuid_like` in particular falls back to hashing `SystemTime + cwd`, which is
low-entropy and predictable (acceptable for filenames, not for anything
security-bearing).

`git` and `ssh-keygen` shell-outs are a deliberate and reasonable choice
(reusing SSH keys for signatures). But `date`, `uuidgen`, `shasum`, and `file`
should be replaced with crates. This is the concrete, reliability-driven core of
the "refactor into one real `rho` binary" item below, not a cosmetic cleanup.

Implemented CR2 primitive slice: `now_rfc3339` no longer shells out to `date`;
it formats UTC timestamps in-process. `uuid_like` no longer shells out to
`uuidgen` or hashes low-entropy host state; it uses `getrandom` and emits a
UUIDv4-shaped id. `mime_type` no longer shells out to `file`; dataset manifests
use a deterministic extension mapping with `application/octet-stream` for
unknown types. Together with the CR1 digest change, the `date` / `uuidgen` /
`shasum` / `file` shell-outs are removed from shared primitives. Remaining
process calls are the intentional integration points such as `git`, `ssh`, and
`ssh-keygen`.

### CR3. Converge on one YAML parser

Implemented in the current parser-convergence branch: the ad-hoc line-based
`yaml_field` / `yaml_first_list_item_after` helpers have been removed from
`src/lib.rs`, shared-root request/approval status paths now parse typed
`RequestManifest` / `ApprovalManifest` values, and encrypted-envelope detection
uses parsed top-level YAML `kind` instead of substring checks. The remaining
YAML work should be treated as normal typed-manifest cleanup, not a two-parser
trust-root risk.

### CR4. Make `path_matches_pattern` total, or prove generator/matcher agreement

`path_matches_pattern` in `crypto.rs` only handles a trailing `/**` or a trailing
`*`. A mid-path wildcard (for example `inbox/*/req/**`) will not match. Today the
safety net is that `.gitattributes` is generated from the same policy, so Git
only invokes the filter on matching paths; but if the generator and this matcher
ever diverge, the failure mode is plaintext committed to a path that should have
been encrypted. `rho repo audit-encryption` is the backstop, but it should not be
the only one. Either use a real glob implementation, or add a test that asserts
the `.gitattributes` generator and `path_matches_pattern` agree across a hostile
pattern set.

Implemented CR4 matcher slice: protected-path matching now lives in `rho_core`
instead of being duplicated in `crypto.rs` and `repo.rs`. It is segment-aware:
`*` matches within a single path segment, while a whole-segment `**` matches
zero or more path segments. Unit coverage includes legacy `/**` patterns,
mid-path wildcards, filename-extension wildcards, and negative cases where `*`
must not cross `/`. Repo policy matching, encryption filters, doctor, and audit
checks now use the same matcher.

Implemented CR4 generator-proof slice: `generated_gitattributes_block` now has
unit coverage that extracts the generated `filter=rho-crypt` patterns and checks
them with `path_matches_pattern` against hostile positive and negative examples,
including mid-path wildcards, recursive wildcards, extension wildcards, and
public paths that must not receive generated encryption filters.

### What is already solid (do not regress)

- **Tar extraction.** `safe_extract_tar_bytes` is fail-closed and correct:
  it rejects `..`, absolute paths, symlinks, hardlinks, devices, FIFOs, and
  unknown/PAX entry types, with bounds checks on entry size, and has hostile
  fixture coverage. This already satisfies protocol-hardening Section 2.
- **Context binding (Section E)** is sound as built: the context is folded into
  both the signed payload and the HKDF `info`, so stripping the context field
  from a manifest invalidates the signature rather than silently downgrading to
  context-free verification. The residual risk is verifiers that *should* require
  context but do not pass an expected context, and the parallel fact that
  revocation checks (Section 4) are skipped when `--repo-root` is not passed.
  Both are opt-in safety. Make "high-level commands always pass expected context
  and `--repo-root`" a hard, tested rule, since opt-in safety eventually gets
  forgotten.
- **Encrypt-to-self plus all admins (Section F)** is the right default and `age`
  will make it cleaner.

### CR5. Add crypto tamper / negative tests, and raise the `age` swap priority

The custom envelope (X25519 -> HKDF-SHA256 -> ChaCha20Poly1305, fresh ephemeral
per recipient) is competent, but the transparent file path reuses one long-lived
repo key with random 96-bit nonces, which is fine at this scale and weak as a
permanent format. Replacing it with `age` (roadmap protocol-hardening #1) also
deletes code that otherwise has to be proven correct, and gives multi-recipient
plus encrypt-to-self for free. Regardless of timing, the crypto unit tests
currently lack tamper / negative cases. Add, before and after the `age` swap:

- flip a ciphertext byte and assert decryption fails
- decrypt as a non-recipient and assert it fails
- mismatch the signature/envelope context and assert it is rejected

Implemented CR5 negative-test slice: `rho-transparent-crypto.sh` now mutates an
explicit repo-key envelope ciphertext byte and proves decrypt fails with AEAD
rejection. `rho-recipient-crypto.sh` already covered non-recipient open and
wrong envelope context; it now also mutates a recipient-envelope ciphertext byte
and proves the intended recipient cannot open the tampered envelope. Remaining
work is carrying the same negative coverage up through the high-level
request/message/result flows and the Git-filter smudge path.

Implemented CR5 Git-filter slice: `rho crypto smudge` now distinguishes a
non-recipient from an active intended recipient whose envelope record fails to
decrypt. Non-recipients still receive the encrypted envelope, preserving
checkout tolerance, but a recipient named by `RHO_IDENTITY` now gets a hard
decrypt error on ciphertext tamper. `rho-auto-encrypt-policy.sh` mutates a
protected-path recipient envelope from the Git index and proves smudge rejects
it for the active owner with AEAD failure. Remaining work is high-level
request/message/result tamper coverage.

Implemented CR5 request-review slice: `rho-request-review.sh` now mutates the
encrypted request ciphertext staged by `rho request submit-run`, writes that
tampered envelope back into the repo-facing request path, and proves
`rho request review` rejects it as the intended owner before review processing
continues. Remaining work is high-level message/result tamper coverage.

Implemented CR5 result-verify slice: `rho result verify` now refreshes protected
`result.yaml` and `result.rhosig.yaml` envelopes before parsing and verifying
the result chain. `rho-result-release.sh` proves encrypted working-tree result
files are refreshed successfully, then mutates the encrypted result ciphertext
and proves `rho result verify` rejects it as the intended collaborator before
result, receipt, or approval-grant verification continues. Remaining CR5
high-level work is message-flow tamper coverage.

Implemented CR5 message-verify slice: `rho message verify` now refreshes
protected `message.yaml` and `message.rhosig.yaml` envelopes before parsing and
verifying a context-bound message signature. `rho-message-send.sh` proves
encrypted working-tree message files are refreshed successfully, then mutates
the encrypted message ciphertext and proves `rho message verify` rejects it as
the intended recipient before signature verification continues. This completes
the requested high-level CR5 tamper coverage for request, message, result, and
Git-filter paths; the remaining crypto-format work is the `age` migration or an
explicit deferral decision.

### CR6. Restructure this document

`improvements.md` has become a ~930-line implementation journal, and the
structure now works against it:

- Two independent numbering schemes both start at 1 (rehearsal findings 1-11,
  then protocol-hardening "1, 2, 4, 5, 6" with no #3, then Sections A-G), so
  "fix #2" is ambiguous.
- Roadmap and changelog are interleaved: every section appends "Implemented
  first slice: ... Implemented X slice: ...", so open work is hard to see at a
  glance.

Suggested split: a `ROADMAP.md` for open work (one numbering scheme, checkboxes)
and a `CHANGELOG.md` for the "Implemented slice" prose (which is genuinely useful
provenance and should be kept). Reconcile the "Suggested order" list below
against current status; several of its items are already partially done but the
list does not say so.

## Suggested order

1. Remove the non-cryptographic `file_digest` fallback (CR1): use `sha2`
   in-process and hard-fail rather than emitting a forgeable digest. This guards
   the signed run-receipt chain that the rest of the order depends on.
2. Fix the concrete example-repo defects (Section A): generate `.gitattributes`
   from crypto policy, remove the stale `paths.yaml` / `rho.yaml` references,
   fix `dataset.yaml` nesting, and extend `rho repo doctor` to validate all
   manifest cross-references and policy contradictions.
3. Define and commit the signed run-receipt and signed-governance schema
   (Sections B, C, D, G) before refactoring crypto, so the new crypto layer has
   a stable thing to sign.
4. Replace the custom crypto envelope with `age`, including multi-recipient
   encrypt-to-self and admins (Section F) and context binding (Section E), and
   add the tamper / negative crypto tests (CR5).
5. Add safe tar extraction and hostile extraction tests.
6. Add key revocation checks to signature verification, and make expected
   context plus `--repo-root` mandatory in high-level commands (CR4 residual,
   Section E).
7. Normalize deterministic identity path mapping.
8. Consolidate membership/admin authority (now signed, per Section C).
9. Refactor the CLI into one real `rho` binary, keeping deliberate `git` /
   `ssh-keygen` integration points explicit and converging on one YAML parser
   (CR3).
