# Skill contract

> SKILL.md runtime authority: STEP 0 stale-clone guard, pre-flight protocol, engine invocation rules, and SKILL_DIR substitution.

- Repository: mvanhorn/last30days-skill
- GitHub: https://github.com/mvanhorn/last30days-skill
- Human docs: https://grok-wiki.com/public/docs/mvanhorn-last30days-skill-50b5421a8cca
- Complete Markdown: https://grok-wiki.com/public/docs/mvanhorn-last30days-skill-50b5421a8cca/llms-full.txt

## Source Files

- `skills/last30days/SKILL.md`
- `skills/last30days/scripts/lib/skill_meta.py`
- `AGENTS.md`
- `.claude-plugin/plugin.json`
- `skills/last30days/scripts/last30days.py`

---

---
title: "Skill contract"
description: "SKILL.md runtime authority: STEP 0 stale-clone guard, pre-flight protocol, engine invocation rules, and SKILL_DIR substitution."
---

`skills/last30days/SKILL.md` is the runtime authority for `/last30days`: the harness loads it when the slash command fires, the model must follow its step order before any engine Bash call, and `scripts/last30days.py` is invoked only through the `SKILL_DIR`-anchored path of the SKILL.md instance that was actually read. The contract is not optional metadata — it defines stale-install guards, pre-flight resolution, plan JSON handoff, and output-shape LAWs that the engine and `lib/render.py` enforce on stdout.

## Skill, engine, and harness boundaries

| Layer | Artifact | Role |
|-------|----------|------|
| **Skill** | `SKILL.md` + sibling `scripts/` | Agent-facing prose contract: steps, flags, synthesis LAWs, failure-mode guards |
| **Engine** | `scripts/last30days.py` + `scripts/lib/*` | Retrieval, planning intake, clustering, `--emit=compact` rendering |
| **Harness** | Claude Code, Codex, Cursor, `~/.agents/skills/`, etc. | Loads SKILL.md via Read; does not pass shell flags through slash invocations |

<Note>
Slash commands cannot carry pipes or flags (`/last30days topic --emit=html` is invalid in harnesses). The model translates user intent into engine flags per SKILL.md. Direct CLI (`python3 …/last30days.py`) is for scripting, cron, and engine testing only.
</Note>

```mermaid
flowchart TB
  subgraph harness["Harness"]
    U["User: /last30days topic"]
    R["Read SKILL.md"]
  end
  subgraph skill["Skill contract — SKILL.md"]
    S0["STEP 0 stale-clone guard"]
    PF["Pre-flight 0.45 → 0.5 → 0.55 → 0.75"]
    INV["SKILL_DIR engine invocation"]
    LAW["OUTPUT CONTRACT / LAWs 1–8"]
  end
  subgraph engine["Engine — last30days.py"]
    PRE["lib/preflight.py Class 1 gate"]
    PIPE["lib/pipeline.py"]
    REN["lib/render.py compact + warnings"]
  end
  U --> R --> S0 --> PF --> INV --> PRE --> PIPE --> REN
  REN --> LAW
```

Version strings stay aligned across three surfaces: SKILL.md frontmatter (`version: "3.3.1"`), `.claude-plugin/plugin.json`, and `lib/skill_meta.read_skill_version()` used by `lib/render.py` for badge emission.

## STEP 0: Stale-clone self-check

STEP 0 runs **before** the rest of SKILL.md. It targets one known bad load path: Claude Code’s **marketplaces** git clone at `~/.claude/plugins/marketplaces/last30days-skill/`, which auto-restores to `origin/main` and can lag the versioned plugin cache.

<Steps>
<Step title="Detect cache SKILL.md">
Run the bash probe in STEP 0 to resolve `CLAUDE_CACHE_SKILL_MD` from `~/.claude/plugins/cache/last30days-skill/last30days/{version}/` (nested `skills/last30days/SKILL.md` or flat `SKILL.md`).
</Step>
<Step title="Compare load path">
If the SKILL.md you Read contains `/.claude/plugins/marketplaces/` **and** the cache path is non-empty, **stop** and re-read `$CLAUDE_CACHE_SKILL_MD` before continuing.
</Step>
<Step title="Otherwise continue">
Codex (`~/.codex/skills/`), `~/.agents/skills/`, `npx skills add` installs, and repo checkouts are valid — do not hop installs on those paths.
</Step>
</Steps>

<Warning>
A stale marketplace clone caused real regressions: models ran `--help` from the stale tree, missed flags such as `--competitors`, and improvised comparison flows. STEP 0 exists only for that Claude Code layout mismatch.
</Warning>

## Skill contract preface (three structural anchors)

Immediately after STEP 0, the **SKILL CONTRACT** block states that `/last30days` is a fixed research tool, not a generic “last 30 days” prompt. Three anchors prevent the documented v3.0.6-style improvisations:

