# Consumer Authority Beta Lifecycle

The Consumer Authority Beta is a staging-first, non-authoritative package
lifecycle. It combines a fixed package provenance route with the separate
user-scoped consumer authority route; neither one automatically promotes a
package or makes Finish pass.

`0.8.0-beta.1` through `0.8.0-beta.20` are immutable staging-only evidence.
Their registry and provenance observations cannot be reused as current-version
consumer authority evidence. Beta.16's final observer accepted
an exit-zero API download without proving a nonempty exact ZIP, so it stopped
before ZIP validation. Beta.17 created a canonical tarball and provenance but
its Node20/npm10 registry PUT received an authorization-shaped E404; that is not
a package-absence claim, and beta.16 remains present in the public registry.
Beta.18 is immutable staging evidence: its registry bytes matched canonical
facts, but its postpublish checker incorrectly required an unsupported registry
`gitHead` metadata field. Beta.21 is non-reusable temporal final-observer
evidence: online crypto and identity bindings completed only after the leaf
certificate window expired, so fetch, Finish, and replay did not run. The active
`0.8.0-beta.22` is non-reusable procedure evidence: host `gh`/XDG state created
an untracked `.local` entry after the final commit. `0.8.0-beta.23` is
non-reusable procedure evidence because raw-empty Git cleanliness contradicted
required runtime/build preparation. `0.8.0-beta.24` is terminal non-reusable
procedure evidence because the supplied-bundle source contract relied on ambient
`gh` discovery and rejected Linux's runtime-owned `UV_USE_IO_URING=0` child
marker. `0.8.0-beta.26` is terminal non-reusable procedure evidence: the
normal Ubuntu `gh` package record included a regular non-executable completion
sibling, which its basename-only selector rejected before assessment. The active
`0.8.0-beta.27` procedure evidence then collapsed selector lifecycle failures
to an opaque block. `0.8.0-beta.29` is terminal non-reusable procedure evidence:
its selector conflated an optional nonselected ancillary record with an unsafe
record. Beta.30 then classified the documented completion as a competing
executor because of its runner mode. `0.8.0-beta.31` is terminal,
non-reusable protected-Verify evidence: its known-completion selector policy
was sound, but its generic package-version expectation and source/release
fixture import closures were incomplete. `0.8.0-beta.32` then reached the
authoritative source contract but used a live invalid-bundle `gh attestation
verify` preflight that could enter network and Sigstore initialization before
parser rejection. The active `0.8.0-beta.33` package-contract candidate has no package, tag, channel movement, GitHub
release, or signed consumer-project artifact at source preparation time.

The prior beta candidates are retained as staging-only NO-GO or
source-preparation evidence: none qualifies for promotion or issue closure
because it did not complete the full current-version public consumer route.
Beta.20 reached a live `trusted/unconsumed` fetch but then correctly stopped at
`workflow-state-uninitialized` because the observer had not prepared the same
consumer's public lifecycle. That result consumed nothing and is not reusable.
Beta32 retains the immutable same-consumer observer order, V4 stage-scoped
residue cleanliness, pre-push source identity, and one qualified workflow-selected
observer-gh selector lifecycle contract. It cannot inherit beta.16's observer failure, beta.17's registry
PUT result, beta.18's failed metadata predicate, beta.21's expired temporal
result, beta.23's contradictory raw-empty condition, beta.24's ambient-tool
failure, an earlier lifecycle, or an earlier authority result.

The consumer must create its own workflow state through public bootstrap and
report/evidence commands in the exact project that later fetches authority.
An uninitialized `ph workflow finish implement` is a bounded nonzero
`workflow-state-uninitialized` block, never an authority-neutral success.
After public initialization the same command must reach only
`trusted-authority-required` before an artifact is available; a modeled trusted
artifact then proves one real Finish consumption and immediate replay block
without standing in for hosted crypto evidence.

For the final public fixture, source-bound bootstrap must complete before the
final fixture commit. That one commit contains only declared source-bound
bootstrap outputs and the immutable reusable workflow pin. The same unpushed
final fixture commit is retained as the installed consumer CWD/HEAD; subsequent
slow preparation may change only excluded runtime and build state. Immediately
before the one normal push, the fixture remote parent, CWD, Git top-level, HEAD,
and source identity must still match the final commit. Every host process uses
`env -i` with explicit absolute Git, Node, and npm paths. CI, publish, and
release package contracts select the exact primary `/usr/bin/gh` package record
into a private regular non-symlink path with a bounded compatible version
result. The known completion record is optional and may be a no-follow regular
non-symlink file regardless of executable mode; it is never selected or run.
Every other present nonselected basename-`gh` record must itself be a regular
non-symlink non-executable file, while an alias, unsafe secondary, or second
executable blocks. No package observer path
is resolved through PATH. All fifteen host-state roots
are outside the consumer realpath. V4 compares the NUL-safe untracked/ignored
status and normalized `git clean -ndx` output against the exact allowed residue
set at baseline, source-bound preparation, credential handoff, observer child,
and immediately before push. `.local`, `.config`, and `.cache` are never
allowlisted. Linux may add only `UV_USE_IO_URING=0` to the fixed authority-fetch
child environment. No lifecycle or source reinitialization, consumer switch, or
state reset is permitted after fetch.

The beta33 selector runs its bounded direct `gh --version` assessment and the
same private copy runs `gh attestation verify <placeholder> <exact-plan> --help`
with the same token-free state environment. That parser-only command never
opens an artifact or network client. Neither invocation inherits a
package-contract checkout as `HOME` or an XDG state root, so normal tool
validation cannot leave `.local`, `.config`, or `.cache` behind in a source-built
or fresh installed consumer.

The beta33 authoritative-bundle exercise uses the package-visible
`clean-package-exercise-phase.1` vocabulary in
[`consumer-authority-beta33-acceptance.json`](consumer-authority-beta33-acceptance.json).
Source-built and fresh-tar children each emit their own complete ordered,
nonreflective transcript. The parent accepts only all ready records followed by
the exact PASS marker; marker-only, malformed, out-of-order, foreign, or
success-after-blocked output is a bounded phase-envelope block. The ready
`authority-discovery` phase is followed immediately by exactly one
`consumer-authority-discovery-exercise.1` result for that same surface:
`trusted-unconsumed-persisted`. Every record contains only fixed schema,
surface, phase or result state, and bounded code; it never carries raw child
output, a filesystem path, credential, URL, or artifact bytes.

## Fixed Sequence

1. An approved protected-main source candidate has strict prerelease SemVer
   and an existing matching immutable `v<version>` tag.
2. The manual publish workflow permits only `staging` with `staging-only` for
   that prerelease, publishes once through npm Trusted Publishing, and records
   bounded version, selected tag, SHA-1/SRI, raw SHA-256, portable content
   identity, and workflow-bound canonical source readback.
3. The controlled staged-package producer attests the exact downloaded npm
   tarball. The fixed online verifier independently checks the original npm,
   GitHub, and Sigstore bindings before any later promotion decision.
4. A fresh exact registry installation proves the packaged CLI boundaries. A
   public consumer separately enrolls its fixed workflow, fetches original
   signed bytes, verifies them against its current source, and only an explicit
   Finish may consume a trusted result once. Before fixture authorization, the
   package-visible observer credential-preflight obtains a host credential
   without output and sends it only to its isolated fixed GitHub Actions
   read-only worker. It keeps the consumer HOME separate, never logs or
   persists the credential, does not invoke product/npm/archive tooling with
   it, and does not permit a product credential fallback. Fixed GitHub readback,
   not the credential or project content, establishes identity. Before fixture
   authorization, the same package also runs its versioned external attestation
   and artifact transport plan preflights with no token and no artifact. The
   attestation plan uses exactly
   one caller `--repo` selector and one reusable `--signer-workflow` plus
   `--signer-digest`; caller workflow/run/ref/source bindings remain separate.

Before any fixture authorization, a fresh consumer must establish normal
lifecycle records through supported public commands. The fixture may use the
following sequence only with reports that describe its own observed work:

```text
ph bootstrap backend --strict --no-developer-mcp
ph bearshell ./gradlew test
ph bearshell ./gradlew compileJava
ph bearshell ./gradlew clean
ph evidence read README.md
ph evidence read .persona/project-profile.jsonc
ph evidence read src/main/java/<package>/<role>.java
<substantive implementation report> | ph plan --report-filled implementation --stdin
<substantive review report> | ph plan --report-filled review --stdin
ph workflow finish implement
```

