Skip to content

feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507

Open
9 of 15 issues completed

Description

@kyle-sexton

Problem

/app-control:drive consumes a project's control-<app> skill, but nothing in the marketplace generates one, interviews for one, maintains one, or keeps one current during ticket work. Two consumer repositories (a .NET Blazor Server web app and its separate identity server, in two repositories, several deployed environments, used from Claude Code and Cursor local and cloud multi-repo agents) built their control skills by hand over one long session. Most of what they learned is transferable and belongs in the plugins: as things a setup skill discovers, questions it asks, rules the generated skill carries, and warnings an agent keeps in mind.

The plugins must adapt to any repository, stack, organization, team and user. Ship none of the consumer's own choices (folder names, steps format, CLI language, environment names). The consumer's specifics below appear only as the example that exposed each point.

Gaps on current main (5a1fd8a76, 2026-10-23):

  1. No generator or maintainer for a control skill. plugins/app-control/ has one skill, skills/drive/SKILL.md. skills/drive/reference/control-skill.md describes the shape a control skill must have but nothing writes it. testing:map-features and testing:refresh-feature-map write and refresh only the testing plugin's own map.
  2. Two map formats for the same knowledge.
    • plugins/app-control/skills/drive/reference/control-skill.md:85-93: features/<feature>.md with ## Reset, ## States, ## Entry points (and Drive); hub index one line per feature (:24).
    • plugins/testing/reference/feature-map.md:79-133: a separate feature-map skill (default .claude/skills/feature-map, plugins/testing/skills/run-e2e/SKILL.md:139) with ## Parts, ## Entry points, ## Drive, ## Traps, its own index and a Doctor line (:51).
    • A repository serving both /app-control:drive and /testing:run-e2e keeps two maps, or breaks one.
  3. Nothing keeps the map current during ticket work. When a change touches a mapped feature or adds one, no step says to update the map in the same pull request. /verification:confirm already reasons about which feature-map entry points a change touches (plugins/verification/skills/confirm/SKILL.md:166-169) and /testing:run-e2e reads the map (plugins/testing/skills/run-e2e/SKILL.md:221-224), but neither updates it.
  4. Nothing says where a lesson goes. A lesson learned while driving could belong in a feature's Gotchas, the map contract, the repository's agent instructions, a path-scoped rule, or a personal file. instruction-placement and docs-hygiene:write-for-agents own that decision and the writing, but the verification flow never calls them (no mention in confirm/SKILL.md or run-e2e/SKILL.md).

Evidence

Verified this pass against origin/main at 5a1fd8a76:

  • git ls-tree of plugins/app-control: only skills/drive/SKILL.md; no map or setup skill.
  • Section headings of both map formats as cited above.
  • grep of verification/skills/confirm/SKILL.md and testing/skills/run-e2e/SKILL.md for instruction-placement and write-for-agents: no hits.
  • Issue search: no open or closed issue for a control-skill generator beyond app-control: new plugin plus testing and debugging changes #7581's decision (below).

Overlap with #7581 (open, the app-control thread): its decisions already include /app-control:map with create, refresh, check, replacing map-features and refresh-feature-map (D26, D52), and one generated control-<app> skill per app (D27). This issue does not reopen those decisions. It supplies the spec #7581 is labelled needs-spec for: the discovery and interview content, the generated skill's rules, the keep-current and lesson-routing steps, the multi-app and multi-repo ownership model, and the contract changes. Not on main yet either way.

Not verified this pass (the implementer must):

  • pstack's two skills and guide, read live: cursor/plugins pstack/skills/create-verification-skill, pstack/skills/maintain-verification-skill, and the guide section "Keep the verification skill honest". Claims about pstack below come from the reporting consumer.
  • Whether /app-control:drive and /testing:run-e2e can resolve grouped ids (<module>/<feature>).
  • Whether instruction-placement and docs-hygiene:write-for-agents can be called from the verification flow as proposed.
  • Points marked observed come from the consumer build (two private SETWorks pull requests the melodic side cannot read); everything needed is restated here.

Proposed approach

Principle: compose, don't absorb

