Repository navigation
Closed
Feature
1 / 11 of 1 issue completed
Copy link
Labels
priority: mediumReal value, no hard deadline; normal backlog flow.Real value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.Refactors, migrations, contract changes; cross-cutting and hard to reverse.
Description
Intent (owner, 2026-10-30)
Agents working an agent-ready issue through the lane drive Claude Code with /harness-ops:control-claude-code to check that a change to a mod, skill, slash command, hook output or configuration looks and behaves as the acceptance criteria say. They attach screenshots or video to the PR as evidence before it merges. The same driver also lets Claude look at its own interface (the / menu, /status, dialogs, the status line) instead of guessing how things look.
Blocks today (2026-10-30, kept for the record)
- The skill says "Attended runs only", and
config/defaults.jsonsetssafety.unattendedWrites: false. readyrefuses any run whereapiKeySourceis notnone(driver/driver.mjs:707), so every run uses a signed-in subscription account.- https://code.claude.com/docs/en/legal-and-compliance: subscription OAuth "is designed to support ordinary use of Claude Code", and plan limits "assume ordinary, individual usage". Automated verification in CI fits API-key authentication better, billed under the Commercial Terms.
- Captures are screenshots and mp4. The model reads images, not video, so judging a recording needs frames extracted.
- Windows driver fix: fix(lefthook): keep the check-affected pre-push job parseable on Windows #11360 (hook) and the follow-up driver PR.
Decisions (owner)
- Credential separation (2026-10-31, Decision recorded):
- The capture job may hold pr-verify's existing credential.
- The PR's own code runs in a child process whose environment and readable files hold no token, and only in-repo heads are captured.
- If the build cannot keep the token out of the PR code's reach, it stops and brings that back to the owner.
- Where capture runs (2026-11-06, issuecomment-6692967126):
- Local capture, on the building session's own subscription, is the main path.
- CI capture with an API key behind the key proxy is a bonus.
- No subscription token ever goes behind the proxy.
- CI capture stays on (2026-11-06, issuecomment-6693820027):
merge.verify-capture.appstays set. - The capture's PR-code boundary stays as built (issuecomment-6689952950).
Done when
- A PR whose diff changes something visible in the interface gets screenshots or video captured and judged against its acceptance criteria before merge (#13958 evidence).
- The same chain (confirm → run-e2e → drive) runs attended locally and unattended in CI.
- Diff-to-feature matching lives only in app-control; confirm and run-e2e call it instead of parsing control skills.
- With the app's control skill not installed, drive falls back to the generic recipe and the report says so; nothing errors.
- If a capture cannot run (blocked, or no usable driver), the verdict records why and does not fail the PR.
- A failed evidence verdict sends the work back to implementation, up to a cap of 3.
- While the loop counter is at its cap, no further loop-back runs and the existing park-and-retry path takes over.
- A passing PR carries one comment linking redacted evidence; account and usage lines never appear in uploaded captures (#13958 evidence; mask fix fix(harness-ops): mask any banner plan name and hide masked lengths in Claude Code captures #13997).
- Fork PR heads never run a capture.
- Every evidence artifact has a retention period.
- Verdict, evidence-loop count and capture count are recorded per PR.
- feat(source-control): post the CI evidence comment from the merge gate #11393 is closed by a merged pull request (PR 13731).
- The capture job's step that starts the app under test passes it no credential variable, and a test asserts that the app process's environment holds none of the pr-verify credential names (2026-10-31 decision). Test:
.github/scripts/pr-verify/workflow.test.mjs:1427(evidence).
🤖 Generated with Claude Code
Metadata
Metadata
Assignees
Labels
priority: mediumReal value, no hard deadline; normal backlog flow.Real value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.Refactors, migrations, contract changes; cross-cutting and hard to reverse.
Type
Fields
Work status
confirmed
Activity
github-actions commentedon Oct 30, 2026
Proposed work class: work-class: structural
Basis: automated intake triage; the lanes App applied this class.
Size: Large
Complexity: High
Uncertainty: Knowable
Flags: none
Blocked by: none
#11364 (comment)
This decision settles the open questions (credential, PR code in the capture job, unattended runs), so nothing new is asked. The work is a multi-PR change across several plugins and pr-verify, so it needs splitting into sub-issues.
Automated triage, applied by the lanes App.
melodic-automation-lanes commentedon Oct 30, 2026
The latest triage verdict holds nothing: admission moved the issue to
status: needs-decision; its comment says why.melodic-automation-lanes commentedon Oct 30, 2026
Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.
Intended outcome: Lane agents drive Claude Code via control-claude-code on the lane's own machine, using an API key for unattended runs, and attach frame-extracted screenshots to the PR; the attended-only rule is dropped for run-directory-only runs.
Related work
Open questions
RECOMMENDED: The lane's own machine before the PR; no new runner infrastructure, and the signed-in-account model still applies. Basis: judgment
RECOMMENDED: API key for any unattended or CI run, since subscription OAuth is meant for ordinary individual use; keep subscription for attended runs. The 'ready' check would then accept an allowed key source instead of only 'none'. Basis: plugins/harness-ops/skills/control-claude-code/driver/driver.mjs:704
RECOMMENDED: Yes, limited to runs that write only to their run directory; keep unattendedWrites false for anything else. Basis: plugins/harness-ops/skills/control-claude-code/SKILL.md:8
RECOMMENDED: Screenshots only at first; add frame extraction as a follow-up, since the model reads images but not video. Basis: judgment
RECOMMENDED: The owner creates the key by hand as a credential step; the Windows fix is a prerequisite only if the lane runs on Windows. Basis: judgment
Sources read
To answer, post a comment that starts with this line:
Automated refinement by the lanes App. Answer with a comment whose first line is
**Decision recorded.**.kyle-sexton commentedon Oct 30, 2026
Intent update (owner, 2026-10-30)
kyle-sexton commentedon Oct 30, 2026
Locked Brief (owner confirmed 2026-10-30)
TLDR
Agents prove work that shows in an app's interface by capturing the running app and having a fresh verifier judge the evidence against the acceptance criteria, attended and unattended, by wiring existing skills (confirm → run-e2e → app-control:drive → control-) with no new plugin or workflow.
Problem
Agents verify UI-visible work only by reading text (diff, tests). Nothing captures the running app, so a change can pass every gate while looking or behaving wrong, and the owner has to spot it.
Goal
/verification:confirm(is it done: mechanical stage, intent by change type, fresh-verifier verdict, loop counter) wraps/testing:run-e2e(touched entry points, run, judge each) wraps/app-control:drive(operate and capture, never judge) which uses acontrol-<app>skill when installed, else the generic recipe.confirm/SKILL.md:168and run-e2e; eachcontrol-<app>owns its app's knowledge, including what to mask; verification owns the verdict and local loop; the PR pipeline owns CI, credentials, the fork rule, retention and metrics; source-control owns posting evidence to a PR (demo-video's posting rules move there)./testing:run-e2e unattendedon in-repo PR heads; only the run directory reaches the judge, which still runs no PR code.Full design:
design-draft.mdbeside this file.Work size
Multi-PR, several plugins (app-control, testing, verification, source-control, playwright, harness-ops) plus
.github/workflows/pr-verify.ymland pr-pipeline config. Not one session.Constraints
docs/plugin-philosophy.md:99-113).pr-verify.yml:18).Acceptance criteria
Captured assumptions
driver.mjs:299-317); the first CI run confirms it.Out-of-scope
Deferred questions
Design
Problem: agents verify UI-visible work only by reading text (diff, tests). Nothing captures the running app, so a change can pass every gate while looking or behaving wrong, and the owner has to spot it.
Layers (verified this session)
/testing:run-e2e"when thetestingplugin is installed. Otherwise drive the live app directly" (confirm/SKILL.md:164)./app-control:drivecan run the app, it launches, operates and captures, and this skill judges what it recorded" (run-e2e/SKILL.md:33-36).unattended(run-e2e/SKILL.md:3), drive takes--unattended(drive/SKILL.md:38), confirm handles unattended runs (confirm/SKILL.md:109).sourcesglobs itself (confirm/SKILL.md:168, repo control skills only) and run-e2e chooses touched entry points. The new app-control action replaces both.Flow and ownership (each concept lives in one plugin; cooperation only through public contracts)
sources:globs (control-skill contract,plugins/app-control/reference/control-skill.md:46-49), so no consumer parses a control skill. App-control knows nothing about Claude Code.control-<app>skill owns its app's knowledge, including what to mask in captures (Claude Code: account and usage lines). Drive applies the masks; todaysecrets_markonly flags captures (drive/reference/evidence.md:47), so masking is new work in drive driven by the control skill.drive/SKILL.md:56-70). With harness-ops absent there is nocontrol-claude-code, so drive falls back to the bundledrunskill or the TUI project-type recipe and says so in the report (:89-92). The verdict then records reduced evidence, never a crash. Unattended with nothing usable: a note, not a FAIL.testingplugin is installed. Otherwise drive the live app directly",confirm/SKILL.md:164); app-control joins that pattern. New: the verifier reads the run dir; NEEDS WORK loops back, own counter capped at 3 (same value asmax-verify-fails,docs/conventions/pr-pipeline.yaml:1075).retention-days(pr-verify.yml:397), and the per-PR metrics (verdict, loop count, capture count) beside roadmap: factory shape from the owner's 2026-10-19 direction - part 3 of 4 #10205's measures.plugins/source-control/skills/pull-request/reference/create.md:389).playwright:demo-video's posting rules move there and demo-video becomes a caller.kyle-sexton commentedon Oct 30, 2026
Durable copy of the
Designsection of the evidence-verification PLAN.md (Brief: #11364 (comment)). Plan status: pending owner approval; Q10, Q11 and Q12 are open.Design
Scope: integration. Artifacts:
design-threads.md,contract-spec.md,sequence-flows.mdin thistopic's design slice (summarized here; this section is the copy implementers read).
Module layout
affected(diff → features, backedby
scripts/affected_features.py),--trusted-root <dir>, the mask post-check, the publishablelist and redacted
report.public.{json,md}; the control-skill contract (reference/control-skill.md)gains
## Maskand the<changed files>placeholder. Knows nothing about Claude Code.Attended runs only.,adds
## Mask,credentials.ci,maxCaptures, masked rendering plus text sidecars in the driver,Ready accepting the named CI credential's key source, plugin dir from changed files.
affectedinstead of parsing control skills; takesapp=/base=;passes
--unattended --trusted-rootthrough; writese2e-summary.json.affected(replacesplugins/verification/skills/confirm/SKILL.md:168parsing); verifier reads thefull local run dir (
publishablegates uploads only); ownsevidence-loop.json(cap 3) and local evidence retention (last 3 heads)./source-control:pull-request evidenceposts one marker comment (localgh --attach; CI fromsource-control-babysit-mergeafter PASS, before enqueue); create.md'sVisual evidence updated; playwright demo-video's
reference/posting.mdmoves here, demo-videocalls it.
.github/workflows/pr-verify.yml,write-verdict.mjs,prompt.md,docs/conventions/pr-pipeline.yaml+ schema + README):merge.verify-capturekey,capturejob, judge jobs read the capture artifact as untrusted data,
verdict.jsonevidenceblock.affected, presence-gated);source-control and the pipeline consume JSON files only. Every arrow presence-gated.
Contracts
Invocations:
/app-control:drive affected [<app>] --base <ref> [--head <ref>] [--out <dir>] [--unattended] [--trusted-root <dir>]— discovery as bare drive (
plugins/app-control/skills/drive/SKILL.md:40-100); launches nothing; emptyfeatureswhennothing matches or no control skill. Glob root: the repository under verification, for project
and plugin control skills alike.
--trusted-root <dir>on every drive action: control skills only from<dir>and plugins loadedfrom under it (a PR head cannot supply its own control skill, masks or driver).
## Mask: image/video Capture commands apply it and write<artifact>.txt; drivescans sidecars; a hit, a missing sidecar, or no
## Maskline → not publishable. Raw captures andsecrets_markentries never publishable./testing:run-e2e unattended app=<app> base=<ref> [scenario]→e2e-summary.json.evidence-loop.<branch-slug>.json; manifest lineEvidence loop: <k>/3; at 3 no loop-back, Next =open the PR so pr-verify FAIL →
max-verify-fails→ fresh attempt → park(
docs/conventions/pr-pipeline.yaml:1068-1082)./source-control:pull-request evidence <pr> [--artifact-url <url>] [--summary <file>]: onemarker comment upserted by comment id; links the retained capture artifact; uploads no files
(attachments have no retention period, AC10).
capture(in-repo head, read-only token, isolated,.baseplugins,--trusted-root .base) uploadspr-capture-<pr>-<sha>(publishable only,retention-days); thethree judge jobs
needs: capturewithalways(), download it by run-scoped id intopr-verify-data/capture/, framed untrusted. Capture skip/failure never fails the run.Variation verdicts
/app-control:driveaffected+affected_features.pyplugins/app-control/reference/control-skill.md:319-322/source-control:pull-request evidenceConventions followed
plugins/app-control/reference/control-skill.md(hub, config layers, credentials from env vars)plugins/app-control/skills/drive/reference/evidence.md(report.json, secrets_mark, run dir kept)docs/plugin-philosophy.md"Keep plugins horizontally decoupled" (presence-gated collaborators)docs/conventions/untrusted-content/README.md(capture content is data to the judge).github/actions/setup-runner-isolation/action.yml(head-code hardening)docs/conventions/pr-pipeline.yamlmax-verify-fails,verify-cap-attempts(loop and park)plugins/playwright/skills/demo-video/reference/posting.md(gh --attach, artifact link comment)affectedaction (no existing app-neutral diff matcher; two ad-hoc copies exist);## Mask+ sidecar post-check (drive only flags secrets today,plugins/app-control/skills/drive/reference/evidence.md:47);--trusted-root(drive has no way to prefer main's control skills);evidence-loop.json..github/workflows/pr-verify.yml:33-36"takes no artifact or cache" (judge jobs now read thecapture artifact; reviewed change);
.github/workflows/pr-verify.yml:18"No pull request code runs here" holds forthe judge, but the run now contains a PR-code job (U2);
plugins/source-control/skills/pull-request/reference/create.md:389"no option that uploads afile" is stale (gh ≥2.99
--attach);plugins/harness-ops/skills/control-claude-code/SKILL.md:8attended-only and app-control: new plugin plus testing and debugging changes #7581's2026-10-23 amendment, superseded by the Brief (2026-10-30).
kyle-sexton commentedon Oct 30, 2026
Durable copy of the
Plansection of the evidence-verification PLAN.md (Brief: #11364 (comment)). Plan status: pending owner approval; Q10, Q11 and Q12 are open.Plan
Citations checked at: c329051 (3 short-form citations inside the locked Brief, lines for
confirm/SKILL.md:168,pr-verify.yml:18,driver.mjs:299-317, resolve underplugins/verification/skills/confirm/,.github/workflows/,plugins/harness-ops/skills/control-claude-code/driver/; left as the owner confirmed them)Settings:
phase_order: composed (default);scaffold_stubs: false (default);integration_posture: by-kind (default; docs/conventions/implementation.yaml absent). Standardsgrounding:
docs/plugin-philosophy.md:99-113(decoupling), the control-skill contract(
plugins/app-control/reference/control-skill.md),docs/conventions/pr-pipeline.yaml. Each sliceis one PR in one plugin (or the pipeline files), with the plugin's version and CHANGELOG bump per
the repository's release convention. Dead-code removal is folded into S2a/S2b (the two glob-parsing
copies), not a separate phase: it is only removable once
affectedexists.Load-bearing assumptions
driver.test.shon Ubuntu 24.04); confirmed on the runner by S7's first runapiKeySourceper credentialANTHROPIC_API_KEYrow (quoted in design)setup-runner-isolationcannot read the runtime token or credentialAlternatives Considered
/app-control:affectedas its own skillRisks and Mitigations
publishable, eval with a planted email line (S3, S4b)affected.jsonshapeworkflow.test.mjsequality to the capture download step (S7)merge-prandenable-auto-mergeand covers bothDecisions made (gate-passed)
docs/plugin-philosophy.md:99-113capture: no-appfrom day one.github/scripts/pr-verify/write-verdict.mjs:247-249already writes per-head verdictssource-control-babysit-merge.github/workflows/pr-verify.yml:80-83); merge-pr activity (docs/conventions/pr-pipeline.yaml:438-450)Phase 1: S1 app-control
affectedaction and--trusted-root[TODO]Satisfies: AC3, AC4
Size Medium. Depends on: none. Plugin: app-control. Design: Module layout (app-control), Contracts C1,
--trusted-root.sourcesindex parsing inplugins/testing,plugins/verification,plugins/app-controland record the parse paths.plugins/app-control/scripts/affected_features.py(+.test.sh, same shape asverify_pin.test.sh): reads a hub index andgit diff --name-only <base>...<head>, writesaffected.json.plugins/app-control/skills/drive/SKILL.md:affectedrow in Actions;--trusted-rootin "Find the control skill" steps 1-2; argument-hint.drive/reference/evidence.md:affected.jsonandtrusted_root.plugins/app-control/reference/control-skill.md:49-50:sourcesresolve against the project directory, or, for a plugin control skill, against the repository under verification; glob syntax fixed in the script (**crosses/,*does not), notfnmatch. A control skill present that matched nothing reportscontrol skill present, <n> files unmatched.drive/evals/evals.json: project skill match, plugin skill match (repo-root globs), no control skill → empty with note, PR-checkout control skill ignored under--trusted-root.bash plugins/app-control/scripts/affected_features.test.shexits 0 with casesplugins/*/SKILL.mdnot matchingplugins/a/b/SKILL.mdandplugins/**/xmatching nested paths;grep -n "affected" plugins/app-control/skills/drive/SKILL.mdshows the Actions row;/skill-quality:check drivePASS.Phase 2: S3 app-control masking contract and publishable set [TODO]
Satisfies: AC8, AC4
Size Medium. Depends on: S1 (same files). Plugin: app-control. Design: Contracts C2, C3.
reference/control-skill.md:## Masksection in the Hub table;<artifact>.txtsidecar rule in "Writing the Capture section";<changed files>placeholder in Environments.## Maskvalues:line naming config fields or commands that print them, e.g. the signed-in email and org), plus a generic credential pattern; drive scans sidecars and every publishable text file (report.public.*,e2e-summary.jsonis the consumer's to scan, S2a) with line wraps joined and box-drawing characters stripped. Video and.castare never publishable; evidence published is masked stills.drive/SKILL.mdEvidence +reference/evidence.md: sidecar scan,artifacts[].mask,publishable,capture_count,report.public.{json,md}; raw andsecrets_markentries never publishable.mapcheck reports a hub with image/video Capture and no## Maskline (skills/map/scripts/check-map.sh+ test).bash plugins/app-control/skills/map/scripts/check-map.test.shexits 0;grep -c "publishable" plugins/app-control/skills/drive/reference/evidence.md≥ 2.Phase 3: S2a run-e2e consumes
affected, unattended summary [TODO]Satisfies: AC2, AC3, AC4, AC5
Size Medium. Depends on: S1. Plugin: testing. Design: Module layout (testing), Contracts C4.
plugins/testing/skills/run-e2e/SKILL.md: Step 1 detects a control skill viaaffected; Step 2 Entry points and Step 4 touched features call/app-control:drive affectedwhen it resolves (remove the glob parsing atplugins/testing/skills/run-e2e/SKILL.md:118-121and:193-196); fallback line when app-control is absent;app=/base=arguments passed to drive with--unattendedand--trusted-root; writee2e-summary.json, scanned with drive's literal values and patterns before it joins the publishable set.affectedstub output; app-control absent →touched features not matched (app-control not installed); unattended with nothing affected →capture: not-applicable.grep -n "sources. globs" plugins/testing/skills/run-e2e/SKILL.mdreturns no matching-logic line;/skill-quality:check run-e2ePASS;/skill-quality:check validate-evals run-e2ePASS.Phase 4: S2b confirm consumes
affected, evidence loop and retention [TODO]Satisfies: AC3, AC5, AC6, AC7, AC10, AC11
Size Medium. Depends on: S1 (S2a for the summary shape, by contract only; no S3 fields used). Plugin: verification. Design: Contracts C5, T6, T10.
plugins/verification/skills/confirm/SKILL.md: replace the touched-feature parsing atplugins/verification/skills/confirm/SKILL.md:168withaffected, presence-gated: when/app-control:drivedoes not resolve, reporttouched features not matched (app-control not installed)and keep thee2e-*path categories (eval for that case); fresh verifier reads the full local run dir (never uploaded;publishablegates uploads only); at cap with the last verdict NEEDS WORK the manifest saysEvidence loop: 3/3 cap reached, last NEEDS WORK;evidence-loop.jsonkeyed by branch (evidence-loop.<branch-slug>.json), written atomically (write temp, rename), counting NEEDS WORK evidence verdicts on distinct heads (a rebase-only head judged NEEDS WORK counts), cap 3, at cap no loop-back and Next routes to the PR and pipeline park path; manifest lineEvidence loop: <k>/3; evidence dirs kept for the last 3 heads; capture not run → verdict notes why, never NEEDS WORK for that alone.context/outcome.md: report rows for capture status and loop count./skill-quality:check validate-evals confirm;grep -n "Evidence loop" plugins/verification/skills/confirm/SKILL.mdmatches.Phase 5: S4a control-claude-code unattended and credential [TODO]
Satisfies: AC2
Size Medium. Depends on: none (spike first). Plugin: harness-ops. Design: T12.
CLAUDE_CODE_OAUTH_TOKENset and withANTHROPIC_API_KEYset; recordapiKeySourceand the first screen (key-approval prompt or not). Write results into the PR body.SKILL.md: removeAttended runs only.(plugins/harness-ops/skills/control-claude-code/SKILL.md:8), state unattended runs on sessions it launches, keep no sign-in and no Desktop; link the Brief as superseding app-control: new plugin plus testing and debugging changes #7581's 2026-10-23 criterion.config/defaults.json:credentials.ci(variable names only),maxCaptures: 20.driver.mjs: under--unattended, keep exactly the named credential variable, Ready accepts the source the spike recorded for it (plugins/harness-ops/skills/control-claude-code/driver/driver.mjs:706-707), refuses two set at once;CLAUDE_CODE_HIDE_CWD=1at launch.node --test plugins/harness-ops/skills/control-claude-code/driver/driver.test.mjsexits 0 with new cases (unattended + named key accepted; two keys refused; attended still requiresnone);grep -c "Attended runs only" plugins/harness-ops/skills/control-claude-code/SKILL.md= 0;bash plugins/harness-ops/skills/control-claude-code/driver/driver.test.shexits 0 on WSL Ubuntu 24.04, with theapiKeySourceper credential recorded in the PR body.Phase 6: S4b control-claude-code masks and changed-plugin launch [TODO]
Satisfies: AC8, AC1
Size Medium. Depends on: S3, S4a. Plugin: harness-ops. Design: T3, Contracts C2.
SKILL.md## Mask: account/email/org lines, usage/limit lines, cwd, with an as-of Claude Code version and recheck trigger, plus a fixture from a real captured screen;values:for the signed-in email/org.maxEntryPoints(judgment: 6) in config. One Launch per changed plugin (singlepluginDirperplugins/harness-ops/skills/control-claude-code/driver/driver.mjs:96-100), with a test. Run/app-control:map refreshon the control skill sosourcescover hook scripts, skillreference//scripts/, and agents. Driverscreenshot/recordapply masks and write<file>.txtsidecars; Launch takes plugin dirs from<changed files>.driver.test.mjscase feeds a fixture buffer holdinguser@example.comand asserts the masked buffer passed to the renderer and the sidecar both lack it;/app-control:map check plugins/harness-ops/skills/control-claude-codereports no missing## Mask.Phase 7: S5a source-control evidence posting step [TODO]
Satisfies: AC8
Size Small-Medium. Depends on: S3 (publishable list). Plugin: source-control. Design: Contracts C6, T9.
skills/pull-requestgainsevidence(newreference/evidence.md): one marker comment linking the retained capture artifact (pr-capture-<pr>-<head>, expiry stated) plus the run's text summary. Nogh --attachfor evidence: attachments are user-attachment assets with no retention period, which AC10 forbids (probe:gh pr comment --helplists--attach, and edits only via--edit-last). Where no CI capture exists, the comment carries the text summary and says media was not uploaded (USER-RESERVED U3 may change this). The comment body is scanned with the same literal values and patterns before posting; a hit posts nothing. Upsert: list the PR's issue comments withgh api, find the marker, edit that comment by id (PATCH repos/{owner}/{repo}/issues/comments/{id}), else create. Movesplugins/playwright/skills/demo-video/reference/posting.mdcontent here. Updateplugins/source-control/skills/pull-request/reference/create.md:389Visual evidence (stale "no upload option").media not uploaded";/skill-quality:check pull-requestPASS.Phase 8: S5b demo-video calls the posting step [TODO]
Satisfies: AC8
Size Small. Depends on: S5a. Plugin: playwright.
grep -rn "posting.md" plugins/playwrightand list consumers (known:plugins/playwright/skills/demo-video/evals/evals.json:53).reference/posting.mdwith a presence-gated call to/source-control:pull-request evidence; demo-video's own person-requested demo attach (outside evidence verification) keeps its current fallback text. Update the eval at line 53.test ! -f plugins/playwright/skills/demo-video/reference/posting.md || grep -q "source-control:pull-request evidence" plugins/playwright/skills/demo-video/reference/posting.md;/skill-quality:check demo-videoPASS.Phase 9: S6 pipeline key and verdict evidence metrics [TODO]
Satisfies: AC10, AC11, AC5
Size Small-Medium. Depends on: none. Files:
docs/conventions/pr-pipeline.yaml,docs/conventions/pr-pipeline/pr-pipeline.schema.json,docs/conventions/pr-pipeline/README.md,.github/scripts/pr-verify/write-verdict.mjs(+ test). Design: C7, C8, T11.merge.verify-capture(appunset,retention-days: 30); schema and README "Verifier verdict" row.write-verdict.mjs:evidenceblock; with no capture summary file →capture: no-app;local_loopparsed from the trusted PR text'sEvidence loop: k/3line as data.node --test .github/scripts/pr-verify/write-verdict.test.mjsexits 0 withno-app,blocked, andlocal_loopcases; CIcheck-plugins/schema validation passes.Phase 10: S7 pr-verify capture job [TODO]
Satisfies: AC1, AC2, AC5, AC9, AC10
Size Large. Depends on: S2a, S4a, S4b, S6, and owner answers to U1 and U2 (USER-RESERVED). Files:
.github/workflows/pr-verify.yml,.github/scripts/pr-verify/prompt.md,workflow.test.mjs. Design: T8, sequence flow 2.setup-runner-isolation, a head-code step tries to read the runtime token and the credential from a hook; the job fails closed if either is readable.capture: sameifas.github/workflows/pr-verify.yml:73-77(fork heads never run), gates, read-only token,.baseplugins,--trusted-root .base,/testing:run-e2e unattended app=<merge.verify-capture.app> base=<base-sha>; uploads publishable files aspr-capture-<pr>-<sha>withretention-days; skip with note when the key is unset..github/workflows/pr-verify.yml:102-175).timeout-minutes: 30; the outer session runs through ci-workflows'run-claude-cliwith its own role, a tool allowlist and the drivemaxEntryPointscap (S4b); a capture timeout leavescapture: blockedand never stops the judge.check-intentgainsneeds: capturewith!cancelled();check-intent-subscriptionandcheck-intent-fallbackkeep their success chain (.github/workflows/pr-verify.yml:434-445) and download the same run's artifact by id. All three download intopr-verify-data/capture/; the prompt frames it untrusted and says a missing or blocked capture is never a FAIL reason, while a trusted-text manifest lineEvidence loop: 3/3 cap reached, last NEEDS WORKis a FAIL (AC7's park path then runs).merge.verify-capture.app: claude-codeand show one in-repo PR with a judged capture (AC1). Comment block at.github/workflows/pr-verify.yml:33-36updated (reviewed change: jobs now take an artifact).node --test .github/scripts/pr-verify/workflow.test.mjsexits 0 with cases: capture job permissions contain nowrite; the three judge jobs carry identical capture-download steps; captureifincludes the head-repository check; no job-levelalways()in pr-verify.yml; only check-intentneeds: capture; a probe step exists, runs aftersetup-runner-isolationand before any head code, and has nocontinue-on-error;actionlintpasses.Phase 11: S5c CI evidence comment from the merge gate [TODO]
Satisfies: AC1, AC8
Size Small-Medium. Depends on: S5a, S7. Plugin: source-control (
plugins/source-control/scripts/source-control-babysit-merge).merge-pr,enable-auto-merge) and post on each before merge; upsert the marker comment linkingpr-capture-<pr>-<head>with its expiry.kyle-sexton commentedon Oct 30, 2026
Owner decisions 2026-10-30: Q10 (CI credential): reuse pr-verify's existing credential legs, no dedicated key ("we already have pr-verify"); #11386 updated and agent-ready. Q12 (evidence media): approved artifacts are attached with
gh pr comment --attachin collapsed sections, format templated so it can change; video publishable under the same every-frame masking rule; AC10's retention now covers CI artifacts and run directories. #11387 and #11391 updated. Q11 (PR code inside the verdict run) still open.kyle-sexton commentedon Oct 31, 2026
Decision recorded.
Q11, PR code inside the verdict run. Asked this question on 2026-10-31, the owner replied with the standing direction and "Go w/ whatever this recommends". The direction does not name Q11, so what follows is the Issue Triage session's reading of it, on the evidence, under its §1.
pr-verify may hold a
capturejob that runs PR code, on in-repo heads only, with no credential in that job: a read-only token and no Claude or other secret. §10 says the only actors are ours and that holds against our own actors come off. §3 and §10 keep one boundary, a token or secret leaking, and a job with no secret in it leaves that boundary as it is. The judge jobs do not change: they run no PR code and read the capture output as untrusted data.If a capture cannot be built without a credential in the job that runs PR code, the build stops and this comes back to the owner as a new question, since that case is the boundary §3 keeps.
20 remaining items
kyle-sexton commentedon Nov 5, 2026
Done-when audit, 2026-11-05. This issue closes when the four slices named in the 2026-11-02 status land. Two have:
It stays open, with nothing for another lane to build here. Re-audit it when #11393 closes.
kyle-sexton commentedon Nov 6, 2026
Decision recorded. Owner, verbatim, in the App Driven PR Verify session on 2026-11-06: "yes go with recommendations, and as long as we're in line with this", followed by the standing direction. It answers the four interview questions below and replaces the 2026-10-31 Q11 reading (issuecomment-6547731014). That reading was the Issue Triage session's, not the owner's words; this record is the owner's.
.github/workflows/pr-verify-capture.ymlon main):PR_VERIFY_CLAUDE_API_KEYas its own OS user (pr-verify-capture.yml:388,:476).blocked(:1384-1398).:853).:31). Under standing direction §3 that credit is capacity, not cost, and the §3 boundary (a token or secret leaking, an account compromised, a bill) holds.Done when:
In CI, the mask post-check can resolve theNo longer a closure gate: per the owner decision of 2026-11-06 (issuecomment-6692967126), local capture is the main path and CI capture is a bonus; PRs 13802 and 13805 still land.values:source: a source that reports the driven session's own auth runs under the capture's Bash allowlist (todaypr-verify-capture.yml:1123-1170does not runcli/account.mjs, so every CI capture stays unpublished), with a test that a capture under the CI key reachespublishable.The App Driven PR Verify session grades each box against main next and posts the evidence.
kyle-sexton commentedon Nov 6, 2026
Done-when grade, 2026-11-06, against main
5e2439748aand PR 13731 headdab16abcd3. A fresh-context grader checked each box. I re-read rows 1 and 8 myself. No box is ticked yet./testing:run-e2e --unattendedon in-repo heads (pr-verify-capture.yml:922-924), and test PR #13719 recorded 12 captures. Gap: no screenshot reaches the judge (below).confirm/SKILL.md:169). CI runs run-e2e → drive, and pr-verify's judge takes confirm's verdict role. That matches the locked design's diagram; it does not match the criterion's wording.confirm/SKILL.md:167; run-e2e Step 2;app-control/scripts/affected_features.py; evalapp-control-absent-fallback-linedrive/SKILL.md:96-99; drive evalevals.json:8-12. In the chain, run-e2e records its own driving.write-verdict.mjs:116-145;prompt.md:85-87;confirm/SKILL.md:175; testwrite-verdict.test.mjs:151confirm/SKILL.md:222-256(evalevidence-loop-01);max-verify-fails: 3(pr-pipeline.yaml:1452-1458)confirm/SKILL.md:254-260(evalevidence-loop-02);prompt.md:128-131;verify-cap.mjs:1-12control-claude-code/SKILL.md:86-107;driver.test.mjs:909). The comment lands with PR 13731.pr-verify-capture.yml:101-105,:1234-1243; testworkflow.test.mjs:1352. A local run on a fork worktree is not covered.pr-capture-*keeps 1-30 days from config (pr-verify-capture.yml:177-191); rows in.github/artifact-retention.json. A local drive run directory keeps no period.evidenceblock (write-verdict.mjs:86-158).local_loopis only the author's PR-body line. A PASS verdict keeps 7 days, and nothing readscapture_count.The gap behind rows 1 and 8:
.claude/skills/control-claude-code-plugins/SKILL.md, has no## Masksection; it hands off only Launch, Ready, Capture and Cleanup.not-checked, so none is publishable, when the hub has no## Maskline (drive/reference/evidence.md:99-107).values:source (cli/account.mjs) exits 1 whenclaude auth statusnames no email or organization (driver.mjs:594). That would also leave capturesnot-checked.Next: this session fixes the hub's
## Maskhand-off and the key-authvalues:case, then grades rows 2, 9, 10 and 11 against the criteria's scope and ticks the boxes that hold.kyle-sexton commentedon Nov 6, 2026
Ticked 9 of 12 boxes in the done-when, each checked against main
d872db47df. The grade has the evidence table. I re-read the cited lines for rows 1 and 3 to 8.Four rows are partly met against the criterion's literal wording but met against the locked Brief's own scope (issuecomment-6534933364), which is the contract:
/testing:run-e2e unattendedon in-repo PR heads; only the run directory reaches the judge". So CI runs run-e2e → drive, and the judge takes confirm's verdict role, as designed.pr-verify-capture.yml:101-105,:177-191).capture,capture_countandlocal_loop(write-verdict.mjs:86-158). The design's contract C8 markslocal_loop"self-reported by the author".Still open:
PR_VERIFY_CLAUDE_API_KEYcredit; the owner chose to wait for the monthly refresh (hub, 2026-11-06). The code fix that makes screenshots publishable (a hub## Maskhand-off, and anone:values result under key auth) is in progress in this session.kyle-sexton commentedon Nov 6, 2026
Decision recorded. Owner, verbatim, in the App Driven PR Verify session on 2026-11-06: "Okay, yeah, go with recommendations here. I want to be safe."
This answers where UI screenshots come from, now that a subscription token cannot sit behind the CI key proxy. The terms are on legal-and-compliance: "developers may not collect, store, or intermediate Claude.ai credentials or session tokens". The same page allows "an end user signing in to the unmodified Claude Code binary with their own Claude subscription".
/verification:confirm→/testing:run-e2e→/app-control:drive→ the control skill. The fresh-context verifier judges the evidence against the acceptance criteria, and/source-control:pull-request evidenceattaches it to the PR. CI's judge reads that evidence.blocked, never failing a PR. No subscription token goes behind the key proxy, and no token goes into the sandboxed session.Effect on the done-when (issuecomment-6689952950):
values:box is no longer a closure gate. PRs 13802 and 13805 still land, so the bonus path works when a key has credit.kyle-sexton commentedon Nov 6, 2026
Decision recorded. Owner, verbatim, in the Lane 6 session on 2026-11-06, answering "Should the CI screenshot capture stay on as a bonus, or be switched off?" with option (a), "Keep it on as a bonus": "a"
This confirms issuecomment-6692967126: the CI key capture stays on as a bonus, and
merge.verify-capture.appstays set. PR 13814, which unset it, closes unmerged. The capture regression after PR 13770 is fixed under #13710.fix(app-control): reuse resolved mask values on drive's report rewrites
test(app-control): skip quoted heredoc bodies in the threads elevatio…
fix(harness-ops): mask any plan name in the Claude Code banner
kyle-sexton commentedon Nov 7, 2026
Done-when evidence (2026-11-07, origin/main). One more box is met.
Agent choice, not an owner answer; for the owning session to tick.
.github/scripts/pr-verify/workflow.test.mjs:1427test("the step that starts the app holds no credential in any env it inherits, and the real key reaches only the key proxy's start", ...). The same box is ticked on feat(pr-pipeline): run the project's app-control skill in pr-verify after the deterministic tests #8651. The test reads the step's environment statically.Left: the two capture rows (a UI-change PR captured and judged before merge; one redacted evidence comment). Both need a live capture, and none is recorded after 2026-11-06T19:44Z.
kyle-sexton commentedon Nov 7, 2026
Closing record. All 13 done-when boxes in the body are met.
/app-control:driveon the building machine's own login, with no key proxy.· Claudeis masked;.github/scripts/pr-verify/workflow.test.mjs:1427on main (evidence: issuecomment-6709893081).#8651 closes next, then #7580.