chnlich

plan-approval

Enforces explicit user approval at the CharlieBot master-to-user

chnlich 8 1 Updated 4w ago
GitHub

Install

npx skillscat add chnlich/charlie-bot/plan-approval

Install via the SkillsCat registry.

SKILL.md

Scope

  • This skill governs only the CharlieBot master-to-user approval boundary; a
    delegated task is already past that boundary.
  • Plan approval and runtime delegation authorization are separate boundaries. The
    approval interaction approves the settled plan terms; the runtime gate decides
    whether a later /delegate or /improve request has an active user-message
    authorization window.
  • A delegated worker, reviewer, or improve-loop worker does not present another plan
    and does not wait for another take off; it follows its delegated task contract.
  • A delegated agent still stops and reports a blocker, contract conflict, or required
    scope expansion.

The principle

The user judges before what they cannot judge after. Exactly two things need that
before-judgment: the design, because implementation builds on it and reversing it
later means redoing the work; and any action the user cannot take back afterward —
because something is destroyed, or because it has already reached other people.
Everything else the user can still correct after the fact, so it belongs to
execution — done autonomously, logged, and surfaced in the completion report,
which is where after-the-fact judgment happens.

approval object = sections 1 and 3 + 4.1 Schema + resolved Trade-offs + entries
promoted from Other Details.
These are the exact terms a "take off" approves. The BLOCK KIT comment
in prompts/plan_template.html is the canonical grammar for the plan surface; do
not restate it here.

Understanding

An understanding page precedes the plan when the master must first align the reading
(the trigger and its exemptions live in the master prompt).

  • The page is artifacts/understanding_<slug>_v<n>.html, reusing the head and style of
    prompts/plan_template.html. Six blocks: goal in one sentence carrying the why one level up; the
    deliverable through two or three concrete examples; acceptance criteria, each a trigger
    condition plus an observable behavior (EARS phrasing, "WHEN THE SYSTEM
    SHALL ", is an example shape, not a requirement); non-goals; numbered
    divergences rendered as div.fork blocks, each carrying the fork grammar's
    folded explainer (block kit section 5, prompts/plan_template.html); Linear.
  • The Linear block carries the whitelist gate, the two-path recall, the candidate or
    draft choice, the take off gate, and the identifier readback. It appears only for
    repos marked Linear-tracked on the memory entry charliebot/workspace-repos, read
    with charliebot memory query --topic charliebot while drafting; a repo not tracked
    carries no Linear block and no explanation. When the marker list is absent altogether
    the block states in one line that the whitelist is not configured, because an absent
    list and an all-untracked list are otherwise indistinguishable and the mechanism
    would fail silently. Two-path recall per the linear skill is presented as either a
    candidate list (identifier, title, state, project, each with a one-line relevance
    judgement) or a conclusion that nothing matches plus a ready-to-file draft; never pick
    a match silently, the user decides. The draft's fields are an English title and body
    (goal, deliverables, acceptance), team, project, assignee, and priority, resolved from
    the Linear defaults held in memory with priority defaulting to medium; a new ticket's
    project follows the closest candidate's project and is named in the draft so the user
    can change it. The write waits for take off per the master prompt's External System
    Writes rule, and the authorization may ride in the same message that answers the
    divergences. After the ticket is created, read its identifier back from the API and carry
    it as a header meta chip on the understanding page and on the plan that follows.
  • Don't guess: any point the request leaves unstated where different readings lead to
    different designs must appear as a numbered divergence; never silently pick a reading.
  • No how: an understanding contains no implementation mechanisms or technology choices;
    design content belongs to the subsequent plan.
  • Facts before design: when the fix follows from why something happened, settle the
    root cause before drafting and state the case for it in one block between goal and
    deliverable, each claim carrying its evidence anchor.
  • Not registered in the plan registry, no verify worker, and the understanding confirmation does
    not use take off. The user confirms by answering the numbered divergences in chat; unmentioned
    items take their recommendation. Confirmation is ordinary feedback and introduces no new
    approval token. External system writes follow the master prompt's External System Writes rule.
  • Before sharing, the page passes the cold-read gate (file-server skill) with --genre understanding and the originating request as the trigger, revised until answer (7) is
    none; the share message carries the same one-line result.
  • The subsequent plan carries the confirmed understanding's file path as a header
    meta chip; section 1 states the goal in its own words.
  • The page as it opens obeys the plan template's page budget (the BLOCK KIT comment in
    prompts/plan_template.html); the always-visible minimum here is the goal sentence and
    each divergence's question, recommendation, and trade lines.

Plan

  • Research the codebase first; present the plan as a decision surface following the
    template block kit, with numbered Trade-offs for the choices the user must judge.
  • Contract text (rule sentences the plan lands verbatim in prompt docs, skills, or task
    specs) is drafted to the standing-reality form and checked block by block before
    charliebot plan present: each sentence opens with what stands or works ("one post
    carries the whole reply up to the limit", over "in one post unless it exceeds the
    limit"), a bare prohibition is recast as the working practice it protects ("each rule
    keeps one home; other files reference it", in place of the ban it replaces), the
    exception arrives as a bounded case rather than the sentence's spine, and dashes give
    way to commas, colons, semicolons, or a restructure (code excepted).
  • Render the artifact per the USAGE note atop the BLOCK KIT comment in prompts/plan_template.html.
  • Run the cold-read gate (file-server skill) with --genre plan before every
    charliebot plan present or amend, trigger per the gate's rule, and revise until
    answer (7) is none. The hand-over message for a plan or understanding version carries
    one line with the cold read's result: the count of terms question 7 still lists (zero
    when clean) and the count the revisions cleared.
  • Register before presenting via charliebot plan present (verbs per charliebot plan --help). The artifact's status chip is a presentation-time snapshot; the plan registry is the live truth. Record the code baseline when the plan pins one.
  • An improve-loop takeoff plan follows this same contract; its approval object covers
    repo, goal, iterations, work branch, and merge-back — loop parameters with reasonable
    alternatives (iteration count, merge-back) make natural Trade-offs.
  • End with: "Say take off when ready to implement."

Feedback

  • Replies that touch the plan are feedback, not approval: Trade-off resolutions by
    number, added constraints, questions, changed terms. Fold each into the plan and
    re-present what changed, then wait. A revision registers with charliebot plan amend,
    which requires --note: the one line saying why this version differs from its
    predecessor. The note rides on the version record; the body stays the complete
    current design and carries no revision marks, and the reader gets the differences
    from charliebot plan diff.
  • A style finding on one passage stands for its pattern: sweep the whole artifact for that
    pattern and fold every fix into the same revision.
  • Only the user changes the goal. Versions answer feedback within the standing goal; a changed
    goal is a new plan — charliebot plan present opens the new lineage and the old one closes
    as superseded.
  • When the user asks to control an Other Details entry, promote it; it joins the
    approval object. Unmentioned Trade-offs take their recommendation at approval.

Approval

  • "take off" (case-insensitive) is the only trigger. Anything else — "go ahead",
    "LGTM", "ship it" — gets: "Say take off to confirm."
  • Trade-off resolutions may ride in the approval message ("1 default, 2 both, take
    off"): resolve them first; the approval covers the result.
  • Approval binds to the approval object as it stands: progress never expires it,
    change does. If an approval-object term must change mid-flight, surface it to the user;
    nothing forces a re-presentation, the user decides whether to revisit.
  • Verify is a required independent step; its results live in the verify thread's own log, never in the
    plan registry. approve records a takeoff unconditionally; the runtime delegation gate reads the chat
    log, not the registry, so approve bookkeeping does not gate execution.
  • After the user says take off, record it via charliebot plan approve.
  • After charliebot plan approve and before delegation, post one English comment on
    the plan's ticket carrying the design conclusion and the choices the user judged. One
    comment per plan lineage. The plan stage never creates a ticket; a plan whose work has
    no ticket posts nothing. Design changes after take off are reported to the user as
    deviations and are not written back to Linear.

Verify

  • Fixed order: draft → register with charliebot plan present → verify → revise → hand the
    plan to the user with its findings. Verify is a read-only repo-less delegation and runs on
    a registered plan, so the goal it measures against has one home and the plan is listed
    while the rounds run.
  • Verify checks fidelity — claims against evidence — and adequacy: whether the design,
    assumed accurate and implemented, entails what section 1 claims. Adequacy reads
    section 1 as the goal statement only; scope boundaries, thresholds, and exemptions
    stated in Context, section 3, 4.1, or Trade-offs bind adequacy equally, so section 1 never
    restates them. Quote the confirmed understanding's file path in the spec as the
    adequacy reference when one exists; otherwise quote the originating request. Without
    one of these, adequacy cannot judge section 1 against what was asked.
  • Every spec declares its scope, and a spec that declares neither is verified as full.
    The first round of a lineage declares full; a revision round answering findings
    declares delta and re-checks the declared terms, their dependents, and whether prior
    findings are closed. The declared terms are the output of charliebot plan diff
    quoted into the spec, not restated by the author. Adequacy's whole-plan refutation
    belongs to full rounds, so a revision that changes the design's structure declares
    full again.
  • Every round of one plan measures with the same ruler: the spec's Goal quotes the registered
    version's section 1 by path, and cites fidelity and adequacy as defined here. A round that
    wants a wider question puts that question to the user.
  • Adequacy findings are advisory. gap-design: says the mechanism looks short of the
    goal — weigh it and revise the design if it holds. gap-goal: says the goal itself may
    be misread — put that question to the user.
  • A revision answers findings by correcting the plan against the goal it was registered with;
    section 1 keeps saying what the first version said. When a finding argues the goal itself
    is the problem, that is a gap-goal: question for the user.
  • Report the adequacy findings when presenting the plan, so the user can weigh them too.

Runtime delegation authorization

This runtime contract is separate from the plan-approval interaction above. It is
derived statelessly from the existing chat event log and applies at the three
request-entry checkpoints — /api/internal/delegate, /api/internal/improve,
and /api/internal/session-message delivering an agent message to a worker node
(the same judgment the worker's creation applies). There is no check at Run
launch: once a Run is registered, review Runs, manual retries, startup recovery
and cron fires are never withheld by later conversation.

  • verify is always allowed without authorization and remains repo-less.
  • A real user event is ET.USER with string content. Scheduled-trigger events
    are ET.SCHEDULED_TRIGGER, not ET.USER, and are excluded on that basis.
    Agent-relay events (ET.AGENT_MESSAGE, injected cross-session via
    /api/internal/session-message) are excluded by type alongside
    scheduled_trigger: an agent_message neither mints nor revokes an
    authorization window. Nested
    tool-result events are not real user messages for authorization.
  • Match pre take off and take off independently, case-insensitively, after
    normalizing consecutive whitespace. The take off substring inside pre take off
    counts, so one message can create both windows.
  • A real pre take off authorizes every non-verify behavior for exactly 12 hours
    from its valid UTC event timestamp, survives later real user messages, and is
    restarted by a new pre token. Missing or unparseable pre timestamps fail closed.
  • Ordinary take off authorizes every non-verify delegation type without a count
    limit when the latest real user message contains it. The next real user message
    ends that window. Scheduled-trigger and nested tool-result events do not mint or cancel it.
  • Scheduled fires stay outside this user-facing gate. No authorization file, field,
    event, counter, lease, consumption marker, or TASK_DELEGATED authorization read is
    permitted; it remains delegation history/UI data.

Execution

  • Default to acting. This is the reversibility test: if you can undo an operation
    alone at similar cost and its effect stays within what you own (reaching neither
    other people nor systems they rely on), do it without asking.
  • When reality forces a change that leaves the approval object intact, make it,
    log it as a deviation, and keep going.
  • Between "take off" and the report there is nothing to ask: return to the user
    only at a checkpoint the plan assigns to the user, at an unapproved action that
    fails the test, or at a blocking failure. Progress updates are fine; requests
    for permission are not.

Report

  • At completion or at a blocking failure, account for every approval-object term:
    delivered or deviated. Each deviation is a retroactive Trade-off — approved term,
    what landed, one-line reason — accepted by default or reverted on request; the revert becomes a new delegation.