1. **Mandatory badge** — first line `🌐 last30days v{VERSION} · synced {YYYY-MM-DD}` (engine emits it in `--emit=compact`; model passes through or builds it from `SKILL_DIR` + `plugin.json` / SKILL frontmatter).
2. **SKILL_DIR substitution** — engine path is the directory of the SKILL.md just Read; no install-path resolver walk.
3. **Read top-to-bottom** — no WebSearch-only answers, no `for dir in …` path discovery, no bare `python3 scripts/last30days.py` without pre-flight flags on named entities.

The **OUTPUT CONTRACT** (LAWs 1–8) was moved high in the file so long SKILL.md reads still load synthesis rules before the model stops reading (~line 1000). LAWs cover: no trailing `Sources:` (LAW 1), `What I learned:` vs comparison title (LAW 2), hyphen not em-dash (LAW 3), no `##` in GENERAL bodies (LAW 4), emoji-tree footer pass-through (LAW 5), no raw evidence clusters in user output (LAW 6), `--plan` on named entities (LAW 7), inline `[name](url)` citations (LAW 8).

## Invocation order (after STEP 0)

| Step | Name | Requirement |
|------|------|-------------|
| Runtime preflight | `LAST30DAYS_PYTHON` | Python 3.12+ (`python3.14` → `python3.12` → `python3`) |
| First-run | Step 0 wizard | If `~/.config/last30days/.env` lacks `SETUP_COMPLETE=true`, run `nux-wizard.md` |
| WebSearch load | STEP 0 (invoke section) | `ToolSearch select:WebSearch` first on Claude Code (deferred tool) |
| Intent | CRITICAL: Parse User Intent | `TOPIC`, `QUERY_TYPE`, optional `TARGET_TOOL`; branded one-liner, then 0.45 |
| 0.45 | Query quality pre-flight | Keyword-trap classes 1–4; ask or reframe before engine |
| 0.5 | Handle / repo resolution | `--x-handle`, `--x-related`, `--github-user`, `--github-repo` per checklist |
| 0.55 | Pre-research intelligence | Subreddits, TikTok/IG targets; mandatory when WebSearch exists |
| 0.75 | Query plan | Model-authored JSON → tmpfile → `--plan` |
| 1 | Research execution | `SKILL_DIR/scripts/last30days.py` foreground, 300s timeout, `--emit=compact` |
| 2 | WebSearch supplements | 2–3 post-engine searches (separate budget from 0.55) |

<Info>
Without a user topic, ask once for a topic — do not run WebSearch or the engine. With a topic, the Python engine is mandatory; missing the `✅ All agents reported back!` footer means the skill was not run.
</Info>

## Pre-flight protocol

### Step 0.45 — Topic quality (model-side)

Four keyword-trap classes drive clarify-or-reframe behavior **before** Bash:

| Class | Pattern | Action |
|-------|---------|--------|
| 1 | Demographic shopping (`gift for 42 year old man`) | One clarifying question, or reframe + gift subreddits |
| 2 | Numeric collisions (`42`, `100`) | Strip number from engine query when not semantically load-bearing |
| 3 | Tutorial phrasing (`how to use Docker`) | Reframe to discussion vocabulary |
| 4 | Generic single noun (`sneakers`) | Ask for facet before running |

Engine mirror: `lib/preflight.check_class_1_trap()` refuses Class 1 on stderr (exit 2) unless qualifiers (budget, hobbies, `who loves …`) are present. Bypass: `LAST30DAYS_SKIP_PREFLIGHT=1`.

### Step 0.5 — Targeting checklist (model-side)

The checklist is the **full** contract — not only X handles:

| Flag | When |
|------|------|
| `--x-handle` | Person, brand, product, creator with X presence |
| `--x-related` | Founders, collaborators, commentators |
| `--github-user` | Person who ships code (peer to X resolution) |
| `--github-repo` | Product / OSS project |
| `--subreddits` | Nearly always |
| `--tiktok-hashtags` / `--tiktok-creators` / `--ig-creators` | Per topic class |
| `--auto-resolve` | Fallback when WebSearch ran but 0.55 incomplete |

<Warning>
Person topics require **minimum** `--x-handle` + `--github-user` + `--subreddits` (typically `--x-related` too). X-handle-only invocations are a documented regression shape.
</Warning>

### Step 0.55 and 0.75 — WebSearch platform gate

When WebSearch is available, both steps are mandatory before engine invocation. When unavailable (OpenClaw, headless CLI), skip 0.55/0.75 and pass `--auto-resolve` so the engine discovers subreddits/handles via configured web backends.

Step 0.75 produces `QUERY_PLAN_JSON` (intent, freshness_mode, cluster_mode, 1–4 subqueries with sources/weights). Primary subquery must include reddit, x, youtube, tiktok, instagram, hackernews, polymarket.

### Engine-visible warnings

If pre-flight flags or `--plan` are missing on eligible named-entity topics, `lib/render.py` injects:

- `## Pre-Research Status` — Step 0.55 skip (keyword-only retrieval)
- `## DEGRADED RUN WARNING` — deterministic planner + bare invocation (LAW 7 backstop on stdout, not stderr)

Pass these blocks through verbatim (LAW 4 exception for engine-emitted status blocks; LAW 5/7 for degraded banner).

## SKILL_DIR substitution and engine invocation

`SKILL_DIR` is the absolute directory containing the SKILL.md the harness Read. `scripts/last30days.py` is always `${SKILL_DIR}/scripts/last30days.py`. Substitute the real path from the Read result — do not walk `~/.openclaw/skills/`, marketplace trees, or multi-install precedence lists.

```bash
SKILL_DIR="<absolute path of the directory containing the SKILL.md you Read>"

if [ ! -f "$SKILL_DIR/scripts/last30days.py" ]; then
  echo "ERROR: scripts/last30days.py not found under SKILL_DIR=$SKILL_DIR" >&2
  exit 1
fi

"${LAST30DAYS_PYTHON}" "${SKILL_DIR}/scripts/last30days.py" "$TOPIC" \
  --emit=compact \
  --save-dir="${LAST30DAYS_MEMORY_DIR}" \
  --save-suffix=v3 \
  --plan "$QUERY_PLAN_FILE" \
  --x-handle=... \
  --subreddits=...
```

<ParamField body="QUERY_PLAN_FILE" type="path" required>
Tmpfile from quoted heredoc (`mktemp …/last30days-plan.XXXXXX` + `<<'PLAN_EOF'`). Engine `parse_plan` reads file paths. **Never** inline `--plan '$JSON'` — apostrophes in plan strings break shell parsing.
</ParamField>

<ParamField body="COMPETITORS_PLAN_FILE" type="path">
Same tmpfile pattern for comparison `--competitors-plan` JSON (entity → handles, subreddits, github_user, context).
</ParamField>

<ParamField body="LAST30DAYS_MEMORY_DIR" type="path">
Defaults to `~/Documents/Last30Days` for raw saves; overridable per run.
</ParamField>

### Research execution precondition gate

Before Bash, confirm:

1. Platform branch chosen (WebSearch vs `--auto-resolve`).
2. WebSearch path: 0.55 + 0.75 completed.
3. `--emit=compact` (not `md` as primary user flow).
4. Command includes `--plan` plus every resolved flag from the checklist.

Run in the **foreground** with **300000 ms** timeout. Read full stdout (evidence envelope + pass-through footer). Post-engine: 2–3 WebSearch supplements (Step 2), distinct from 0.55 budget.

### Comparison and agent modes

- **COMPARISON** (`vs` / `versus`): per-entity 0.55 resolution, single engine call with `--competitors-plan` file + main entity outer flags; synthesis uses required `##` headers per template.
- **`--agent`**: skip intro, AskUserQuestion, wait states, and invitation; emit structured research report; still runs engine + supplements.

## Version and metadata helpers

`lib/skill_meta.read_skill_version(path)` parses SKILL.md YAML `version:` (quoted or unquoted). Used by render badge logic and release consistency tests — one regex, shared with `tests/test_plugin_contract.py` and `tests/test_version_consistency.py`.

Plugin manifest `.claude-plugin/plugin.json` carries the same semantic version for marketplace installs; badge resolution tries `jq` on `"$SKILL_DIR/../../.claude-plugin/plugin.json"` then SKILL frontmatter.

## Contract vs direct CLI

| Surface | Who sets flags | SKILL.md authority |
|---------|----------------|-------------------|
| `/last30days topic` | Model after reading SKILL.md | Full step + LAW contract |
| `python3 …/last30days.py …` | Human/script | Same flags exist, but no model pre-flight unless scripted |

<Check>
A successful harness run: STEP 0 satisfied → WebSearch loaded (if Claude Code) → 0.45/0.5/0.55/0.75 as applicable → engine from `SKILL_DIR` with `--emit=compact` + plan + targeting → synthesis obeys LAWs → engine footer present.
</Check>

## Related pages

<CardGroup>
<Card title="Skill, engine, and harness" href="/skill-engine-harness">
Boundaries between the Agent Skills package, Python engine, and multi-harness runtimes.
</Card>
<Card title="Query types" href="/query-types">
QUERY_TYPE classification, intent parsing, and engine refusal paths.
</Card>
<Card title="Output contract" href="/output-contract">
Badge line, LAWs voice contract, `--emit` modes, and footer pass-through.
</Card>
<Card title="CLI reference" href="/cli-reference">
Direct `last30days.py` flags vs slash-command constraints.
</Card>
<Card title="Troubleshooting" href="/troubleshooting">
Stale marketplace clones, missing sources, and `--diagnose`.
</Card>
</CardGroup>
