Skip to content

feat(app-control): publish masked CI captures through a hub Mask hand-off and a none: values result #13757

Closed
3 of 3 issues completed

Description

@kyle-sexton

Refs: #11364
Parent: #11364 (done-when rows 1 and 8). session: App Driven PR Verify

Problem

The CI capture publishes no screenshot, so the pr-verify judge sees only run-e2e's pass and fail lines. The #11364 grade shows it: run 42235412941 recorded 12 captures and uploaded 1 file. There are two causes:

  1. The hub has no ## Mask. .claude/skills/control-claude-code-plugins/SKILL.md has no ## Mask section, so drive's post-check marks every capture not-checked (plugins/app-control/skills/drive/reference/evidence.md:99-107).
  2. Key auth has no account values. claude auth status --json reports "authMethod": "api_key" with no email or orgName (probe 2026-11-06, Claude Code 2.1.319). plugins/harness-ops/skills/control-claude-code/cli/account.mjs then exits 1, and drive treats a failed values: source as not-checked (evidence.md:79-82).

Design (decided; owner decisions on #11364 issuecomment-6689952950 settle the mechanisms)

  • Hand-off, not a copy. The hub's ## Mask hands off to the driving control skill's ## Mask, as the hub's other sections already hand off (hub SKILL.md:10-12). The 12 mask lines stay in one place (owner standing direction §6).
    • Resolution rule: the target is the hub named on the hub's Driving tool: line.
    • Trusted root: --trusted-root / trusted-root= covers the target too, so a PR head cannot supply its own masks or values source.
    • Unreadable target: a target that cannot be read leaves every capture not-checked.
  • The none: result. A values: cli/ source may print exactly one line, none: <reason>, and exit 0. That source then resolves with no values; this reuses the ## Mask none: grammar (plugins/app-control/reference/control-skill.md:215).
    • The check applies to each source on a values: line.
    • Anything else stays not-checked: a non-zero exit, empty output, or extra lines alongside none:.
    • The reason is recorded in report.json as values_none: [{source, reason}].
  • account.mjs prints none: only for key auth. It prints none: API-key auth has no signed-in account and exits 0 only when authMethod is api_key with no email and no organization. An OAuth session with no identity still exits 1. accountValues keeps returning an array: the driver masks with it at capture time (driver.mjs:716-718).

Work: one PR per plugin, in this order

  1. app-control (contract):
    • reference/control-skill.md "The Mask section": the hand-off line, its resolution and trusted-root rule, and the none: source result.
    • drive/reference/evidence.md "Mask post-check": follow the hand-off, accept none: per source, record values_none.
    • map/scripts/check-map.sh (and its test): accept the hand-off, and run mask-missing against the resolved target. The fixture is a hub whose Capture lists screenshot and whose Mask hands off.
    • Drive evals:
      • hand-off → applied;
      • unreadable target → not-checked;
      • one none: source → publishable;
      • none: plus a resolved source → the values are scanned;
      • none: plus an extra line → not-checked.
    • Version bump and changelog fragment.
    • The repo hub's ## Mask hand-off line ships in this PR.
  2. testing (run-e2e/SKILL.md, Step 5 and lines 124-127):
    • Follow the hand-off when it reads values:, under the same trusted-root rule, and accept none:.
    • Eval: the hub hands off and the target's values: source fails → e2e-summary.json stays out of publishable.
    • Version bump and changelog fragment.
  3. harness-ops (control-claude-code):
    • cli/account.mjs prints none: for key auth. Tests at the account.mjs level: key auth with no identity → the none: line, exit 0; OAuth with no identity → exit 1 and empty stdout; unparsable JSON → exit 1.
    • A rewritten key-auth /status fixture, every value invented per .claude/rules/eval-case-transcripts.md, and a maskCells test with no values.
    • Version bump and changelog fragment.

PRs 1 and 2 change skill bodies, so they carry do-not-merge until a valid before/after eval lands (hub rule R379/R381).

Done when

  • The app-control PR merges, with drive evals for the hand-off, the unreadable target, a lone none:, mixed sources and an extra line.
  • The testing PR merges, with the run-e2e eval in which a failed values: source behind the hand-off keeps e2e-summary.json out of publishable.
  • The harness-ops PR merges, and node --test plugins/harness-ops/skills/control-claude-code/driver/driver.test.mjs exits 0 with the account.mjs and key-auth fixture cases.
  • grep -n "^## Mask" .claude/skills/control-claude-code-plugins/SKILL.md matches on main.
  • bash plugins/app-control/skills/map/scripts/check-map.test.sh exits 0, including the hand-off fixture case.

The live CI capture that shows a published, masked screenshot reaching the judge is #11364's rows 1 and 8. It waits on PR_VERIFY_CLAUDE_API_KEY credit.

Out of scope

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 6, 2026
  2. kyle-sexton commented on Nov 6, 2026

    @kyle-sexton
    MemberAuthor

    Claim: the App Driven PR Verify session is building this now. It is three PRs, one per plugin, as the Work section says: app-control and harness-ops in parallel, then testing. Other lanes, please leave it.

  3. github-actions commented on Nov 6, 2026

    @github-actions
    Contributor

    Proposed work class: work-class: structural
    Basis: automated intake triage; the lanes App applied this class.

    Size: Large
    Complexity: Medium
    Uncertainty: Known
    Flags: none
    Blocked by: none

    Understood: the CI capture publishes almost no screenshots because the hub has no ## Mask hand-off and key auth yields no account values. The issue asks for a hub-to-control-skill Mask hand-off, a none: result for values: sources, and account.mjs printing none: for key auth. The work is three PRs, in app-control, testing and harness-ops.

    The design and done-when items are exact, so nothing is missing. I found no duplicate among the open issues. #11364 is the parent. The live CI capture check depends on credit and sits with #11364.

    Automated triage, applied by the lanes App.

  4. added
    priority: mediumReal value, no hard deadline; normal backlog flow.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Nov 6, 2026
  5. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.
    on Nov 6, 2026
  6. melodic-automation-lanes commented on Nov 6, 2026

    @melodic-automation-lanes
    Contributor

    Not admitted to the issue lane yet: sub-issue #13759 is open.

  7. melodic-automation-lanes commented on Nov 6, 2026

    @melodic-automation-lanes
    Contributor

    Split at intake (owner Q93). This issue is Size Large, so its done-when is split into 3 sub-issues, each holding its task-list lines as written.

    Automated split by the lanes App. Admission waits on this issue while a sub-issue is open; work-unblock closes it as completed once every sub-issue is closed as completed by a merge or a listed human and its done-when is covered.

  8. kyle-sexton commented on Nov 6, 2026

    @kyle-sexton
    MemberAuthor

    Re-scope after the owner's local-capture decision (#11364 issuecomment-6692967126) and the CI capture switch-off (PR 13814).

    This tree still applies, and not only to CI. A local capture goes through the same drive mask post-check (plugins/app-control/skills/drive/reference/evidence.md, "Mask post-check"), and the post-check marks every capture not-checked, so unpublishable, when the hub has no ## Mask. Without the hand-off, local screenshots cannot reach the evidence comment either.

    The only part that is CI-only is the key-auth none: result. It costs nothing locally, where account.mjs returns the signed-in email and organization. Nothing here is moot, so nothing closes early. This parent closes when #13758 and #13759 close (R233).

  9. melodic-automation-lanes commented on Nov 7, 2026

    @melodic-automation-lanes
    Contributor

    Every sub-issue of this split is closed as completed by a listed human or a merge (#13758, #13759, #13760), so the lanes App closes it as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: mediumReal value, no hard deadline; normal backlog flow.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Fields

    Work status

    ready

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions