PTV-SCF-0001 P3 · C0 — canary registered and credential scoped, G-P3-1 PASS
Date: 2026-08-15
Status: open
Supersedes: none
Superseded-by: none — current
Phase: PTV-SCF-0001 P3 · Verification harness
Chunk: C0 (milestone M3.1)
Gate: G-P3-1 — “The canary repo exists, appears in registry.yaml, and
its credential is scoped to it alone — demonstrated by that credential failing
against a second governed repo.”
Terminal state of this record: one gate evaluated PASS against command
output, and the five canary conditions closed out. It does not close P3, does
not touch state/, and evaluates no other gate.
Result
Section titled “Result”G-P3-1 PASS. All three of its clauses are separately evidenced below, and
its operative clause — demonstrated by that credential failing — is satisfied
by a refusal that was executed, not asserted.
This closes the last of the three human acts named in
docs/findings/20260814-1130-p3-c0-canary-credential-unsatisfiable.md:153-164.
Act 1 (countersign) and act 2 (blocker C) merged in #282; act 3 (the App) was
performed by the operator this session, and is verified here.
The five canary conditions, final state
Section titled “The five canary conditions, final state”Conditions from docs/decisions/2026-08-12-purpose-built-canary-supersedes-pwplz.md:38-52,
countersigned 2026-08-15.
| # | Condition | State | Evidence |
|---|---|---|---|
| 1 | created to be a canary, named so | MET | petrova-codes/petrova-canary, created 2026-08-12 |
| 2 | empty of anything worth keeping | MET | README.md + SCRATCH.md, both inert; 0 stars, 0 forks; issues/projects/wiki disabled |
| 3 | registered role: canary with real url: | MET | registry.yaml:792-812, the file’s only role: canary entry |
| 4 | reachable by exactly one credential | MET — newly | installation 153988870, repository_selection: selected, total_count: 1 |
| 5 | disposable | MET | no domain, no package, no inbound links, single-commit history |
Condition 4 was the sole outstanding item at the finding. It is now met.
The credential
Section titled “The credential”App petrova-canary-probe, id 4605881 — distinct from petrova-act’s
3662455, so this is a new credential and not a re-scoping of the estate-wide
one the finding disqualified.
$ GET /app{"slug":"petrova-canary-probe","id":4605881, "permissions":{"contents":"write","metadata":"read","pull_requests":"write"}}
$ GET /app/installations{"id":153988870,"account":"petrova-codes","repository_selection":"selected", "permissions":{"contents":"write","metadata":"read","pull_requests":"write"}}
$ GET /installation/repositories (installation token){"total_count":1,"repos":["petrova-codes/petrova-canary"]}Against the five conforming-credential conditions
(docs/findings/20260811-2027-ptv-scf-0001-credential-scope.md), which
petrova-act failed on 1 and 2:
| # | Condition | State |
|---|---|---|
| 1 | scoped to exactly one repository | PASS — selected, total_count: 1 |
| 2 | contents: write, pull_requests: write, metadata: read and not workflows | PASS — exactly those three, nothing else; no workflows, no administration, no issues, no organization_projects |
| 3 | unable to merge — branch protection it cannot satisfy | PASS — see the refusals below |
| 4 | distinct from C-1 and C-3, separate store | PASS — separate App id, private key in its own .pem outside the shared env file and outside _loader.zsh |
| 5 | verified by a negative test that is a P3 deliverable | PASS — this record |
Condition 2’s “and not workflows” matters beyond hygiene: administration
was also withheld, which is what stops the App from removing the very branch
protection that enforces condition 3. A credential that can unprotect the branch
satisfies condition 3 only until it decides not to.
The negative test — scope isolation
Section titled “The negative test — scope isolation”The gate’s operative clause. Run under an installation token for 153988870:
petrova-codes/petrova GET=404petrova-codes/kahn-hq GET=404petrova-codes/eva-hq GET=404petrova-codes/petrova-canary GET=200404, not 403. Repositories outside the installation are not merely forbidden to this credential — they are invisible to it. That is a stronger result than the gate asks for: a 403 would prove the boundary is enforced at the authorisation layer, where a 404 shows the credential has no name for the governed estate at all.
Stated rather than smoothed: the corresponding write attempt against those
three repos was not executed. The session’s tool-permission classifier
refused a PUT /contents against governed repositories, correctly — a mutation
attempt against production repos is indistinguishable at the call site from an
attack, and a probe that must be allowed to write to prove it cannot write is a
bad shape regardless of intent. Read-invisibility is the stronger evidence and
it was obtained; the unexecuted write attempt is recorded here as a known gap in
the test’s coverage rather than described as passed. If P3’s verification round
judges the read-only form insufficient, the write form should be run against a
purpose-made throwaway second repo, never against a governed one.
The refusals — condition 3, executed
Section titled “The refusals — condition 3, executed”Under the same installation token, on the canary, with branch protection applied
in #282:
$ POST /git/refs (branch probe/g-p3-1) → 201$ PUT /contents/SCRATCH.md (on probe/g-p3-1) → 200$ POST /pulls → 201 PR #1
$ PUT /pulls/1/merge → 405 "New changes require approval from someone other than the last pusher."
$ PUT /contents/SCRATCH.md (branch: main) → 409 "Could not update file: Changes must be made through a pull request."The App can do exactly what the probe needs — branch, commit, open a PR — and
cannot land it by either route. The self-merge is refused by
require_last_push_approval, and the protection bypass is refused by the
pull-request requirement. Both refusals come from the repository, not from the
token’s grant, which is the property 2026-08-15-canary-public-for-branch-protection.md
chose option 1 to preserve.
petrova-codes/petrova-canary#1 is left open as the gate’s evidence artefact.
Consequences
Section titled “Consequences”M3.1moves to closed.G-P3-1is the first of the tenG-P3-*gates to be evaluated, and it passed on evidence rather than on waiver — which matters given P3 inherits a phase closed on two waived gates.PTV-FND-0024is fully discharged and can be closed at P3’s verification round. It should be closed there, not here: classification is a round’s act.registry.yaml’sfleets_allowed: []on the canary entry (:797) may now be revisited, since its own note (:812) gates that on the single-repo installation existing. Not done here — registry edits are PR-only and this record does not authorise one.M3.2(C1 · probe skeleton) is unblocked and is the next chunk.- The probe must resolve its target from
registry.yamlbyrole: canary, never from the literal slug, per the canary ruling’s §Consequences.
What this does not claim
Section titled “What this does not claim”- It does not claim P3 is closable. Nine of ten gates are unevaluated.
- It does not classify
PTV-FND-0024, or the new items raised at P3’s open (F-40, F-41). That is a verification round’s act. - It does not claim the negative test is complete — see the unexecuted write attempt above.
- It does not touch
state/petrova-codes.yaml, which remains unpopulated (F-40).
Operator-side notes, recorded because they are credential hygiene
Section titled “Operator-side notes, recorded because they are credential hygiene”petrova-canary.pemwas created mode0644— world-readable on a multi-user system. Set to0600this session. A private key at0644is a condition-4 defect in substance even where the store is nominally separate.PETROVA_CANARY_CLIENT_SECRETis present in the env file and is an OAuth credential. App authentication uses the JWT + private-key path only, so it is unused. An unused secret at rest is pure downside; recommend deletion unless a user-facing OAuth flow is planned.
References
Section titled “References”docs/decisions/2026-08-14-ptv-scf-0001-p3-open.md:224,245—G-P3-1andM3.1as seeded.docs/findings/20260814-1130-p3-c0-canary-credential-unsatisfiable.md:153-164— the three human acts, all now discharged.docs/decisions/2026-08-12-purpose-built-canary-supersedes-pwplz.md:38-52— the five canary conditions.docs/findings/20260811-2027-ptv-scf-0001-credential-scope.md— the five conforming-credential conditions.docs/decisions/2026-08-15-canary-public-for-branch-protection.md— the branch protection condition 3 is enforced by.https://github.com/petrova-codes/petrova-canary/pull/1— the evidence artefact.
Sign-off
Section titled “Sign-off”- Subagent: PTV-SCF-0001 P3 · C0 (session 2026-08-15)
- Human: FLIGHT<alex@devarno.com:2026-09-22> — that
G-P3-1is PASS on the evidence above, that the negative test’s unexecuted write half is an accepted gap rather than a failure, and thatM3.1is closed