index-sync
Atomically updates both the specs index and the tasks index for a given spec, detects and repairs pre-existing drift between them, and enforces write invariants before any mutation
What this skill does
# Index Sync
## Purpose
Ensure the specs index and the tasks index (resolved via `scripts/resolve-paths.sh`) remain consistent at all times.
Every status/progress write to either index MUST go through this skill — never write one index alone.
## Why Two Indexes Exist
- `specs/index.json` (resolved as `$SPECS_INDEX`) — source of truth for spec lifecycle status (`draft`, `approved`, `in-progress`, `completed`, `cancelled`, `reserved`, `merged`, `obsolete`)
- `tasks/index.json` (resolved as `$TASKS_INDEX`) — mirrors spec status and carries task-level progress (`progress`, task counts). Its vocabulary is `pending`, `in-progress`, `completed`, `cancelled`, `draft`, `reserved`, `merged`, `obsolete` — the same as the specs index except `approved` (which maps to `pending`) and the extra `pending` value.
They drift when one is written without the other. This skill closes that gap.
## Inputs
You will receive:
1. **specId** — e.g. `"SPEC-001"` (required)
2. **newStatus** — the target status for both indexes. Must be one of: `pending`, `in-progress`, `completed`, `cancelled`, `draft`, `reserved`, `merged`, `obsolete` (tasks index) / `draft`, `approved`, `in-progress`, `completed`, `cancelled`, `reserved`, `merged`, `obsolete` (specs index). Pass `null` to skip status update (progress-only write).
3. **newProgress** — string in `"N/M"` format, e.g. `"4/10"` (optional — omit or pass `null` to skip progress update)
4. **specsIndexPath** — absolute path to the specs index (resolved via `scripts/resolve-paths.sh` as `$SPECS_INDEX`; default: `<repo-root>/.claude/specs/index.json`)
5. **tasksIndexPath** — absolute path to the tasks index (resolved via `scripts/resolve-paths.sh` as `$TASKS_INDEX`; default: `<repo-root>/.claude/tasks/index.json`)
## Process
### Step 0: Pre-Write Validation (Invariant Checks)
Before ANY write, validate the requested mutation. These checks use `jq` only. If any check fails, **ABORT with a clear error message** — do NOT write bad data.
#### 0a. Status enum check
If `newStatus` is provided, verify it is one of the allowed values:
```bash
# Allowed values for tasks/index.json epics
TASK_STATUSES="pending in-progress completed cancelled draft reserved merged obsolete"
# Allowed values for specs/index.json
SPEC_STATUSES="draft approved in-progress completed cancelled reserved merged obsolete"
# newStatus must be valid in AT LEAST ONE vocabulary. `pending` is task-only
# and `approved` is spec-only, so requiring membership in BOTH would reject those
# legitimate values. Accept if it appears in either set.
if ! echo "$TASK_STATUSES $SPEC_STATUSES" | grep -qw "$newStatus"; then
echo "ABORT: invalid status '$newStatus' — not in tasks ($TASK_STATUSES) or specs ($SPEC_STATUSES) vocabulary"; exit 1
fi
```
#### 0b. Progress format check
If `newProgress` is provided, verify format `N/M` with integer N ≤ M:
```bash
# jq invariant check
echo "$newProgress" | jq -Rr '
split("/") |
if length != 2 then error("progress must be N/M format") else . end |
(.[0] | tonumber) as $n |
(.[1] | tonumber) as $m |
if $n < 0 or $m < 0 then error("progress values must be non-negative") else . end |
if $n > $m then error("completed (\($n)) cannot exceed total (\($m))") else "ok" end
' 2>&1 | grep -q "^ok$" || { echo "ABORT: invalid progress format '$newProgress' (must be N/M with N<=M)"; exit 1; }
```
#### 0c. Spec directory existence check
When writing a new epic entry, confirm `$SPECS_DIR/{slug}/` exists on disk. Skip this check for updates to existing entries.
```bash
eval "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-paths.sh")"
SPEC_DIR="$SPECS_DIR/${specSlug}"
[ -d "$SPEC_DIR" ] || { echo "ABORT: spec directory '$SPEC_DIR' does not exist — cannot create index entry for a non-existent spec"; exit 1; }
```
#### 0d. Required fields check (for new entries only)
When adding a new entry, verify all required fields are provided: `specId`, `title`, `status`, `path`.
### Step 1: Detect Pre-Existing Drift
Read both index files and compare the entry for `specId`. Report any disagreements BEFORE applying the new write.
```bash
# Resolve paths in THIS block — each bash block runs in its own shell, so the
# eval must be repeated wherever the resolved vars are used.
eval "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-paths.sh")"
# Extract current values from both indexes (empty string if entry not found)
SPEC_STATUS=$(jq -r --arg id "$specId" '
.specs // [] | map(select(.specId == $id)) | first | .status // ""
' "$SPECS_INDEX" 2>/dev/null)
TASK_STATUS=$(jq -r --arg id "$specId" '
.epics // [] | map(select(.specId == $id)) | first | .status // ""
' "$TASKS_INDEX" 2>/dev/null)
TASK_PROGRESS=$(jq -r --arg id "$specId" '
.epics // [] | map(select(.specId == $id)) | first | .progress // ""
' "$TASKS_INDEX" 2>/dev/null)
```
If `SPEC_STATUS != TASK_STATUS` and both are non-empty, report:
```
⚠ Drift detected for SPEC-NNN before write:
specs/index.json status: "<old-spec-status>"
tasks/index.json status: "<old-task-status>"
Reconciling: specs/index.json is authoritative — tasks/index.json will be updated.
```
Trust `specs/index.json` as authoritative on status disagreements. The new write then supersedes both.
### Step 2: Compute the Status Mapping
The two indexes share almost the same vocabulary. The mapping is **identity** —
the tasks index mirrors the spec status exactly — with a **single exception**:
`approved` (a specs-only value) maps to `pending` in the tasks index.
| Canonical (specs index) | Tasks index equivalent |
|--------------------------|------------------------|
| `draft` | `draft` |
| `approved` | `pending` |
| `reserved` | `reserved` |
| `in-progress` | `in-progress` |
| `completed` | `completed` |
| `merged` | `merged` |
| `cancelled` | `cancelled` |
| `obsolete` | `obsolete` |
When `newStatus` is `approved`, write `pending` into `tasks/index.json`.
Every other value is written as-is to both indexes (identity mapping).
### Step 3: Update specs/index.json
Use a single atomic `jq` rewrite (read → transform → write back).
```bash
# Resolve paths in THIS block (own shell — repeat the eval per block).
eval "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-paths.sh")"
# Wrap in flock to prevent concurrent writes (see Concurrency section below)
(
flock -w 10 200 || { echo "ABORT: index busy, another session holds the lock — retry in a moment"; exit 1; }
# Build the patch object (only include fields that are being updated)
jq_status_patch=""
[ -n "$newStatus" ] && jq_status_patch="| if .specId == \"$specId\" then .status = \"$newStatus\" else . end"
jq --arg id "$specId" \
--arg status "$newStatus" \
'
.specs |= map(
if .specId == $id then
(if ($status != "" and $status != "null") then .status = $status else . end)
else . end
)
' "$SPECS_INDEX" > "${SPECS_INDEX}.tmp" && mv "${SPECS_INDEX}.tmp" "$SPECS_INDEX"
) 200>"$LOCK"
```
If the entry does not exist in `$SPECS_INDEX` yet (new spec creation), append it instead of updating.
### Step 4: Update tasks/index.json
Apply the mapped status and/or progress to `$TASKS_INDEX`:
```bash
# Resolve paths in THIS block (own shell — repeat the eval per block).
eval "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/resolve-paths.sh")"
(
flock -w 10 200 || { echo "ABORT: index busy, another session holds the lock — retry in a moment"; exit 1; }
TASK_STATUS_VAL="$mappedTaskStatus" # computed from Step 2 mapping
PROGRESS_VAL="$newProgress" # may be empty/null
jq --arg id "$specId" \
--arg status "$TASK_STATUS_VAL" \
--arg progress "$PROGRESS_VAL" \
'
.epics |= map(
if .specId == $id then
(if ($status != "" and $status != "null") then .status = $statuRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.