Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/CodySwannGT/lisa --skill lisa-validate-tracker-mapping명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | lisa-validate-tracker-mapping |
| description | Detect and repair drift between… |
| allowed-tools | ["Skill","Bash","Read","Write","Edit","AskUserQuestion"] |
/lisa:validate-tracker-mapping answers one question: do the status/label names this project's .lisa.config.json maps each lifecycle role to still exist — with the exact name — in the live tracker and PRD source?
Lisa's lifecycle is driven by configured role → name mappings (ready → "Ready", done.staging → "On Stg", a GitHub ready build label → "status:ready", a Notion ticketed value → "Ticketed", …). When someone renames or deletes a status in JIRA, a label in GitHub/Linear, or a select option in Notion, the config silently points at a name that no longer resolves. The symptom is downstream: a build finishes but the completion transition can't be found, so the item stalls — exactly the kind of half-broken state /lisa:repair-intake then has to clean up. This skill catches that drift at the source: the config-to-live mapping itself.
It is the audit/repair counterpart to /lisa:setup:jira (and the other setup:* skills): where setup:* establishes the mapping interactively, this skill re-validates an existing mapping against the live workflow and, on request, repairs the config to match.
repair=true enables writes — to the config only, never to the tracker. Within repair mode:
AskUserQuestion before writing. Default to leaving the value unchanged../.lisa.config.json).projects=<glob-or-path> — batch sweep. Expands to every directory under the glob that contains a .lisa.config.json. Example: projects=~/workspace/acme/projects/*.workspaces=<file> — batch sweep driven by a Lisa workspaces file (the { "<project-path>": "<branch>" } map used by /lisa:update-projects). Each key is a project path to audit. Combine with filter=<substring> to restrict to matching paths (e.g. filter=acme).repair=true — enable config repair (see confirmation policy). Default false.lane=build|prd|both — which mapping family to check. Default both. build = the destination tracker workflow; prd = the PRD source status/label mapping.Batch mode runs the identical per-project audit on each resolved project and prints one section per project plus a roll-up summary.
# Single-repo (default): the audit target is the current directory.
# Batch: expand projects=<glob> to dirs containing .lisa.config.json,
# or read workspaces=<file> keys (optionally filtered).
For workspaces=<file>, parse with jq (never grep/sed JSON):
jq -r 'keys[]' "$WORKSPACES_FILE" \
| sed "s#^~#$HOME#" \
| while read -r proj; do
[ -n "$FILTER" ] && case "$proj" in *"$FILTER"*) : ;; *) continue ;; esac
[ -f "$proj/.lisa.config.json" ] && echo "$proj"
done
Skip (and note) any path with no .lisa.config.json.
For each project, resolve the effective config from .lisa.config.local.json first, then .lisa.config.json:
read_config() {
local path="$1" local_v global_v
local_v=$(jq -r "$path // empty" .lisa.config.local.json 2>/dev/null)
global_v=$(jq -r "$path // empty" .lisa.config.json 2>/dev/null)
printf '%s\n' "${local_v:-$global_v}"
}
tracker=$(read_config '.tracker')
source=$(read_config '.source')
Resolve the effective role → name mapping using the same defaults /lisa:intake and /lisa:setup:jira use (config-resolution contract). The tracker field is required; only vendor-specific role-name keys present in config override the defaults, and absent vendor-specific role-name keys fall back to the documented defaults. Build the list of (role, configured-name) pairs to validate:
UNRESOLVABLE with setup guidance (/lisa:setup:jira, /lisa:setup:github, or /lisa:setup:linear). Do not default to JIRA.jira.workflow): ready, claimed, optional review, blocked, and each done.<env> (dev / staging / production). Defaults: Ready, In Progress, Code Review, Blocked, {dev: "On Dev", staging: "On Stg", production: "Done"}.github.labels.build, github.labels.prd): each configured label string.linear.workflow): ready, claimed, review, blocked, and each done.<env> — native workflow states, the Linear analogue of jira.workflow, not of github.labels. Defaults: Ready, In Progress, In Review, Blocked, {dev: "On Dev", staging: "On Stg", production: "Done"}.linear.labels): the prd.* map plus the one surviving build-lane key, the human_needed marker (linear.labels.build.human_needed). A config that predates the state model may still carry ready / claimed / blocked / done under linear.labels.build; those are inert — nothing reads them. Do not audit them against the live label set, and do not report them as drift. Report them once as a migration note pointing at /lisa:setup:linear, which removes them.notion.values): each configured select-option value, validated against the property's options.Resolve the live, canonical names per vendor through the same access layer the rest of Lisa uses — never a second source of truth.
/lisa:atlassian-access)First confirm the active substrate is authenticated to this project's atlassian.cloudId / atlassian.site. /lisa:atlassian-access already enforces account identity: a substrate authed as a different Atlassian account is skipped, not used. If no substrate matches the configured account, mark the project UNRESOLVABLE (auth mismatch) and move on — do not validate against the wrong instance, and do not trust a case-insensitive JQL pass as proof.
Enumerate the project's full workflow status set, preferring the most authoritative substrate:
# GET /rest/api/3/project/<KEY>/statuses → union of canonical status names across issue types
# via lisa-atlassian-access curl substrate
# jq: '[.[].statuses[].name] | unique'
to.name from getTransitionsForJiraIssue (with includeUnavailableTransitions=true) on a sample ticket, plus changelog-observed names (fromString/toString). This can miss a status that is empty and unreachable from the sample, so when only this substrate is available, report the enumeration method in the output so the operator knows the set may be partial.Note: JQL
status = "<name>"matching is case-insensitive, so a JQL probe that "passes" does not prove the configured casing matches. Always compare against the canonicalstatus.namestrings from the enumeration above, case-sensitively.
gh)gh label list --repo "$REPO" --limit 200 --json name -q '.[].name'
Enumerate via the corresponding access surface (lisa-linear-access workflow states + labels; Notion data-source select options for notion.statusProperty; Confluence page-exists check per parent id). Same compare-exact-case contract as JIRA.
For Linear, lisa-linear-access operation: list-workflow-states returns each state's name, type, position and isTeamDefault (the access layer sets it from the team's defaultIssueState). Keep isTeamDefault — Step 4 needs it, and it is the only authoritative answer to "which state does this team create Issues into". Do not approximate it with a name guess: Todo is merely the stock name, and a team that renamed its defaultIssueState is exactly the case a name guess misses.
For each (role, configured-name) pair, classify against the live name set:
"On Stg" vs live "ON STG"). Canonical = the live exact name.One role carries a further check that name-existence cannot express:
ready only: the configured state exists, but it is the team's default created state (isTeamDefault). This is worse than a name that does not resolve. The name resolves perfectly, so every existence check passes while the gate runs backwards: ready is supposed to mean "a human moved this Issue here", and the team's default means "nobody has touched this". Build-intake claims from that lane, so an INVERTED mapping dispatches work no human ever approved. Measured on the first team it hit: 20 Issues in the claimable lane, 12 never marked ready — including decision tickets shaped like leaves, which the leaf-only gate cannot catch either. Report it even when every other role is VALID.A project's verdict:
ready is never VALID, no matter how cleanly it resolves.Per project, print a terminal-first section:
<project-path> [tracker=<vendor> project/repo=<id>] VERDICT
role configured live (canonical) status
ready Ready Ready VALID
claimed In Progress In Progress VALID
blocked Blocked Blocked VALID
done.dev On Dev On Dev VALID
done.staging On Stg ON STG CASE_DRIFT → fix: "ON STG"
done.production Done Done VALID
An INVERTED ready gets a full line rather than a one-word status, because the operator reading it is not necessarily an engineer and the word alone does not convey the stakes:
ready Todo Todo INVERTED
"Todo" is the state this Linear team puts every NEW issue into, so the
build queue is currently claiming issues nobody marked ready. Pick or
create a state a person moves an issue into (Lisa's default is "Ready")
via /lisa:setup:linear, then set linear.workflow.ready to it.
End with a roll-up: counts of VALID / DRIFTED / UNRESOLVABLE projects and the exact next command (… repair=true when drift is auto-repairable; an admin note when a status is genuinely MISSING; /lisa:setup:linear when a ready is INVERTED).
repair=true)For each drifted role, repair the config (preserve everything else; only write changed keys; always jq … > tmp && mv):
Rewrite the configured value to the live canonical name. Examples:
# scalar role (ready/claimed/blocked/review)
jq --arg v "$CANONICAL" '.jira.workflow.<role> = $v' \
.lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
# done.<env>
jq --arg v "$CANONICAL" '.jira.workflow.done.<env> = $v' \
.lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
# github/linear label
jq --arg v "$CANONICAL" '.github.labels.<lane>.<role> = $v' \
.lisa.config.json > .lisa.config.json.tmp && mv .lisa.config.json.tmp .lisa.config.json
Compute the closest live candidates (case-insensitive token/substring overlap, then edit distance). Present via AskUserQuestion:
Role
<role>maps to<configured>, which no longer exists in<vendor>. Closest live names:<c1>,<c2>,<c3>. Pick the replacement, leave unchanged, or enter a name manually.
Only write on an explicit pick. Never auto-select. If the user leaves it unchanged, keep the project DRIFTED and surface the admin remediation (add the status back, or fix it in the tracker).
Never auto-repair an INVERTED ready, even with repair=true. Every other classification has one correct answer that the live tracker already knows: CASE_DRIFT has the canonical casing, MISSING has a shortlist of near-matches. INVERTED has neither. The configured name is live and correctly cased; what is wrong is which lane the project chose to mean "build-ready", and nothing in the config or the tracker records what the human intended instead. Guessing would silently repoint the queue at a lane that may hold nothing, or worse, at another lane the team fills automatically — swapping one wrong answer for a quieter one.
The team may also genuinely not have a dedicated ready lane yet, in which case the repair is to create a state, not to rewrite a string — /lisa:validate-tracker-mapping audits config, it does not mutate the tracker.
So: present the team's non-default states via AskUserQuestion, and write linear.workflow.ready only on an explicit pick. If none fits, or the user declines, leave the config untouched, keep the project DRIFTED, and hand off:
Linear has no dedicated build-ready state on this team. Run
/lisa:setup:linear— it offers to createReadyand records the mapping.
Until then, say plainly that the build queue is claiming unapproved work and that pausing build intake is the safe interim.
After any JIRA repair, clear the setup-jira reachability cache so it re-verifies the new mapping:
jq 'if .jira then .jira.verified_workflow_hash = null else . end' \
.lisa.config.local.json > .lisa.config.local.json.tmp 2>/dev/null \
&& mv .lisa.config.local.json.tmp .lisa.config.local.json || true
Re-print the project section showing the post-repair mapping and the new verdict (VALID if every role now resolves).
VALID and writes nothing./schedule (or run in CI) to detect drift continuously; run repair=true interactively when drift appears (the MISSING path needs a human decision and should not run unattended)..lisa.config.json (and clears the local verification-cache key) — it preserves all unrelated config and never stages secrets or env values.UNRESOLVABLE, not VALID.MISSING mapping; confirm the replacement via AskUserQuestion.jq for all JSON reads/writes; write only changed keys via jq … > tmp && mv.UNRESOLVABLE (e.g. an auth mismatch) must not abort the rest of the sweep.atlassian-access, gh, Linear/Notion MCP); do not invent a parallel mapping or lifecycle vocabulary.notion.statusPropertyconfluence.parents): each configured parent page id, validated by existence.