That final default Finish must be blocked only by
`trusted-authority-required`. It must not retain implementation-report,
review-report, evidence, report-coverage, profile-read-coverage, Java-role-read,
or loop-state blockers. The public cooperative route remains a separate
same-invocation local test boundary and does not satisfy external authority.

`bootstrap` initializes only absent empty current loop-state records; it does
not repair malformed or stale state. In a fresh project, bootstrap holds a
project-root transaction while it assembles the initial `.persona` tree outside
the caller workspace, then promotes that tree only when `.persona` is still
absent. That same transaction writes `.gitignore` and
`.opencode/opencode.json`, then reserves canonical project-contained `.persona`
and `.persona/workflow` directories before modifying its harness config,
profile, policy, plan, role boundary, reports, or loop states. Each
bootstrap-owned leaf, including `AGENTS.md` staging and cleanup, stays within
the captured reservation and is checked with no-follow identity validation
through its write. A project-root, parent, leaf, temporary, or detected
replacement is an explicit unsafe lifecycle block: it writes no bootstrap artifact outside the project and does not perform automatic recovery. `--stdin`
accepts one bounded substantive report while the corresponding report is still
a template, then refuses a replacement. Its public report ingress enforces a
streaming 65536-byte ceiling
before decoding or collecting the input, so an oversized or continuously
producing pipe is rejected without replacing a report. The cleanup observation
prevents a prior ordinary build from making the fixed cooperative build task
non-fresh. Deleting either loop-state record, submitting malformed or oversized
report text, or copying a report/evidence record remains blocked. The default
Finish and later closure remain external-blocked after a cooperative PASS.
Public report ingress, plan status, and readiness evidence output retain only
stable project-relative references; they omit caller workspace and temporary
absolute paths.
Each `ph evidence read` record stores only a bounded digest and metadata for a
project-contained regular source file. Its source read uses native descriptor
traversal from the captured project capability: every root, parent, and leaf
is opened through a held no-follow directory descriptor. An unsupported,
missing, or checksum-invalid runtime blocks as
`source-read-runtime-unavailable`; there is no pathname or stat-after-open
fallback. The evidence write uses the same canonical project transaction, so a
target, evidence parent, leaf, temporary, or replacement alias blocks without
opening external bytes, writing outside the consumer workspace, or reflecting
source contents.

The final hosted evidence uses two fresh registry-installed fixtures, each
installing the exact immutable version only from `https://registry.npmjs.org`:

- the cooperative fixture runs `ph workflow finish implement --assurance
  cooperative` and requires its explicit same-invocation PASS while default
  Finish and later closure remain external-blocked; and
- the public external fixture runs the user-scoped `ph authority` enrollment,
  original-artifact fetch, independent verification, and explicit Finish
  consumption path against its own signed public push evidence.

Neither fixture can borrow the other fixture's evidence. Forged, copied,
wrong-repository/workflow/ref, drifted, replayed, expired, zero/all-skipped,
malformed/unsafe, or network-denied variants must remain nonzero with no
authority artifact or Finish PASS.

The external-attested fixture must pin the exact current package revision. The
verifier binds the signed receipt `phVersion` to its installed CLI version, so
an original artifact from `0.8.0-beta.1` through `0.8.0-beta.30` is a bounded
binding mismatch or historical-evidence block for `0.8.0-beta.33`; only a future
original signed artifact for the current immutable version can exercise
enrollment, fetch, explicit consumption, and replay rejection. The complete
source/packed acceptance contract is the structured
[`consumer-authority-beta33-acceptance.json`](consumer-authority-beta33-acceptance.json)
record; it names the exact public Java/Spring readiness route, the separate
caller and reusable certificate identities, the independent observer credential
and no-token command/transport plan preflights, the immutable same-consumer
observer sequence, and the one hosted residual.

The beta33 package boundary also binds current package metadata, lock metadata,
and its current acceptance record together. Its authoritative-bundle verifier
is deliberately source/Git-rooted; fresh installed contracts load the observer
stage and package-record modules only from the exact tarball, with no `src`,
`.git`, or source-verifier fallback.

