# Census proposal: claim-prev-rehearsal

Status: RULED. platform-1e accepted every row as proposed on 2026-10-18 (census-ruling-claim-prev-rehearsal). Overlay `internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal.tsv`, loaded last (after `claim-step6-artifacts-fix2`), every row `rev3=census-ruling-claim-prev-rehearsal`.

## What changed

`carlos system install-channel` takes `--policy FILE` (controller ruling R1, with platform-00's binding additions). FILE is exactly what `carlos system claims run` sign-policy writes. With it:

- the policy's floor, verified as guard-arm verifies it (the root-signed moves-protocol authorization, for this deployment's bucket, under `CARLOS_RELEASE_PUBKEY`), stands in for the published floor while none is published;
- a published floor, or either half of one, must be the policy's bytes, or the run is refused;
- one updated box is admitted: `applied.json` names the channel release, `carlos` hashes to its signed artifact, and the guard is not armed. Its `carlos.prev` is refreshed from `carlos` by the updater's own `notePrev`, and the identity is kept beside the slots.

Without `--policy` nothing changes (ruling 6).

## Units

| Unit | Change | Class | Executing identity |
|---|---|---|---|
| `internal/host/installchannel.go` `readChannelPolicy` | new; read site `file:ReadFile` | authority F,J4 | uid 0 at the box's attended root shell, running `carlos system install-channel --policy`. Reads the operator-staged policy file once, before the apply lock or any store read. |
| `internal/host/installchannel.go` `channelInstall.refreshPrev` | new; read site `file:Lstat` | authority F,J4 | the same uid 0 root shell, under root's apply lock. Lstats `carlos.prev` to refuse a symlink or a non-regular file. |
| `internal/host/installchannel.go` `channelInstall.run` | unchanged digest | F,J4 (region-ha-2b) | unchanged |
| `internal/host/installchannel_linux.go` `slotState`, `var openat2`, `removeBeneath` | unchanged digests | F,J4 (region-ha-2b) | unchanged; 2b's rows stand and are not restated |
| `channelInstall.install`, `channelInstall.checkFloor`, `policyFloorPublished`, `channelPolicy`, `RunSystemInstallChannel` | changed or new, no read site of their own | envelope | the same root shell |

The `internal/host` envelope is re-pinned. The seal stands: no accepted-legacy row. No box agent, update, recovery or edge path reaches either new unit.

## 9B8

No new bucket writer. install-channel writes only local files (`carlos.prev`, its version sidecar through `notePrev`, and `channel-identity.json`). No 9B8 entry is proposed.

## Test files

The rewritten root-runner witness (`activation_console_rootrunner_linux_test.go`) and the new `installchannel_policy_test.go` are tests and are outside the census.

## Fix round 1 (RULED)

Status: RULED. platform-1e accepted every row as proposed on 2026-10-18 (census-ruling-claim-prev-rehearsal-fix1). Overlay `internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal-fix1.tsv`, loaded after the ruled overlay, every row `rev3=census-ruling-claim-prev-rehearsal-fix1`. Everything is in the `--policy` path; without `--policy` nothing changes.

What changed:

- carlos.prev's version record is mandatory in the `--policy` path (Astra). `completePrev` runs under the prev lock: it removes the old verdicts and record, copies carlos when refreshing, then writes the record and reads it back. Every failure is fatal, then the floor's judgement follows through `notePrev` without a replace. The `--policy` "already installed" answer requires the record, and completes it when it is missing or stale.
- An open update journal, or an update-begun marker beside the guard, refuses through `guardArmIdle` (I1).
- The `--policy` applier reads the root participation census (`rootMovesFloorIdentity`), as the update's does, so a census box takes the root moves floor branch (I2).
- The policy file is opened with O_NOFOLLOW and O_NONBLOCK, checked with fstat to be a regular file, and capped at 64 KiB (M3).

| Unit | Change | Class | Executing identity |
|---|---|---|---|
| `internal/host/installchannel.go` `channelInstall.run` | digest unchanged; basis corrected (M5): it now also runs with `--policy` at an updated box's root shell | F,J4, kept exactly | unchanged |
| `internal/host/installchannel.go` `channelInstall.refreshPrev` | re-pinned; basis now says it reaches `notePrev`, and through it `judgePrevWith`/`judgeSeedsPrev` (or the root moves floor judge on a census box), which can open the registry as root when a legacy local floor exists | F,J4 | uid 0 at the box's attended root shell |
| `internal/host/installchannel_linux.go` `openChannelPolicy` | new; read sites `file:Open`, `file:Stat`, `file:ReadAll` | F,J4 | uid 0 at the box's attended root shell |
| `internal/host/installchannel.go` `readChannelPolicy` | `@remove`: its read moved into `openChannelPolicy` | | |
| `completePrev`, `prevRecorded`, `newChannelInstall`, `install` | no read site of their own | envelope | the same root shell |

The internal/host envelope is re-pinned. The seal stands. No 9B8 entry: no new bucket writer. (On a census box, verifyPointer remembers the channel identity in root's guarded identities, as the update already does; that is a local root-state write, not a bucket write.)

## Fix round 2 (RULED)

Status: RULED. platform-1e accepted it as proposed on 2026-10-18 (census-ruling-claim-prev-rehearsal-fix2).

Overlay `internal/provision/testdata/plan14-root-reads-claim-prev-rehearsal-fix2.tsv`, loaded after fix1. Envelope only: the internal/host envelope is re-pinned, and no root-read unit's digest or read sites change.

What changed, all in the `--policy` path:

- "Already installed" always runs carlos.prev's judgement again: `channelPrevJudge`, which is `notePrev` without a replace. It fails on the judgement's error, so a judgement that was cut off or failed is finished or reported, never answered over. `completePrev` ends with the same judgement, after the version record.
- The unfinished-update refusal's reason drops guard-arm's floor prefix.
- `installChannelUsage` is now one literal, of the same value, so the re-approved RHB-C19 can be pinned whole.

The seal stands. No 9B8 entry.
