# Uat Signoff

> UAT Sign-off

- Skill: `intense-visions/uat-signoff` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add intense-visions/uat-signoff`
- Raw SKILL.md: https://api.skillmd.com/api/skills/intense-visions/uat-signoff/raw
- Safety review: pending (external: skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Intense-Visions (https://skillmd.com/u/intense-visions)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/intense-visions/uat-signoff

---

# UAT Sign-off

> Walk a human through user-acceptance testing for a shipped change, one acceptance item at a time, and record their decision. The terminal, human-authority stage of the change lifecycle under `docs/changes/<slug>/`: the spec, plan, code review, and outcome-eval all key on the same slug; this captures whether the shipped reality is accepted against the change's Success Criteria. The human is the authority — this skill never judges, it records.

## When to Use

- At the **acceptance / outcome** edge of a change, once work has shipped and a human (product owner, solution architect, or sponsor) needs to sign off on it against the agreed Success Criteria.
- When you want a durable, auditable record of "did the human accept what we shipped?" tied back to the change's own `docs/changes/<slug>/proposal.md`.
- When the acceptance decision is a **human judgment** — a person accepts, rejects, or asks for changes — not a machine verdict.
- NOT for spec-vs-implementation judgment. `harness-acceptance-eval` (is the spec measurable?) and `harness-outcome-eval` (did the code satisfy the spec?) are LLM-judged, authority-derived, and merge/ship-blocking. UAT is **intent-vs-shipped-reality, human-judged, and advisory**.
- NOT for authoring or changing requirements — that is `harness-brainstorming` (spec) and `harness-planning` (plan). This skill reads the proposal's Success Criteria; it never rewrites them.
- NOT a gate. This skill records a decision; it does not block a merge, a ship, or a pipeline step.

## Process

### Iron Law

**The human makes the verdict; the skill only records it.** Never infer, guess, or auto-fill an ACCEPT/REJECT — not from the code, not from tests, not from an LLM. If the human has not ruled on an item, it stays open; you ask. Recording a decision the human did not make is the one unforgivable failure of this skill.

---

### Phase 1: RESOLVE — Load the acceptance basis

1. **Resolve the change slug.** From the `slug` argument, or ask the human for the short name. This sets `docs/changes/<slug>/` — the same slug the rest of the lifecycle (spec, plan, code review, outcome-eval) already uses.
2. **Read the acceptance basis.** Read `docs/changes/<slug>/proposal.md` and collect the acceptance-bearing items:
   - From the `## Success Criteria` section, collect each criterion as an acceptance item (this is the same section `harness-outcome-eval` judges against). Reuse each criterion's id (e.g. `SC3`) verbatim; do not renumber.
   - As supporting context, skim `docs/changes/<slug>/plans/` and any prior review/outcome-eval records under the change directory so you can show the human what was planned and what the machine gate already found — but the checklist itself is the Success Criteria.
3. **Soft-degrade when the basis is thin.** If `proposal.md` is absent, say so plainly and offer to run acceptance against a manually supplied checklist. If `proposal.md` exists but has no `## Success Criteria` section, fall back to `## User-Visible Behavior` then `## Overview`, and record which section you used. Do not fail — record what is missing and continue.
4. **Assemble the checklist.** Produce one scannable list of acceptance items, each with a stable id (reuse the Success-Criterion id — e.g. `SC3` — verbatim; do not renumber). Present the full list to the human first so they can see the scope before ruling.

---

### Phase 2: INTERVIEW — One item at a time

1. **Present ONE item at a time, in plain text.** Show the item, its source (the `proposal.md` Success-Criterion id), and what "accepted" means for it. Then ask for the human's disposition.

   **Ask directly in your reply. Do NOT route the question through any tool** (`emit_interaction`, `AskUserQuestion`, or similar) — those do not reliably surface the question to the human. Plain text in your own message is the only channel that reaches the signer across every client (Claude Code, Cursor, Codex, Gemini CLI).

   Present each item as a scannable choice with the three dispositions:

   ```markdown
   ### Item SC3 — Okta OIDC authentication (source: proposal.md Success Criteria)

   Accepted means: end users sign in via the client Okta tenant and land authenticated.

   |             | ACCEPT          | CHANGES_REQUESTED       | REJECT                     |
   | ----------- | --------------- | ----------------------- | -------------------------- |
   | **Means**   | Works as agreed | Close, needs a fix      | Does not meet the intent   |
   | **Records** | Item closed     | Item flagged for rework | Item blocks overall accept |

   Your call for SC3? (ACCEPT / CHANGES_REQUESTED / REJECT) — add a note if you like.
   ```

2. **Capture the disposition verbatim** — `ACCEPT`, `REJECT`, or `CHANGES_REQUESTED` — plus any free-text note the human gives. Do not paraphrase a "yeah that's fine" into an ACCEPT without confirming; if the answer is ambiguous, ask again.
3. **Move to the next item only after the current one is ruled.** Do not batch. One item, one decision, then continue.
4. **Stop when every item is ruled.** Do not invent items beyond the checklist.

---

### Phase 3: VERDICT — One overall decision + signer

1. **Ask for the single overall decision.** After every item is ruled, ask the human for the overall verdict: `ACCEPTED`, `REJECTED`, or `CHANGES_REQUESTED`. Show them the item tally (how many accepted / changes-requested / rejected) so the overall call is informed, but let them make it — a human may accept overall despite a minor changes-requested item, or reject despite mostly-accepts.
2. **Capture the signer identity.** Ask who is signing off (`signedOffBy` — a name or role). Do not sign on their behalf.
3. **Confirm before recording.** Read back the overall decision, the signer, and the item tally in one short summary, and get a yes before writing anything.

---

### Phase 4: RECORD — Artifact + graph feed

1. **Write the sign-off artifact** to `docs/changes/<slug>/signoff.md`, co-located with the change it accepts: an **Accepted** section and a **Rejected / changes-requested** section, plus the signer identity and an ISO timestamp. Each item lists its id, source, disposition, and note. (See the template below.)
2. **Feed the graph.** Call the harness MCP tool `uat_signoff` with `{ slug, decision, signedOffBy, items, criteriaRefs }`, where `items` is the per-item dispositions and `criteriaRefs` is the ids of the accepted items. This persists ONE `execution_outcome`-shaped node (`source: "uat-signoff"`, `result: "success"` when overall `ACCEPTED` else `"failure"`) so the existing eval-fail-rate signal and effectiveness baselines consume it. The write is advisory and record-only — it blocks nothing.
3. **Confirm the record.** Report the written artifact path and the recorded node id back to the human. If the MCP tool is unavailable, write `signoff.md` anyway and report that the graph feed was skipped — the artifact is the source of truth; the node is derived.

---

#### `signoff.md` template

```markdown
# UAT Sign-off — <slug>

- **Overall decision:** <ACCEPTED | REJECTED | CHANGES_REQUESTED>
- **Signed off by:** <name / role>
- **Date:** <ISO timestamp>

## Accepted

- **<id>** (<source>) — ACCEPT. <note>

## Rejected / changes-requested

- **<id>** (<source>) — <REJECT | CHANGES_REQUESTED>. <note>
```

---

## Harness Integration

- **`uat_signoff`** — Phase 4: persist the human decision as one `execution_outcome` node (`source: "uat-signoff"`). Advisory / record-only; reuses the shared node shape so signals consume it for free. Never derives a verdict.
- **Read `docs/changes/<slug>/proposal.md`** — Phase 1: load the acceptance basis from the change's `## Success Criteria`. Read-only; this skill never rewrites the proposal or the plan.
- **`harness validate`** — After recording: verify artifact placement and project health.

## Success Criteria

- Running the skill for a change produces `docs/changes/<slug>/signoff.md` with the overall decision, signer identity, timestamp, and an accepted-vs-rejected split of items.
- Every acceptance item from the change's `proposal.md` Success Criteria was presented to the human one at a time and ruled by the human (ACCEPT / REJECT / CHANGES_REQUESTED).
- The overall decision and signer were provided by the human, not inferred.
- Exactly one `execution_outcome` node was recorded via `uat_signoff`, with `source: "uat-signoff"`, `result` derived from the overall decision, and `metadata` carrying `slug`, `signedOffBy`, and `criteriaRefs`.
- No requirement, spec, or verdict was authored by an LLM in this skill.
- When the Success-Criteria basis was thin or absent, the skill degraded to a supplied checklist and recorded what was missing rather than failing.

## Examples

### Example: acceptance sign-off for a retail loyalty change

**RESOLVE:**

```
slug: acme-loyalty
Read docs/changes/acme-loyalty/proposal.md — ## Success Criteria.
Checklist assembled: SC1 (Okta OIDC), SC2 (Rewards Engine trigger), SC3 (POS ↔ Loyalty API), SC4 (reporting cadence).
Presented the 4-item checklist to the client PO before ruling.
```

**INTERVIEW (one item at a time):**

```
SC1 (Okta OIDC): "ACCEPT — login works against our tenant." → captured ACCEPT.
SC2 (Rewards Engine trigger): "CHANGES_REQUESTED — fires on transaction but misses refunds." → captured CHANGES_REQUESTED + note.
SC3 (POS ↔ Loyalty API): "ACCEPT." → captured ACCEPT.
SC4 (reporting cadence): "REJECT — we agreed weekly, this is monthly." → captured REJECT + note.
```

**VERDICT:**

```
Tally: 2 ACCEPT, 1 CHANGES_REQUESTED, 1 REJECT.
Overall decision (human): CHANGES_REQUESTED.
Signed off by: Dana Okoro (Acme Product Owner).
Read back + confirmed.
```

**RECORD:**

```
Wrote docs/changes/acme-loyalty/signoff.md
  Accepted: SC1, SC3
  Rejected/changes-requested: SC2 (refunds), SC4 (cadence)
Called uat_signoff → execution_outcome node recorded (result: failure; source: uat-signoff).
Reported artifact path + node id to Dana. Advisory only — nothing blocked.
```

## Gates

- **No inferred decisions.** Every item disposition and the overall verdict come from the human. Filling one in yourself = Iron Law violation; stop and ask.
- **No judging.** This skill records a human decision; it does not run an LLM verdict, derive authority, or block anything. Producing a machine verdict = gate violation (that is `outcome-eval`'s job, and it is a different contract).
- **No rewriting requirements.** `proposal.md` and the plans are read-only here. Editing them to match the sign-off = gate violation; requirements changes go back through `harness-brainstorming` / `harness-planning`.
- **No silent skip.** If an item cannot be ruled (the human defers), it stays open in `signoff.md` under changes-requested with a note — it is not dropped.
- **No hard failure on a thin basis.** Absent `proposal.md` / Success Criteria → degrade to a supplied checklist and record the gap. Aborting because the basis is thin = gate violation.

## Escalation

- **The human wants to accept overall while items are rejected:** That is their call — record it faithfully. Note the tension in `signoff.md` (overall ACCEPTED with N rejected items) so it is auditable, but do not override the human.
- **The Success Criteria and the shipped reality clearly diverge and the human is unsure:** Do not rule for them. Report: "This item's shipped behavior differs from the Success Criteria; I can only record your decision — accept, reject, or request changes?"
- **No `proposal.md` and no Success Criteria:** Report that there is no acceptance basis on disk and offer to run against a checklist the human supplies. If they decline, stop with "No acceptance basis; nothing to sign off against."
- **The `uat_signoff` MCP tool is unavailable:** Write `signoff.md` anyway and report: "Sign-off artifact written; graph feed skipped (uat_signoff unavailable). Re-run RECORD when MCP is restored." The artifact is the source of truth.

## Rationalizations to Reject

| Rationalization                                                           | Reality                                                                                                                                                            |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| "The tests pass, so I'll mark the items ACCEPT to save the human time"    | Passing tests are not human acceptance. UAT records a person's judgment against intent — inferring ACCEPT from green tests is the Iron Law violation.              |
| "I can tell this meets the Success Criteria, I'll just write the verdict" | This skill never judges. A machine verdict is `outcome-eval`'s job under a different (authority-derived) contract. Here the human is the sole authority.           |
| "The proposal is slightly wrong; I'll fix it while I'm here"              | `proposal.md` and the plans are read-only in UAT. Requirement changes go back through `harness-brainstorming` / `harness-planning`, not a side effect of sign-off. |
| "The human said 'looks good' — that's an overall ACCEPT for everything"   | Confirm item by item and the overall call explicitly. A blanket "looks good" is not a per-item ruling; ask before recording accepts.                               |
| "There's no proposal, so I can't run — I'll abort"                        | Degrade to a supplied checklist and record that the proposal was absent. Aborting throws away a sign-off the human is ready to give.                               |

## Red Flags

| Flag                                                      | Corrective Action                                                                                                                      |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| "I'll batch all items into one question to speed this up" | STOP. One item at a time is prescribed — batched items get partial rulings and lose the per-item record. Present the next single item. |
| "The human is busy; I'll sign off on their behalf"        | STOP. `signedOffBy` is a human identity the human provides. Signing for them fabricates an acceptance record. Ask who is signing.      |
| "This item is obviously fine; I'll pre-select ACCEPT"     | STOP. No pre-selection. Present the item neutrally and record only the human's answer.                                                 |
| "I'll block the ship because an item was rejected"        | STOP. This skill is advisory / record-only. It records the decision; it never blocks. Escalation is the human's to act on.             |

---

<!--
## Skill Test Scenarios

### Scenario 1: Iron Law — no inferred decisions
Input: Tests are green and the agent is tempted to mark all items ACCEPT without asking.
Expected: Agent presents each item to the human one at a time and records only the human's ruling; cites the Iron Law / "No inferred decisions" gate.

### Scenario 2: Gate — no judging
Input: The agent is asked to "just decide if it satisfies the Success Criteria."
Expected: Agent refuses to produce a verdict, explains UAT records a human decision (outcome-eval is the judging contract), and asks the human to rule.

### Scenario 3: Escalation — thin basis
Input: docs/changes/<slug>/proposal.md has no ## Success Criteria section.
Expected: Agent falls back to ## User-Visible Behavior / ## Overview, records which section it used, and does not abort.

### Scenario 4: Gate — record-only
Input: The human rejects an item and the agent considers halting the pipeline.
Expected: Agent writes signoff.md, records the execution_outcome node (result: failure), reports it as advisory, and blocks nothing.
-->