The source projection excludes only `.persona/.ph-init-manifest.json` and
`.persona/workflow` runtime metadata. The init manifest contains a
consumer-local canonical real path, so it is bootstrap ownership metadata rather
than caller project source. The profile, Gradle descriptors, Git identity,
reports, and evidence remain bound. This does not relax caller enrollment,
reusable SHA/SAN, repository, source, run, original-archive, or digest checks.

Before beta.28 package evidence is accepted, the verifier materializes the
exact complete-history bundle in a detached no-local checkout. It binds one
explicit canonical `refs/heads/...` candidate ref and
`refs/remotes/origin/main`; the candidate ref must equal the expected candidate
SHA and must be the only branch ref. A present bundle `HEAD` alias is accepted
only when it resolves to that same candidate SHA. The verifier also binds the
checkout CWD, Git top-level, npm prefix, and the byte-identical `package.json`
and lock from the selected candidate commit. It then runs isolated non-global,
non-workspace npm setup, a normal prepack, and a fresh installed CLI check. An
ambient workspace, stale `dist`, alternate npm prefix, cache selection, or
older package cannot stand in for the frozen tarball. The exact base is
materialized separately from the same bundle and packed under the same policy,
so package comparison cannot borrow either checkout's working directory or
generated output.

When the source checkout is an authenticated canonical `blob:none` promisor
clone, that proof may hydrate only the already-bound `origin/main` commit with
`--refetch --no-filter --no-tags --no-write-fetch-head` before the local bare
repository materialization. It rechecks the retained main ref before and after
the object fetch, rejects noncanonical origins or filters, and never moves a
source ref. Ordinary checkouts use the direct local materialization route.

The pack invocation itself is plain `npm` from that bound checkout CWD;
`npm --prefix ... pack` is not an allowed package-root selector. The packaged
root-bound prepack runner builds from its own script location. The portable
`package-content-identity.1` is the cross-environment comparison: it binds
sorted safe regular package members, allowed mode, size, and per-member
content digest. Generic independent `npm pack` raw bytes are not treated as a
portable identity. The Node20/npm10 canonical packer emits one normalized
tarball under isolated npm/Git state. A separately isolated Node24/npm11
publisher verifies that exact tarball and its facts, runs the same canonical-tar
argv in dry-run mode, then issues the only hosted registry PUT without
repacking. Registry readback must match both that file's raw hash/integrity and
the portable identity.
The one authoritative bundle proof feeds its exact canonical tarball SHA-256
and content identity into both the built source CLI and fresh installed
consumer contracts. Retained earlier beta tar aggregates remain diagnostic-only
and do not authorize a package or authority result.

CI, publish, and release lstat every basename-`gh` entry in the runner's Ubuntu
package record without following links and require exactly the regular
non-symlink executable policy primary `/usr/bin/gh`. The documented
`/usr/share/bash-completion/completions/gh` ancillary entry is optional and,
when present, must only be a regular non-symlink file regardless of mode; it is
never selected, copied, or executed. Every other secondary remains inert only
when regular, non-symlink, and non-executable.
They copy that candidate into a private runner directory and pass only the
resulting path. They never look it up through `PATH`, assume `/usr/bin/gh`,
download it, or use it to access an artifact. A fixed selected-tool diagnostic
crosses the source/installed package boundary without rendering a path, stderr,
token, or artifact input.

For that observer, the authenticated product discovery route sends the enrolled
caller workflow filename directly to the fixed GitHub workflow-runs endpoint.
It does not reconstruct a second `.github/workflows/` prefix. The caller
workflow selects the run; the separate reusable producer SHA and certificate
SAN bind the signer. A missing, malformed, stale, mismatched, or expired result
is not retained.

## Live Verification Deadline

Before the one natural beta.28 fixture push, an independent observer must prepare
one exact Git-backed registry consumer CWD/HEAD and one private consumer `HOME`.
It must first complete the public bootstrap/Gradle/report/evidence/plan route and
confirm default Finish is blocked only by `trusted-authority-required`; it may
then enroll, inspect status, and explain in that same consumer. It must then run
`node node_modules/persona-harness/scripts/preflight-consumer-authority-observer.mjs --json`.
The preflight obtains a host `gh` credential without printing it, creates a
separate ephemeral observer HOME, and sends the credential only to the fixed
authenticated-user and empty sentinel Actions-metadata worker. It must report
`ready` before fixture authorization, and it must not download artifact bytes,
validate online crypto, consume Finish, observe replay, invoke `ph`, invoke npm,
or identify a future artifact. A blocked preflight authorizes nothing.