The owner's direction: the new skills compose the skills that already own each concern and do not copy their content. A copy drifts. Absorb only what is specific to driving an app.

  • One maintain pass over one map contract, owned by whichever plugin owns the unified contract. Merge testing:refresh-feature-map into it rather than adding a second maintainer beside it (consistent with app-control: new plugin plus testing and debugging changes #7581 D26).
  • /verification:confirm and /testing:run-e2e call a "touched features" step the map owner provides: update the touched feature folders, add a folder for a new feature, drive each once.
  • Lesson routing calls instruction-placement to choose the surface and docs-hygiene:write-for-agents to write. The map skill decides only whether a lesson is about one feature (its Gotchas) or every feature (the contract).
  • Every composed skill is presence-gated, with a stated fallback when it is not installed, as the marketplace already does elsewhere.
  • One instruction surface per repository. The consumer keeps agent instructions in AGENTS.md; CLAUDE.md is an @AGENTS.md shim and Cursor reads AGENTS.md natively, so the pointer to the control skill went into AGENTS.md only. The setup skill discovers which surface the repository treats as the source of truth and writes only there.

What pstack does (to port, after reading it live)

  • Create: interviews the repository first and the user second; builds launch, doctor, drive, evidence and cleanup plus an executable helper; seeds three to five features; proves one end to end, including that the evidence survives cleanup.
  • Maintain: a single generic skill. Locates the project's own verification skill (several found: ask which; none: stop), reads each feature's source with one read-only subagent per feature, drives every feature live once, and sorts each problem as doc drift, driver gap or product regression. Edits only the skill's own directory, never product code, and ends clean, changed (one PR) or blocked. The guide says to run it daily from a scheduled automation.

Setup flow: discover, interview, generate, prove, maintain, keep current

Discover (read-only, from the repository and the machine):

Area What to find Example that exposed it
Surfaces Apps a person drives (web, API, CLI, desktop), and workers with no UI Main web app, admin web app, file viewer, APIs, a worker; two of them share one port
Run Documented start command, launch profiles, ports, one-instance limits The main and admin web apps both bind 5031
Ready A health endpoint, plus a signal that the instance uses the expected data A "runtime mode (Local)" log line; without it the app silently read deployed data
Config The configuration layering, and which override mechanism the app honors A Local run re-applies a local settings file over environment variables, so env-var overrides silently do nothing; only a gitignored per-developer file wins
Environments Names and URLs, verified by a read-only call, never from docs alone The repo's docs named cloud resources that do not exist; one repo's instructions omitted an environment that answers
Identity Where sign-in happens, the options shown, prompts on some runs only, tenant-dependent labels and options A separate identity server. The username label is per tenant ("Username or Email", "Email" or "Username"). Code sign-in shows only when the tenant enables passwordless. A passkey enrollment prompt appears on some sign-ins
Dependencies Services a journey crosses, their URL per environment, whether the app's actual target can be read back The app's sign-in challenge redirect reveals which identity server it really uses
Side effects Channels that reach real people, and whether "local" still sends The local app resolves live mail credentials
Shared systems Deployed systems a local run still writes to The local app signs in through the shared dev identity server, so a failed login counts toward lockout (10 attempts, 5 minutes) for everyone using that account
Data Tenancy, seeds, and the app's supported reset path Reprovisioning through the app's own admin page
Harnesses Existing E2E specs, test accounts, selector conventions A sibling Playwright suite
Evidence Logs, a log server, OTLP, and profiling by tier Blazor Server: the real work is on the server, so browser profiles mostly show the client runtime
Team vs personal What is committed and team-wide, what is per user, and the OS-standard per-user location Repo defaults < per-user config (OS application-data folder) < environment variables < flags
Related repositories Systems whose pages appear in a journey but live elsewhere, and whether agents can reach them Cursor cloud agents run multi-repo, so they reach both; a person may not have the second checkout

Interview (only what discovery cannot settle; one question at a time, with a recommendation):

  • Which apps, and which three to five features, to seed first.
  • Write policy per environment and per external system: unattended, needs a person's grant, or never. Classify by where the data lands, not where the app runs.
  • Test accounts: which to use and which must never be locked out. Credentials come from an environment variable or per-user config, never a flag and never a committed secret, unless the team declares a non-production credential shareable (the consumer did, for a team agent account).
  • The recipient allowlist for each side-effect channel, or "never send".
  • Flat map or grouped by module.
  • Ownership across repositories: which journeys this repository owns, and which belong to a related system's skill.
  • The driver and CLI language, defaulting to what the repository already uses.
  • Where agent instructions live (see the principle above).

Generate and prove:

  • Generate the hub, the CLI or helper, the per-app settings, the contract and the seeded features.
  • Prove it pstack's way: launch, ready, sign in, drive one feature, capture, stop. Then check that the evidence survived and that nothing the run started is still running.

Maintain: the generic pstack-style pass, offered on demand and as a scheduled run.

Keep current (a gap pstack also leaves open):

  • The generated contract carries a rule, and the repository's instruction surface carries a pointer: a change that alters a mapped feature, or a page another skill's steps include, updates that feature folder or shared step file in the same pull request and drives it once. A new user-facing feature adds a folder and an index line; a removed one deletes them.
  • Lesson routing: one feature, its Gotchas; every feature, the contract; every agent in the repository, the instruction surface; only you, your personal gitignored rules.

Several apps and several repositories, without duplication (observed)

The consumer gave each fact one owner:

Fact Owner How others use it
The CLI, the contract, the shared settings (timeouts, sign-in account, the app list) One skill in one repository Other app skills point to it. A future step is publishing the CLI as a pinned tool package, so no repository needs another checked out
An app's launch settings and environments The app's own skill (app.json), in the repository that builds the app The shared config lists only the app's name, skill and owning repository
Steps for a page The skill of the app that serves the page A journey crossing into it includes them by reference: "<app>:<name>"
Write policy Committed files only (the app's settings, then the repo defaults); prod refused in code No per-user layer can loosen it

Mechanics worth generalizing:

  • Cross-app includes. An include takes prefix (to include a file twice with unique step ids), with (parameter overrides; the wrong-password attempt reuses the identity server's credential steps with a different password), kind (mark every included step a write) and finally (always run, as the reset).
  • The other checkout's location is a per-user setting. Without it the CLI stops with a message naming the setting and the repository to clone. The owner called this graceful degradation and wants it kept.
  • One run file and one log per app, so two apps (the app and its local identity server) run at once.
  • An environment can extend another (for example a local app with a local identity server), and each environment names its dependency services, which ready checks: the service answers, and the app actually sends traffic to it.

Rules the generated skill should carry (observed, transferable)

  1. Feature and sub-feature. A feature is a user's goal; a sub-feature is a state or branch inside it. The passkey prompt during sign-in is a sub-feature; managing passkeys is its own feature, because the user reaches it on its own.
  2. Every entry point is listed and marked driven or not driven: <reason>. Proof through one entry point never stands in for another.
  3. A feature that cannot be driven says why and what would make it drivable (example: session expiry is disabled by the local config).
  4. A journey belongs to the app where the user starts and ends. Another service's pages are steps owned by that service's skill. A dependency service is a precondition that ready checks.
  5. Tenant- or configuration-dependent UI is a named precondition, or a tolerant selector (for example a label regex covering every tenant wording).
  6. Prompts that appear on some runs only use optional steps, not retries or sleeps.
  7. A write's reset runs even when the drive fails (finally steps).
  8. Never press the button that sends unless every recipient is allowed; stop at the form.
  9. The auth bootstrap reuses the mapped sign-in steps, so sign-in selectors live in one file.
  10. Sign out in a throwaway browser session, never in the one holding the saved session.
  11. Capture by tier for a server-rendered app. Browser trace, performance, CPU and heap; server CPU, heap and counters through the platform's diagnostics tools, attached to the real app process.
  12. Progressive disclosure. Every level is a folder whose README.md indexes the level below: modules, then features, then the feature folder holding its prose and its steps. Feature ids are paths (auth/sign-in). A flat map stays valid for small apps.
  13. Executable steps beside the prose, with unique ids that the prose, the evidence file names and the write grants all use. The prose never repeats selectors.

Pitfalls for a control CLI

Material for developer-experience:build-cli and for the contract's driver section:

  • Windows handle inheritance. Starting the app through a redirected child process passes the caller's handles to the app, so a caller piping the CLI's output hangs forever. Start the shell without inheriting handles (shell-execute, hidden window).
  • A composite command keeps --json stdout to one document. drive --start runs launch, ready and stop; their output goes to stderr.
  • The PID the tool records may be a wrapper, not the app. Here it was the shell. Find the real app process (by name and start time) before attaching a profiler.
  • Reject typos fast: unknown flags, repeated step ids, feature ids that are not paths, an app or environment that does not exist.
  • Name the override mechanism the app actually honors. Do not assume environment variables win.

Contract changes (both plugins, once unified)

  • Grouped ids (<module>/<feature>) resolved by /app-control:drive and /testing:run-e2e. Verify both resolvers before promising this.
  • One section set per feature: Sub-features, Entry points (each driven or not, with a reason), Reset, Drive (with a not-driven form), Gotchas. This replaces app-control's States and testing's Parts and Traps; decide the mapping when unifying.
  • An optional machine-readable steps file per feature, with unique step ids, includes, optional steps, finally steps, and cross-app includes. The steps format stays the consumer's choice.
  • Per-app settings in the app's own skill. Environments that name their dependency services and may extend another environment.
  • Step kinds and write grants keyed to the system a step changes; the write policy comes from committed files only.
  • A Capture section with a row per tier (browser and server).
  • Optionally, a step log the control CLI emits that /app-control:drive maps into report.json.
  • The hub may index modules instead of features. control-skill.md:24 today expects one index line per feature and a ## States section, so a grouped layout is one hop deeper than the contract describes.
  • Several apps in one repository: one control skill per app; the shared CLI, config and contract live once, and the hub says where.

Files likely to change

  • plugins/app-control/skills/drive/reference/control-skill.md and plugins/testing/reference/feature-map.md (unify or extract one shared contract; pick the source of truth).
  • New plugins/app-control/skills/map/ (or the name app-control: new plugin plus testing and debugging changes #7581 settles) with create, refresh/maintain and check actions.
  • plugins/testing/skills/map-features/, plugins/testing/skills/refresh-feature-map/ (retire into the map owner per app-control: new plugin plus testing and debugging changes #7581).
  • plugins/verification/skills/confirm/SKILL.md, plugins/testing/skills/run-e2e/SKILL.md (call the touched-features step; resolve grouped ids).
  • plugins/developer-experience/skills/build-cli/ (CLI pitfalls).

Acceptance criteria

  • One feature-map contract exists on main; control-skill.md and feature-map.md either are the same file or one points to the other with no duplicated section definitions.
  • An app-control skill generates a control skill: it runs repository discovery covering every row of the Discover table before asking the user anything, and asks only what discovery left open, one question at a time with a recommendation.
  • The generated skill passes a prove step that launches, readies, signs in, drives one feature, captures, stops, and then checks the evidence still exists and no process the run started is alive.
  • A maintain action reads each feature's source, drives every feature once, classifies each problem as doc drift, driver gap or product regression, edits only the control skill's directory, and ends clean, changed or blocked.
  • testing:refresh-feature-map no longer exists as a separate maintainer of a second map.
  • /verification:confirm and /testing:run-e2e invoke the touched-features step when a change touches a mapped feature, presence-gated with a stated fallback.
  • Lesson routing invokes instruction-placement and docs-hygiene:write-for-agents when installed, with a stated fallback when not.
  • The setup writes the control-skill pointer only to the instruction surface the repository treats as the source of truth (for example AGENTS.md when CLAUDE.md is an @AGENTS.md shim).
  • /app-control:drive and /testing:run-e2e resolve a grouped id <module>/<feature>, proved by a test or eval, and a flat map still resolves.
  • The contract defines the feature section set, the optional steps file (unique ids, includes, optional and finally steps, cross-app includes), per-tier Capture rows, environments with dependency services and extension, and write grants from committed files only.
  • The 13 rules above appear in the generated skill's guidance or the contract.
  • developer-experience:build-cli covers the five CLI pitfalls above.
  • No consumer-specific folder name, steps format, CLI language or environment name ships as a default.

Constraints and gotchas

Context

Source: local handoff item 20261023-211851-app-control-testing-control-skill-and-feature-map-contract-gaps.md (retired into this issue). Reported against app-control 0.2.0, testing 0.31.1, developer-experience 0.2.0, plus verification, instruction-placement and docs-hygiene.

Related: #7581 (app-control thread, map create/refresh/check decision), #8816 (app-control plugin with drive), #7195 (feature-map first-consumer pass), #8651 (pr-verify runs the control skill).

Activity

  1. added
    priority: mediumReal value, no hard deadline; normal backlog flow.
    work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.
    on Oct 23, 2026
  2. github-actions commented on Oct 23, 2026

    @github-actions
    Contributor

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

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

    Decision recorded. This issue's - [ ] criteria are already in its body, written by a listed human; the owner amended them on 2026-10-23 and set the merge order on 2026-10-24. Nothing new is decided here. It closes through its sub-issues (Track A slices and Track B), never by a lane run on it.

    Sources, quoted verbatim:

    [quote truncated; the full decision is at the link]

    #9507 (comment)

    That decision settles the open questions. I read this as a large feature: a setup skill that discovers, interviews, generates and maintains a control skill, with one unified feature-map contract. It is split into sub-issues, so it is tracked here rather than done as one change. No duplicates found; #7581 is the related thread.

    Automated triage, applied by the lanes App.

  3. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    status: needs-decisionAwaiting a human or maintainer judgment call.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Oct 23, 2026
  4. melodic-automation-lanes commented on Oct 23, 2026

    @melodic-automation-lanes
    Contributor

    Not admitted to the issue lane yet: Size large: the split into sub-issues at intake is pending (owner Q93).

  5. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    Owner decisions from the brief interview for this issue (2026-10-23), folded into one effort that closes this issue through a series of PRs (sub-issues), in two tracks: Track A (general skill updates) and Track B (attended Claude Code driving, shipped as control-claude-code in harness-ops).

    Acceptance-criteria amendments:

    • Lesson routing: "Lesson routing invokes instruction-placement and docs-hygiene:write-for-agents" becomes: the verification report lists lessons; a lesson about a feature or drive step goes to /app-control:map refresh, any other lesson goes to /corrections:codify, which places it by scope. This keeps one router.
    • Flat map: every feature is a folder (features/[<module>/]<feature>/README.md); the module level is optional, which is what "flat" means here. Existing features/<f>.md maps still resolve and map refresh migrates them.
    • Section set: Parts, Sub-features, Entry points, Reset, Drive, Gotchas (States becomes Sub-features, Traps becomes Gotchas, Parts kept for change-to-feature matching). Capture stays in the hub with per-tier rows.
    • shared/: allowed only for infrastructure used by two or more slices; map check reports items used by fewer.
    • Config: generated skills layer committed config/defaults.json < gitignored config/local.json < environment variables < flags (environment variables are the channel cloud agents have); credentials from environment variables by default. No runtime dependency on these plugins.
    • Safety and production: shipped defaults are safe (allowlisted recipients only, lockout budget respected, production read-only, no unattended production writes); only the team's committed config loosens them.
    • Keep current: /verification:confirm and /testing:run-e2e call /app-control:map refresh <feature> when installed, else report drift and name it.

    Out of this series: the CLI step log into report.json, and #7581's script fingerprinting (D52/D53).

  6. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    Slice drafts A1-A9 for this spec, handed over from the desktop poteto session (PLAN Phase 13) to the session that owns this PR series. These are drafts for the owner of this issue to file as sub-issues, not filed items.

    Two inputs to apply first:

    The B, C and D slices (run-e2e shrink, /testing:write settings, dogfood map), #9532/#9542, #9533 and #9534 stay with poteto.

    A1. refactor(app-control): unify the feature-map contract in one app-control reference

    • Problem. Two contracts describe the same thing (feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507 gap 2).
      • App-control's feature files use ## Reset, ## States, ## Entry points and ## Drive
        (control-skill.md:88-97). Its index has one line per feature (:24).
      • Testing's feature files use ## Parts, ## Entry points, ## Drive and ## Traps
        (feature-map.md:79-133). Its index adds Driver, Doctor, Isolation and Written-at lines
        (map-features/SKILL.md:140-148).
      • pstack's feature files use Sub-features, How to get to it (user POV),
        Driving it with <harness> and Gotchas (create-verification-skill:36).
    • Fix.
      • Move control-skill.md to the home Q2 picks, recommended
        plugins/app-control/reference/control-skill.md.
      • Repoint drive's two links.
      • Set the feature section set from Q3, recommended Sub-features, Entry points, Reset, Drive,
        Gotchas.
      • Every listed entry point is marked driven or not driven: <reason>, per feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507 rules 2 and 3.
      • Each index line gives the title, the file, the entry-point count and the source paths the
        feature covers. The source paths let a caller map changed files to features (see Q6).
      • Fold Isolation into the hub. Doctor is already ## Ready (D54).
      • No size limit yet (D52: it is measured on the dogfood map first; see D1 and D2).
      • Testing's feature-map.md is untouched here. It retires in A5, because pointing it at another
        plugin's path breaks docs/plugin-philosophy.md:472-474.
    • Done when.
      • test -f plugins/app-control/reference/control-skill.md && test ! -e plugins/app-control/skills/drive/reference/control-skill.md
        exits 0 (or the Q2 path instead).
      • The feature section list in that file is exactly the Q3 set, and ## States is gone.
      • Drive's before and after evals (14 cases, drive/evals/evals.json) are non-inferior per
        skill-criteria.md:186-191.
      • Both greps return nothing.
    • Blocked by. Q2, Q3.
    • Files. plugins/app-control/reference/control-skill.md (moved),
      plugins/app-control/skills/drive/SKILL.md (two links), drive/evals/evals.json if a case names
      States, fragment.
    • Size Small, Complexity Medium, Uncertainty Known after Q2 and Q3.
    • Lane: interactive. Removing ## States is a cut of drive's contract and needs the eval run.
    • Rating: not HIGH.

    A2. feat(app-control): add /app-control:map create

    • Problem. Nothing generates a control-<app> skill (feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507 gap 1). map-features writes only
      testing's map.
    • Fix. A new skill plugins/app-control/skills/map/.
      • Actions. A bare run proposes an action. create runs only on the explicit word.
        --unattended is supported. (D26, D52; app-control: new plugin plus testing and debugging changes #7581:25.)
      • create follows pstack's create (create-verification-skill:11-44):
        1. Interview the repository first, covering every Discover row in feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507.
        2. Ask the person only what discovery left open, one question at a time, each with a
          recommendation.
        3. Generate the hub, the Q3 feature files for three to five seed features, and an optional
          cli/.
        4. Prove it through /app-control:drive: launch, ready, sign in, drive one feature, capture,
          cleanup.
        5. Check that the evidence survived and that no process the run started is still alive.
        6. Say what a stand-in did that the real target will not.
      • Safety rules are hard requirements (feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507 "Constraints"):
        • Never press a send without every recipient allowed.
        • Respect a shared account's lockout budget.
        • Take write policy per environment and per external system from committed files only.
        • Run a write's reset even when the drive fails.
      • Instruction pointer. create writes the pointer to the control skill only to the instruction
        surface the repository treats as its source of truth (Q19).
      • Unattended. An --unattended create stops at the first question it cannot answer from the
        repository, with the list of open questions. It never guesses an answer to a safety question.
      • Evals port the 16 map-features cases and feature-map-writer-refuses-unsafe-location,
        reworded to the new output path, and add these:
        • Discovery answers come before any question.
        • A send step with an unlisted recipient stops at the form.
        • An unattended create with an open safety question stops.
    • Done when.
      • test -f plugins/app-control/skills/map/SKILL.md exits 0. Its argument-hint names
        create and --unattended, and its body says a bare run proposes.
      • Each ported case exists, and the old and new skills run concurrently on the ported cases
        are non-inferior per skill-criteria.md:186-191 (the baseline for A5).
      • The three new cases exist, and check-evals-quality.sh exits 0.
      • Both greps return nothing.
      • The skill names no folder name, steps format, CLI language or environment name as a
        default.
    • Blocked by. A1; Q4, Q9, Q19.
    • Files.
      • plugins/app-control/skills/map/SKILL.md, reference/discover.md, reference/interview.md,
        evals/evals.json
      • plugins/app-control/README.md
      • Generated rows in docs/catalog.md and docs/skill-cheat-sheet.md
      • Fragment
    • Size Medium (Large if A9's content is pulled in), Complexity High, Uncertainty
      Knowable (the prove step on a real app; whether drive can run the sign-in step unattended).
    • Lane: interactive. The uncertainty is Knowable, and the prove-step eval needs a browser
      (eval-browser-dispatch.yml).
    • Rating: HIGH (new skill frontmatter).

    A3. feat(app-control): add /app-control:map check

    • Problem. "If an agent updates the feature map, how do we know that it's correct?"
      (realign/audit-A.md:177; D52 check).
    • Fix. A read-only check action:
      • The index matches the feature files: none missing, extra, duplicate or dead (pstack maintain
        step 1).
      • Each feature has the Q3 sections.
      • Each entry point is marked driven or not driven: <reason>.
      • Each section is a pointer or a command, never a copied recipe (control-skill.md:103-104).
      • Every linked file resolves.
      • The output is a findings list with no verdict word, and the action never edits.
      • Model-run or script per Q10.
    • Done when.
      • The map body and argument-hint name check.
      • Three evals: a dead index line, a missing not driven reason, a copied recipe. Each is
        reported, and no file changes.
      • Both greps return nothing.
    • Blocked by. A2; Q10.
    • Files. map/SKILL.md, map/evals/evals.json, and map/scripts/ only if Q10 picks a
      script. Fragment.
    • Size Small, Complexity Low, Uncertainty Known.
    • Lane: fits if Q10 picks the model-run option. A script makes it interactive.
    • Rating: HIGH (argument-hint frontmatter; also scripts/ under the script option).

    A4. feat(app-control): add /app-control:map refresh

    • Problem. /testing:refresh-feature-map maintains only testing's map. pstack's maintain pass
      adds three things it lacks:
      • one read-only source reader per feature;
      • a live pass that holds three invariants;
      • the doc drift, driver gap and product regression triage (maintain-verification-skill:21-37).
    • Fix. A refresh action that ports refresh-feature-map's steps 1-6 and outcomes (clean,
      changed, blocked; at most one PR on a fixed branch) and adds:
      • Source wave. One read-only subagent per feature file. Children never drive and never edit.
      • Live pass through /app-control:drive. It drives every feature once and holds pstack's
        three invariants:
        • Re-run ready before driving again after a surprise.
        • Evidence survives every cleanup.
        • No residue outlives the drive.
      • Triage. Wrong prose is doc drift and gets fixed. A behavior the driver cannot reach is a
        driver gap and gets fixed. Broken app behavior is a product regression: report it and keep it
        out of the PR.
      • Edit scope. Only the control skill's directory.
      • Copied script. upkeep-skip.sh and its test are copied into map/scripts/. A5 deletes the
        testing copies.
      • Script fingerprints. refresh counts repeated script fingerprints and lands a proposed CLI
        verb in the refresh PR (Q97 item 52, interview-checklist.md:142).
      • Cadence. Daily, scheduled (decided; see "Decided").
    • Done when.
      • The map body and argument-hint name refresh.
      • bash plugins/app-control/skills/map/scripts/upkeep-skip.test.sh exits 0.
      • The 11 refresh-feature-map cases and feature-map-maintainer-confines-edits are ported and
        non-inferior to the old skill (run concurrently).
      • There is one new case per triage class.
      • Both greps return nothing.
    • Blocked by. A2, A3.
    • Files.
      • map/SKILL.md, map/reference/refresh.md
      • map/scripts/upkeep-skip.sh and .test.sh (mode 0755)
      • map/evals/evals.json
      • Fragment
    • Size Medium, Complexity High, Uncertainty Knowable (whether drive's report carries
      enough for triage).
    • Lane: interactive (new executables; eval runs).
    • Rating: HIGH (scripts/, frontmatter).

    A5. refactor(testing): retire map-features and refresh-feature-map for /app-control:map

    • Problem. After A2 to A4, the second generator, maintainer and format are dead weight, and
      the PLAN sanity check (PLAN.md:915) still fails.
    • Fix. Migrate every caller, then delete.
      • Delete in testing:
        • plugins/testing/skills/map-features/ and refresh-feature-map/, including
          scripts/upkeep-skip.sh and its test
        • plugins/testing/reference/feature-map.md
        • plugins/testing/evals/feature-map-*, already ported in A2 and A4
      • Retire feature_map_dir:
        • testing.schema.json:55-59
        • resolve-config.sh:68-69,229,290-320 and its test cases (resolve-config.test.sh:409-534
          per the earlier draft)
        • setup.sh:177
        • docs/conventions/config-cascade/README.md:652
        • run-e2e/context/e2e-config.md
      • Repoint run-e2e's "Feature map" subsection (run-e2e/SKILL.md:221-237). It finds the
        project's control-<app> skill by discovery, reads its hub index as project data, cites
        /app-control:map for the format, and keeps the per-entry-point report line that
        verification/skills/confirm/SKILL.md:166-169 depends on.
      • Repoint the other callers:
        • plugins/autonomy/reference/routines.md:247,343 (feature-map-upkeep to /app-control:map refresh)
        • plugins/verification/reference/config.md:86, schemas/verification.schema.json:19,
          .claude-plugin/plugin.json:34 and skills/setup/SKILL.md:55 (the wording names
          control-skill entry points)
        • docs/conventions/invocation-mode/README.md
        • docs/upstream/cursor-pstack.md mapping rows
        • The testing and verification READMEs
        • The catalog and cheat-sheet rows
        • Every ## Next that names either removed skill
      • Legacy maps. A legacy map at .claude/skills/feature-map gets the migration path from Q4.
    • Done when.
      • test -d plugins/app-control/skills/map && test -d plugins/app-control/skills/drive && test ! -e plugins/testing/skills/map-features && test ! -e plugins/testing/skills/refresh-feature-map
        exits 0 (PLAN.md:915).
      • rg -n 'map-features|refresh-feature-map|feature_map_dir' plugins docs --glob '!**/CHANGELOG.md' --glob '!docs/upstream/**'
        returns nothing.
      • bash plugins/testing/scripts/resolve-config.test.sh exits 0.
      • Run-e2e's evals that touch the map (run-e2e/evals/evals.json) are non-inferior before and
        after.
      • Both greps return nothing.
    • Blocked by. A4; Q4, Q5, Q6, Q17.
    • Files. As listed in Fix.
    • Size Medium, Complexity High, Uncertainty Known.
    • Lane: interactive (shell edits, 0755 deletions, eval runs). This is open question O3; see
      Q17.
    • Rating: HIGH (scripts/; the verification plugin.json userConfig description).

    A6. feat(verification): update touched features in the same PR through /app-control:map

    • Problem. No step keeps the map current during ticket work (feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507 gap 3).
    • Fix.
      • The contract gains the keep-current rule. A change that alters a mapped feature updates its
        file in the same PR and drives it once. A new user-facing feature adds a file and an index
        line. A removed feature deletes them.
      • /verification:confirm and /testing:run-e2e invoke the touched-features step (shape per
        Q6). The step is presence-gated, with a stated fallback: report the touched features as not
        updated.
    • Done when.
      • Both bodies name the step, the gate and the fallback.
      • Two evals: a change to a mapped feature names its file to update; with app-control absent,
        the fallback line appears.
      • Both greps return nothing.
    • Blocked by. A5, B1; Q6.
    • Files. plugins/app-control/reference/control-skill.md, verification/skills/confirm/SKILL.md,
      testing/skills/run-e2e/SKILL.md, evals, fragments.
    • Size Small, Complexity Medium, Uncertainty Known after Q6.
    • Lane: fits (additive markdown) unless Q6 adds a map action, which makes it HIGH and part of
      A3.
    • Rating: not HIGH.

    A7. feat(app-control): route lessons from map runs through instruction-placement

    A8. feat(app-control): resolve grouped feature ids in drive and run-e2e

    A9. feat(app-control): add steps files, cross-app includes and environment dependencies to the contract

    • Problem. These parts of feat(app-control): discover-and-interview setup that generates and maintains a control skill #9507's "Contract changes" are unbuilt:
      • the optional machine-readable steps file (unique ids, includes with prefix, with, kind
        and finally, optional steps);
      • per-app settings in the app's own skill;
      • environments that name dependency services and may extend another;
      • write grants from committed files only;
      • per-tier Capture rows;
      • several apps and several repositories with one owner per fact.
    • Fix.
      • Add each item to control-skill.md, keeping the steps format the consumer's choice.
      • Drive's ready checks each named dependency service answers, and that the app sends traffic
        to it.
      • map create interviews for ownership across repositories.
      • Expected split: A9a steps and includes; A9b environments, multi-app and multi-repo.
    • Done when.
      • Each listed item has a contract section.
      • An eval shows a missing other-checkout setting stops with the setting's name.
      • Both greps return nothing.
    • Blocked by. A2, A8.
    • Size Large (split before filing), Complexity High, Uncertainty Knowable.
    • Lane: interactive.
    • Rating: not HIGH unless drive's argument-hint changes.
    Design questions and recommendations (poteto, not owner decisions except Q3)

    Design questions

    Each question lists its options, a recommendation with its basis, and what it unblocks. Lauren
    means pstack (read live, see the top). Matt has no counterpart for the map, drive or control
    skill: "Matt adds none of these terms" (decision1/verification.md:218).

    Q1. Which issue is the parent of these slices?

    Q2. Which file is the single source of truth for the feature-map contract?

    • Context. Today there are two: drive/reference/control-skill.md and
      testing/reference/feature-map.md. Two skills in one plugin may cite each other's files with the
      full plugin-root form (docs/plugin-philosophy.md:461-471). No skill may cite another plugin's
      skill by path (:472-474). Lauren keeps one shape, in the generator's
      references/feature-map-example/ (create-verification-skill:36).
    • Options:
      • (a) A plugin-level plugins/app-control/reference/control-skill.md, read by drive and map.
      • (b) Keep it in drive/reference/; map cites it in plugin-root form.
      • (c) Put it in map/reference/; drive cites it.
      • (d) A convention doc under docs/conventions/, cited by URL.
    • Recommend (a). Neither skill owns it more than the other. The testing plugin already keeps
      its shared map format at plugin level (plugins/testing/reference/feature-map.md, read by three
      skills). Testing's copy is deleted in A5, and testing cites /app-control:map. Basis:
      plugin-philosophy.md:461-474; run-e2e/SKILL.md:221-222.
    • Alternatives. (b) is the smallest diff. (d) is reachable by any plugin, but an installed
      plugin cannot read it offline.
    • Unblocks. A1.

    Q3. Which sections does a feature file have?

    Q4. How do we migrate: build the new skill beside the old ones, or move in one PR? And what
    happens to an existing .claude/skills/feature-map?

    • Context. Lauren's principle-migrate-callers-then-delete-legacy-apis is in the pstack skill
      list (read live). A single move is one Large slice that the lane skips (dimensions.mjs:62-72,
      per the earlier draft). Consumers may already hold a map at the default path
      (testing.schema.json:57).
    • Options:
      • (a) Build /app-control:map beside the testing skills (A2-A4), then retire them (A5). check
        and refresh detect a legacy map and propose a one-PR conversion.
      • (b) One move PR. A legacy map is left alone, with a changelog note.
      • (c) Move first with content unchanged, then change the contract.
    • Recommend (a). Each slice merges alone. Eval baselines exist before the delete. Existing
      consumers get a path. Basis: the pstack principle; skill-criteria.md:186-189 (ship only what
      was measured).
    • Alternatives. (c) measures the move on its own. But feature_map_dir resolves through
      testing's script (resolve-config.sh:290-320), so an unchanged move cannot resolve its path from
      app-control.
    • Unblocks. A2-A5 order.

    Q5. Does testing keep a thin pointer to the map?

    • Context. Skill directories are decided: none (PLAN.md:915). This question covers only
      non-skill pointers.
    • Options:
      • (a) One README line in testing naming /app-control:map.
      • (b) None. The catalog and changelog suffice.
      • (c) A stub skill for one release. This is ruled out by PLAN.md:915.
    • Recommend (a). It costs one line and points users at the move. Basis: PLAN.md:915;
      judgment.
    • Unblocks. A5.

    Q6. How do run-e2e and confirm choose the touched features without reading another plugin's
    file?

    Q7. What does run-e2e do when app-control is not installed?

    • Context. check-visual-parity uses drive when it is among the available skills and otherwise
      keeps its own route (check-visual-parity/SKILL.md:39-40). The tool-per-job rule is to name the
      tool and stop if it is missing (Q569, interview-checklist.md:614).
    • Options:
      • (a) Stop with the gap report naming /app-control:drive.
      • (b) Keep the current launch-and-drive flow as a fallback.
      • (c) Declare app-control a plugin dependency (dependencies in plugin.json, as
        plugins/playgrounds/.claude-plugin/plugin.json:17-22 does).
      • (d) Fall back to the bundled run skill for launch only.
    • Recommend (c), with (a) as the runtime message if the dependency does not resolve. It keeps
      one driving path and installs drive where testing goes. Basis: the playgrounds precedent; Q569.
      Whether dependencies auto-installs a sibling from the same marketplace is UNVERIFIED; read
      https://code.claude.com/docs/en/plugins-reference before deciding.
    • Alternatives. (b) keeps two drivers, which the shrink exists to remove.
    • Unblocks. B1.

    Q8. Where do e2e_driver, reuse_running_instance, browser_driver, recording and
    browser_mode live once drive drives?

    • Context. Drive reads no settings file (drive/SKILL.md:48). The tool-per-job setting is a
      named default read through the adapter (Q569). The generated control skill names its tools
      (control-skill.md:21).
    • Options:
    • Recommend (a). The control skill becomes the one place a run's tool is named. Basis: D54;
      Q569; drive/SKILL.md:64-67.
    • Alternatives. (b) needs no migration, but drive grows arguments. (c) waits on config-cascade: settings resolution and convention home #7571, which
      is still open.
    • Unblocks. B1, B2.

    Q9. Does app-control read a settings file before #7571 lands?

    Q10. Is map check run by the model or by a script?

    • Options:
      • (a) The model runs it: read-only steps in the body.
      • (b) A deterministic script (index-to-file match, section presence, the not-driven marker), with
        the model judging "copied recipe".
      • (c) The script only.
    • Recommend (b), in a later slice. Ship (a) first so A3 is lane-fit. The counts are
      deterministic work. Basis: judgment; /discipline:script-the-deterministic-work.
    • Unblocks. A3.

    Q11. Grouped ids: do we need them before the dogfood map, and does the hub index modules?

    Q12. Dogfood: what is "the app", and what drives it?

    • Context. The surface here is Claude Code running a skill. eval-browser-dispatch.yml already
      runs claude plugin eval on a hosted runner.
    • Options:
      • (a) claude -p with --plugin-dir per feature, with stream-json as evidence.
      • (b) claude plugin eval on one case per skill as the drive step.
      • (c) The run-e2e non-ui playbook (run-e2e/context/non-ui.md).
    • Recommend (b). The cases already exist, and the CLI isolates runs. Basis:
      eval-browser-dispatch.yml:1-21; judgment.
    • Unblocks. D1, D3.

    Q13. Which mechanism runs the per-PR live smoke?

    Q14. Does the K36 probe gate the refactor default, and which result flips it?

    • Context. PLAN.md:894-896 says the probe gates the default on. c17-sweep.md:34 says the
      default is on (Beck's cycle) "until the K36 eval". topic10/report.md:126 says to drop the
      step "only if that eval shows agents skip it". Matt removed the step; Lauren has none
      (c17-sweep.md:34).
    • Options:
      • (a) Ship C2 with on after C1 regardless. The result is recorded and revisits only on evidence
        that agents skip it.
      • (b) on only if the with-skill arm ran Refactor in a majority of runs, else off.
      • (c) Ship C2 now with on and run C1 afterwards.
    • Recommend (a). It follows topic10/report.md:126. The gate is that the result exists, not a
      threshold, and C2 still waits on C1 as PLAN requires.
    • Alternatives. (b) is stricter and needs a stated N.
    • Unblocks. C2.

    Q15. How do the write settings resolve, and at which scope?

    • Options:
      • (a) Add the two keys to testing's existing cascade (resolve-config.sh), repo scope only, with
        no userConfig.
      • (b) Repo scope plus userConfig.
      • (c) The skill reads docs/conventions/testing.yaml itself.
    • Recommend (a). It is one resolver, at the scope the sweep set. Basis:
      resolve-config.sh:1-17; c17-sweep.md:34 ("scope repo").
    • Alternatives. (c) is a second way to read the same file.
    • Unblocks. C2.

    Q16. Threads: should analyze-profile's description name thread dumps in T1?

    • Context. The description lists only Chrome trace, cpuprofile and heapsnapshot
      (analyze-profile/SKILL.md:2). A description change is measured separately
      (skill-criteria.md:179-189) and is frontmatter (HIGH).
    • Options:
      • (a) Leave it in T1 and rely on drive's ## Next routing. Change it later with invocation
        probes.
      • (b) Change it in T1.
      • (c) Never change it.
    • Recommend (a). Basis: skill-criteria.md:179-189; drive/SKILL.md:116-119.
    • Unblocks. T1's rating.

    Q17. Who runs the map move? (Open as O3, realign/MORNING-REPORT.md:420-423; no GUIDE row.)

    • Options:
      • (a) An interactive session's PR for each of A4 and A5.
      • (b) Split into lane-fit pieces.
    • Recommend (a). The lane cannot add or move executables, edit resolve-config.sh or run
      evals. Basis: the lane facts above.
    • Unblocks. A4, A5.

    Q18. Which skill chooses the surface for a lesson?

    • Context. /instruction-placement:audit is a sweep, not a one-lesson chooser (its
      description). /docs-hygiene:write-for-agents writes agent markdown.
    • Options:
      • (a) map decides the scope; /docs-hygiene:write-for-agents writes; instruction-placement
        only on a periodic audit.
      • (b) Add a one-lesson mode to instruction-placement.
      • (c) map writes every lesson itself.
    • Recommend (a). It needs no new mode, and placement stays audited. Basis: both skills'
      descriptions in this session's listing.
    • Unblocks. A7.

    Q19. Where does create write the pointer to the control skill?

  7. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    For the laptop session: the A1-A9 drafts are the comment above (#9507 (comment)). The owner's Parts ruling (#7581 (comment)) supersedes the section set in A1: Parts, Sub-features, Entry points, Reset, Drive, Gotchas. Split confirmed: the A slices and Track B are yours.

  8. 4 remaining items

  9. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    Merge order for plugins/testing/skills/run-e2e/SKILL.md (reply from the desktop poteto session): I'd use a different order. B1 (the run-e2e shrink) is blocked by A5 in the slice plan. B1 drops run-e2e's driving and its map section, and the map section only goes once A5 retires map-features and refresh-feature-map. Proposed:

    1. A5 (refactor(testing): retire map-features and refresh-feature-map for /app-control:map #9585) merges first (yours).
    2. B1 (mine; I file it once A5 merges) rebases onto A5 and shrinks run-e2e to choosing entry points and reporting through /app-control:drive.
    3. A6 (feat(verification): call /app-control:map refresh for touched features #9586) and A8 (feat(testing): resolve grouped feature ids in run-e2e #9587) start after B1 merges and rebase onto the shrunk file. Their run-e2e edits shrink too, since the driving text they would touch is gone.
      If A5 slips, I can do B1's non-map part first, but that means two passes over the file. Send me a message when A5 merges.
  10. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    Owner decision 2026-10-24 on the plugins/testing/skills/run-e2e/SKILL.md merge order: we adopt poteto's proposal (#9507 (comment)).

    1. A5 (refactor(testing): retire map-features and refresh-feature-map for /app-control:map #9585) merges first, after A2 (feat(app-control): add /app-control:map create with config, driver CLI and safety sections #9581, PR feat(app-control): add /app-control:map create with config, driver CLI and safety sections #9628) and A4 (feat(app-control): add /app-control:map refresh #9583).
    2. B1 (poteto) rebases onto A5 and shrinks run-e2e to choosing entry points and reporting through /app-control:drive.
    3. A6 (feat(verification): call /app-control:map refresh for touched features #9586) and A8 (feat(testing): resolve grouped feature ids in run-e2e #9587) start after B1 merges.

    The laptop session will message poteto when A5 merges. #9585 keeps status: needs-decision until then only to keep the lane from picking it up early; its hold reason is now 'after A2 and A4', not 'after B1'.

  11. kyle-sexton commented on Oct 25, 2026

    @kyle-sexton
    MemberAuthor

    A5 has landed (D17 order): #10033 merged as 2640984.

    • /testing:map-features, /testing:refresh-feature-map and plugins/testing/reference/feature-map.md are gone. feature_map_dir is retired.
    • run-e2e's "Feature map" subsection now reads the project's control skill. It warns on a legacy map and names /app-control:map refresh <path>.

    B1 (run-e2e shrink) can start from main. For the desktop session (poteto): this is the D17 hand-off.

  12. kyle-sexton commented on Oct 25, 2026

    @kyle-sexton
    MemberAuthor

    Decision recorded. This issue's - [ ] criteria are already in its body, written by a listed human; the owner amended them on 2026-10-23 and set the merge order on 2026-10-24. Nothing new is decided here. It closes through its sub-issues (Track A slices and Track B), never by a lane run on it.

    Sources, quoted verbatim:

    Body criteria as amended by those decisions (only the amended lines; the rest of the body's list stands as written):

    • The verification report lists lessons; a lesson about a feature or drive step goes to /app-control:map refresh, any other lesson goes to /corrections:codify (replaces the body's "Lesson routing invokes instruction-placement and docs-hygiene:write-for-agents" line).
    • /verification:confirm and /testing:run-e2e call /app-control:map refresh <feature> when installed, else report drift and name it.
    • Every feature is a folder, features/[<module>/]<feature>/README.md, with the module level optional; an existing features/<f>.md map still resolves and map refresh migrates it.
    • The contract's feature section set is Parts, Sub-features, Entry points, Reset, Drive, Gotchas.
  13. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    and removed
    status: needs-decisionAwaiting a human or maintainer judgment call.
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 25, 2026
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.status: readyTriaged, unblocked, and fully specified; eligible to pick up.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Fields

    No fields configured for Feature.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions