← the whole session plugin/skills/verify-ux-assignment-state/SKILL.md

For each UX assignment, determine the exact user state required to test it, verify that state against actual code (never trust the AI-generated claim), and record the assignment with clear, reproducible required-state parameters.

Verify UX Assignment Required State

Purpose: For each UX assignment, determine the exact user state required to test it, verify that state against actual code (never trust the AI-generated claim), and update the assignment with clear, reproducible required-state parameters.

Triggers: 'verify assignment state', 'what state does this need', 'add required state', 'check simulator path'

Where assignments live

If your project has its own admin tool tracking a UX-assignment/test-plan queue (a database, an admin UI with its own API), use its own endpoints wherever this skill says "fetch" or "update" — describe your own routes and auth in place of the generic examples below.

Otherwise, use the harness's local fallback: alignment-harness records ux-assignments prints a folder — keep one JSON or markdown file per assignment there, with whatever fields make sense (id, title, category, a priority/severity score, the uxStory, test steps, required-state, validation, validation notes). The steps below work the same either way; only the fetch/update mechanics differ.

Step 1: Fetch assignments, sorted by priority, non-terminal only

If you're reading from local files:

# List assignment files, then filter to ones not yet verified/failed/skipped
ls "$(alignment-harness records ux-assignments)"

Read each file, sort by whatever priority field you're using, and skip anything already marked verified, failed, or skipped.

If you have your own admin API, the equivalent is a "pending" or "active" query against your own assignments endpoint, sorted by score.

Intent Integration

When verifying UX assignment state, cross-reference against your Intent DB (/alignment-harness:intent-db, if you have it) to ensure the assignment tests the ORIGINAL documented intent, not a drifted interpretation.

During State Verification

  1. Check if the assignment references any intent slugs — if present, look each one up:

    node <path-to-intent-db-skill>/intent.js get <slug>
    
  2. Validate that the required state produces a state where the intent can be verified — if the intent says "silent renewal with zero interruption" but the test state doesn't trigger a renewal scenario, the required state is wrong even if it technically loads a page.

  3. If the assignment has no linked intents — look up the feature area in your Intent DB and add the matching slugs to the assignment when updating it.

  4. If the assignment contradicts a documented intent — mark it as needing review with a note explaining the contradiction, e.g.:

    {
      "validation": "intent_mismatch",
      "validationNotes": "Assignment expects an upgrade button to be visible, but the intent for this flow says a subscriber should see a guidance modal instead."
    }
    

Step 2: For each assignment, determine required state

2a. If a required-state description already exists — parse it into its parts

However your project records required state (a URL with query params if you have a state-simulating admin tool, a plain description otherwise), separate it into:

  • State conditions — the things that actually define the scenario (signed-in or not, paid or not, which plan, etc.)
  • Navigation/command instructions — where to go, whether to reset state first (not part of the state itself)

Example (opt-in, illustrative — replace with your own app's actual state vocabulary): a URL like http://localhost:3000/admin2/user-simulator?auth=session_expired&userType=returning_paid&accessTier=MAX_ACCESS&redirect=/hub&clear=true — here auth, userType, and accessTier are state conditions; redirect and clear are navigation commands, not state.

2b. CRITICAL — Reason about the PERSONA, not the parameters

Before you pick a state, pick a method

Everything below is about choosing the right test state. That is the second question. The first is whether a state-simulating tool is the right tool at all.

If the assignment is about what a person in a given tier actually experiences, use a real test account, not a simulator. Access level is almost always decided on the backend and only reflected to the frontend afterward. A simulator (if you have one) typically overrides what the browser believes that value is; it never consults the server. So verifying a paywall under a simulated free tier proves the paywall renders when the front-end is told "free" — it does not prove a real free person reaches it. Reporting the former as the latter is how a verification pipeline certifies a journey nobody has walked.

Use a state-simulating tool for fast state-flipping when the access logic is already known good, or when the assignment is explicitly about admin tooling. Then use the table below.

If you have your own simulator, keep a running list of its known lies here — anything it silently gets wrong (a page reload wiping the simulated state, a param that's written but never actually read, a value that looks valid but doesn't exist) — so agents don't trust it blindly. This is a template to fill in for your own tooling, not a fixed list; every simulator ends up with its own quirks.


The #1 classification mistake is mapping category keywords to state parameters programmatically. Instead, reason about WHO the user is in the uxStory:

Ask yourself: "Who is the person in this story, and what is their journey?"

Example reasoning table (opt-in, illustrative — build your own from your product's actual states):

uxStory says... Wrong mapping Correct reasoning
"new subscriber going through checkout" subscription=canceled (has a canceled sub) userType=new_user (never had a sub at all)
"paid user loses access" accessTier=FREE (free user) subscription=active AND auth=session_expired (was paid, session died)
"trial user upgrades" subscription=trialing only subscription=trialing AND accessTier=LIMITED (a card-on-file trial state, not the same as a no-card trial)

The persona hierarchy:

  1. Read the uxStory as a human narrative — who is this person?
  2. What have they done before? (new user = never paid, returning = previously paid)
  3. What is their current subscription/access state, in your own product's own vocabulary?
  4. What access do they currently have?
  5. What is happening TO them? (session expired, page load, button click)

Only AFTER you have the persona clear, translate to whatever parameters or setup steps your own test tooling needs.

2c. If no required-state description exists yet — verify against code

DO NOT trust the assignment's claims. Read the actual code to determine what state is needed.

Token-efficient pattern — grep before read:

  1. Find the relevant file: The assignment's title and uxStory mention a feature. Grep for it:

    Grep(pattern="featureKeyword", path="<your app's source>", output_mode="files_with_matches")
    
  2. Find the conditional: Once you have the file, grep for the state check:

    Grep(pattern="accessTier|subscription|isPaid|hasPaid|isOnTrial", path="<file>", output_mode="content", context=3)
    

    (Replace those literal names with whatever your own codebase actually calls these things.)

  3. Read only the 20-30 lines around the conditional — not the whole file.

  4. Map the code condition to a required-state description — build your own table here the first time you do this for a project, the same shape as the persona-reasoning example above, using your own code's actual condition names and values.

  5. Write the required-state description in whatever form your project uses (a URL with params, a short prose description, a fixture setup script).

Step 3: Verify the state actually works

Before recording the required state, verify:

  1. Your state-simulating tool (if you have one) actually accepts these params. Check wherever it defines valid params.

  2. The code actually checks these conditions. Read the relevant component and confirm the conditional exists. If the code does NOT check the condition the assignment claims, mark it as phantom (validation: 'phantom').

  3. The destination the assignment points to actually exists. If the assignment says "verify behavior on /dashboard", confirm that route exists.

Step 4: Record the update

Write the update back to wherever the assignment lives (your own admin API, or a file in alignment-harness records ux-assignments):

{
  "requiredState": "auth=session_expired&userType=returning_paid&accessTier=MAX_ACCESS (or your own project's equivalent description)"
}

If the assignment is phantom (code doesn't match claim):

{
  "validation": "phantom",
  "validationNotes": "Code in <file>:<line> does not match claim. <specific reason>."
}

Step 5: UI rendering (if you have a UX-assignments admin page)

If your project has an admin page that renders these assignments, it's worth building a small component that parses a required-state description into readable, color-coded chips (one per condition) with a link to jump straight into that state — this is a nice-to-have, not something this skill requires. If you don't have such a page, recording the state clearly in the assignment file is enough.

Step 6: Verify persistence (MANDATORY)

After updating items, spot-check that the data actually persisted — re-fetch (or re-read the file) and confirm the field you just wrote is actually there:

ls "$(alignment-harness records ux-assignments)" | wc -l
grep -l "requiredState" "$(alignment-harness records ux-assignments)"/*.json | wc -l

If items you updated show as missing, whatever you're using to persist the update may be silently dropping the field — check that the field name you're writing matches exactly what your storage (API controller, or your own file-writing code) expects.

Efficiency Rules

  1. Grep before read — find the 3 lines that matter, then read only 30 lines around them
  2. One fetch to start — don't re-fetch per item, fetch/list all pending items in one pass
  3. Batch updates — if updating multiple items, do them in sequence without re-reading the whole queue each time
  4. Stop at phantom — if code doesn't match, mark phantom and move on. Don't try to fix the assignment.
  5. Trust an existing required-state description if present, but check staleness — if the code has changed since it was last verified (compare a last-verified timestamp against the file's last-modified date, if you track one), re-verify rather than trusting a possibly-stale description. Only do deep code verification for items WITHOUT a required-state description, or where the uxStory contradicts what's recorded.