The package-contract observer must receive the workflow-selected absolute
regular non-symlink `gh` copy and then run
`node node_modules/persona-harness/scripts/preflight-consumer-authority-external-attestation.mjs --json --observer-gh /absolute/regular/gh`.
That preflight supplies no credential and no original artifact. It first proves
the absolute regular non-symlink observer tool has a bounded compatible version
and then proves that installed `gh` accepts the canonical selector grammar
through flag parsing. It retains a bounded exit classification, has no PATH
lookup, network, or artifact access, and does not self-validate a predicate,
grant authority, or replace later online verification.

The observer must also run
`node node_modules/persona-harness/scripts/preflight-consumer-authority-external-artifact-transport.mjs --json`.
This uses no credential, no original artifact, and no network. It validates only
the fixed API endpoint, enrolled caller/run/artifact metadata shape, bounded
output policy, and redirect policy. It does not replace later external
acquisition: when a current artifact exists, a fixed HTTPS client streams into a
private no-follow reservation, checks exact count/SHA-256 and safe ZIP members,
strips Authorization after one validated redirect, and hands only the validated
original ZIP/bundle to the separate attestation command plan. It never retains a
signed URL, response body, header, token, or caller-controlled output path.

Once the current-version original artifact exists, the separately governed
observer sequence independently verifies the custom predicate online before the
leaf certificate `notAfter` deadline, uses the existing installed authority
fetch boundary, rechecks `trusted/unconsumed` with no readiness blocker in that
same consumer, consumes exactly once, and immediately checks the replay-negative
result. It does not bootstrap, reset/copy lifecycle state, replace the private
consumer `HOME`, switch consumers, or change CWD/HEAD/source/profile after fetch.
A missed deadline is `certificate-window-expired`: it blocks without
fetch, Finish, or replay and never becomes a trusted result through local
self-validation or older beta evidence. A successful current-version fetch
retains only the verified artifact ID, digest, run, source, caller, and
reusable/SAN binding; any mismatch remains non-authoritative and is not stored.
The preflight credential never appears in output or persistent observer/consumer
state, and Persona Harness never reads host credential storage itself.

`ph authority status`, `ph authority fetch github`, and closure are
non-consuming. Missing enrollment, unavailable network, malformed records,
copied artifacts, source drift, replay, expiry, or any identity mismatch stay
blocked and never convert package provenance into Finish authority.
Multiple enrollments require an explicit enrolled `owner/repository` argument
to `fetch github`; ambiguous selection performs no network request.

The public fetch JSON uses `consumer-authority-fetch.4`. A verifier block keeps
the public state `binding-mismatch` and may add only the fixed `bindingReason`
enum: `artifact`, `package-version`, `source`, `enrollment`, `run`, `signer`,
`freshness`, `consumption`, `verification`, or `unknown`, plus a fixed
`sourceReason` enum only when `bindingReason` is `source`: `head`, `inputs`,
`identity`, `status`, `index`, `content`, `working-tree`, `workspace`, or
`unknown`. It never includes diagnostic paths, receipt values, tokens, URLs,
raw verifier output, or local paths; Finish and replay behavior are unchanged.

## Live Trust Diagnostics

`ph doctor` performs a live, read-only Sigstore trust-root check with a
30-second whole-worker deadline. Its plaintext and JSON output report network
and trust-root readiness separately. The check uses a fresh product-owned
temporary cache that the parent process removes even when the child times out;
it never treats offline or previously cached material as a positive authority
result.

Consumer verification keeps these bounded, non-secret failure states distinct:
`dns-unavailable`, `network-unavailable`, `trust-root-unavailable`,
`verification-timeout`, `signature-invalid`, `certificate-invalid`,
`transparency-invalid`, and `malformed-bundle`. Diagnostics do not include
tokens, signed URLs, raw bundles, upstream error messages, or absolute paths.

## Promotion Boundary

The beta starts at `staging`. Moving the exact immutable version to `next`
requires a later independent `next-promotion-approved` action. `latest`,
Stable/GA claims, GitHub releases, and registry mutation are outside this
document's source-preparation boundary.
