Sync vault thesis status to Quartr watchlists, backfill quartr_id frontmatter, and optionally seed keyword alerts. Use when user says "watchlist sync", "seed quartr ids", or after a batch of $status changes. The only skill that mutates Quartr watchlists.
Installs into .claude/skills of the current project.
Are you the author of Watchlist Sync?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jameswong2011-watchlist-sync)
---
name: watchlist-sync
description: Sync vault thesis status to Quartr watchlists, backfill quartr_id frontmatter, and optionally seed keyword alerts. Use when user says "watchlist sync", "seed quartr ids", or after a batch of $status changes. The only skill that mutates Quartr watchlists.
---
**Codex execution:** Read `.agents/skills/_shared/codex-compat.md` first. Treat `SKILL_ARGS` as the arguments supplied with `$watchlist-sync`, or infer them from the user's request when this skill is invoked implicitly.
Mechanical set-diff of `Theses/` vs Quartr watchlists. No investment judgement. Per `.agents/skills/_shared/quartr.md`.
`$status` / `$prune` / `$thesis` do **not** call Quartr; they print `→ $watchlist-sync`. This skill is the single writer.
## Arguments
- empty / `--sync` — diff and apply membership; write missing `quartr_id:`
- `--seed` — first-run: create Vault Monitoring if absent, backfill all `quartr_id:`, sync both lists
- `--keywords` — create the seed keyword alerts in `.agents/skills/_shared/quartr.md`'s spirit (named observables, not "AI"). Idempotent against `list_keywords`
- `--dry-run` — report the diff, write nothing (Quartr or vault)
## Step 0: Pre-flight
Vault-wide lock. Timeout 10 minutes. Rename-marker glob: hard-block on any marker (would write `quartr_id:` onto a mid-rename filename).
MCP required. If Quartr tools are unavailable: abort with `❌ Quartr MCP not connected. Run this skill in a Grok session (or any session with the quartr server). Weekly launchd uses grok for this reason.`
## Step 1: Load vault membership
From `Theses/*.md` frontmatter:
| Set | Rule |
|---|---|
| `active` | `status: active` |
| `monitoring` | `status: monitoring` |
| `ignore` | draft, closed, or no status |
Filename ticker = stem before ` - `. Store `quartr_id:` if present.
Skip non-company theses (no resolvable company — e.g. `BTC-CRYPTO`, `GLD` ETFs, thematic notes). Record them as `skipped-non-company` rather than forcing a Quartr id.
## Step 2: Load Quartr watchlists
`list_watchlists`. Vault Active must be id `30182`. If missing, abort (do not silently create a second Active list).
Vault Monitoring: if a watchlist named exactly `Vault Monitoring` exists, capture its id. If `--seed` and none exists, `create_watchlist` name `Vault Monitoring` (no companies yet). Persist id.
`get_watchlist` both lists. Build `quartr_active` / `quartr_monitoring` as sets of companyId.
## Step 3: Resolve ids
For every ticker in `active ∪ monitoring` lacking `quartr_id:`:
1. `.data/quartr_watchlists.json` `ids` map
2. Else `search_companies` per `.agents/skills/_shared/quartr.md` (alias table is the source of truth; strip FMP suffixes)
Ambiguous → leave unresolved, report, do not add to a watchlist.
## Step 4: Diff and apply
Desired: `active` tickers on 30182 only; `monitoring` tickers on Vault Monitoring only; neither list contains draft/closed.
For each list, `add_to_watchlist` / `remove_from_watchlist` in batches of ≤50 companyIds. Do not touch Followed (id 0).
Write `quartr_id: N` into thesis frontmatter for every newly resolved id. Insert as a new key immediately before the closing `---` of the opening YAML block **only if the key is absent**. If `quartr_id:` is already present with a non-empty integer, leave it (immutable provenance, same class as `source:`). If present but empty/null, replace that value. Never write a second `quartr_id:` key. Do not change `conviction:` / `status:` / `source:`.
Write `.data/quartr_watchlists.json`:
```json
{
"updated": "YYYY-MM-DD",
"active_id": 30182,
"monitoring_id": <id or 0>,
"ids": { "<FILENAME_TICKER>": <companyId>, ... }
}
```
Keys are filename tickers (`ASMI`, `KLA`, `000660`), not Quartr tickers.
## Step 5: `--keywords` (optional)
`list_keywords`. Create only names that do not already exist (match on `name`).
Default seed (companySetting `watchlists`, watchlistIds = [30182] unless noted):
| name | Scope override |
|---|---|
| `cRPO` | 30182 |
| `megawatt` | companies: NBIS, CRWV if resolved |
| `energisation` | companies: NBIS |
| `dual source` | companies: 000660 |
| `Waymo` | companies: UBER |
| `Photonics` | Vault Monitoring (SIVE) if monitoring_id set |
| `hybrid bonding` | companies: BESI, ASMI, TSM |
| `Anthropic` | 30182 |
| `gigawatt` | 30182 |
| `prepayment` | 30182 |
Do not alert on `AI` or `capex`. Name length 3–40.
## Step 6: Report + lock release
```
✓ $watchlist-sync complete
Vault Active (30182): +[n] / −[m] (now [N] companies)
Vault Monitoring ([id]): +[n] / −[m] (now [N] companies)
quartr_id written: [K] theses
unresolved: [list]
skipped-non-company: [list]
keywords created: [list or skipped]
State file: .data/quartr_watchlists.json
```
`--dry-run` prefixes every line with `DRY`. Release lock.
## Design constraints
1. **Only writer of Quartr watchlists.** Other skills remind; they do not add/remove companies.
2. **Followed (id 0) is personal.** Never mutate it.
3. **`quartr_id:` is hygiene, not a conviction change.** No snapshot, no Log entry, no `$sync`.
4. **Idempotent.** Re-running with a clean diff is a no-op plus a refreshed `updated:` date.