CLAUDE.md · git:20260702.f02853f · 2026-07-02 · sha256 15fa1268f2c604c9

CLAUDE.md git:20260702.f02853fA

Immutable. This exact content is served forever at /api/v1/blob/15fa1268f2c604c9.

# Project conventions - tiktok-skills

This file is for any Claude Code agent working on this repository. Read it
before making changes. Conventions here are mandatory unless the user asks
otherwise.

## Versioning

- Single source of truth: `.claude-plugin/plugin.json`,
  `.claude-plugin/marketplace.json`, `.codex-plugin/plugin.json`, and
  `.agents/plugins/marketplace.json`. Plugin manifests must always match on
  package name and version; marketplace entries must point to the same package;
  author, license, homepage, and the public skill-bundle description must stay
  aligned.
- Keep `CLAUDE.md` and `AGENTS.md` aligned when changing shared project rules.
  Claude-specific workflow details belong here; Codex-specific details belong in
  `AGENTS.md`.
- Codex marketplace install uses `.codex-marketplace/tiktok-skills/`. Do not edit
  that generated package by hand. Update the root files first, then run
  `python3 scripts/sync_codex_marketplace.py`.
- **Default: bump the PATCH segment (3rd level, `0.0.X`).** This is the automatic
  behavior for every shippable commit, regardless of how large the diff feels.
  Skill renames, lib API breaks, new features: still PATCH by default.
- Only bump MINOR or MAJOR when **the user explicitly asks** for a higher rank
  ("this is minor", "make it 2.0", "bump major"). Do not promote on your own
  initiative even if semver textbook says so.
- After bumping, two steps are required:
  1. Tag the commit: `git tag -a v<X.Y.Z> -m "..."` + `git push origin v<X.Y.Z>`
  2. **Publish a GitHub Release** for the tag: `gh release create v<X.Y.Z> --title "v<X.Y.Z>" --notes "<changelog>" --latest`
  A tag alone does NOT update the README release badge. The shields.io badge
  reads from the Releases API, not from raw tags. Skipping step 2 leaves it stale.

## Commits

- Primary author **must** be Sergey: every `git commit` needs
  `--author="Sergey Bulaev <s@bulaev.org>"`. The harness defaults to the Claude
  identity if you forget; verify with `git log -1 --format='%an <%ae>'` before
  pushing.
- Co-author trailer (`Co-Authored-By: Claude ...`) is fine and welcomed.
- Verify locally before push: build never breaks, no broken refs in `SKILL.md`,
  library smoke import passes.

## Skill bundle invariants

- **Exactly 5 skills.** Adding requires merging or splitting elsewhere to stay at
  5. The number is announced in plugin manifests and the README.
- **Frontmatter `description:` target <= 400 chars** (some bundle-heavy skills
  land slightly higher when their scope is genuinely broad; keep under 510).
  Always include a "Not for X (use Y)" disambiguation sentinel when a skill
  overlaps with a sibling.
- **No em dashes anywhere in `description:` fields.** Em dashes in body prose are
  allowed for table separators and list dividers only. No em dashes inside the
  literal fill-in lines of any hook skeleton (they would leak into generated
  scripts and on-screen text).
- **Skill names are public surface.** Renaming a skill is a major version bump and
  requires updating: `.codex-plugin/plugin.json`,
  `.agents/plugins/marketplace.json`, `.claude-plugin/plugin.json`,
  `.claude-plugin/marketplace.json`, root `SKILL.md` bundle list, README skill
  table, and every `tt-<name>` cross-reference in sibling SKILL.md files.

## Voice rules + reference layout

- Canonical voice rules live at root `references/voice-rules.md` (spoken and
  on-camera scripts plus captions). Skill-local "Hard rules" sections must only
  contain skill-specific overrides (the hook layers, caption limits, settings
  flags) and start with: `Global voice rules: see root SKILL.md Voice rules.`
- Other root-level references shared across skills:
  `references/hook-formulas.md` (10 TikTok 3-second hook formulas) and
  `references/algorithm-heuristics.md` (completion rate, rewatch, shares).
- Skill-local references live in `skills/<skill>/references/`. Cite from the skill
  with bare `references/X.md`. Cite root from skills with `../../references/X.md`.
- `tt-humanizer` has `sub-skills/` for the folded-in post-audit (pre-film)
  workflow. Don't duplicate this pattern in other skills without a clear reason.

## Layer separation

- **Write layer (Publora):** `lib/publora_client.py`. TikTok is video-only, so
  publishing is a multi-step flow: `create_draft` (no scheduledTime) ->
  `get_upload_url` -> `upload_to_presigned` (PUT to S3) -> `schedule_post`
  (PUT /update-post). `publish_video` runs all four. Also `list_connections` /
  `tiktok_connections` (GET /platform-connections) and the `tiktok_settings(...)`
  helper for `platformSettings.tiktok`. Skills should call
  `lib.publish(kind, draft_text, target_url, video_path=..., ...)` rather than
  inline the dispatch. Real endpoints: `POST /create-post`, `POST /get-upload-url`,
  `PUT /update-post/:id`, with `platforms: ["tiktok-<id>"]` (an array of STRING
  ids), header `x-publora-key`.
- **TikTok requires a video file.** Without a `video_path`, `lib.publish` returns
  the caption and settings as a copy-paste block (upload in-app) plus an optional
  caption-only draft in Publora. There is no text-only TikTok post.
- **Two TikTok quirks to keep documented:** the boolean inversion bug
  (allowComments/allowDuet/allowStitch may map to disable_*), and unaudited apps
  posting PRIVATE only. Both live in `tt-caption-writer` and the settings matrix.
- **No read layer ships by default.** There is no cheap, documented TikTok read
  actor wired in. Skills ask the user to describe the video or paste the script.
  If a read actor is added later, gate it behind `APIFY_TOKEN` and keep the
  paste/describe fallback.
- Don't name competing third-party schedulers in committed files. The bundle is
  positioned around the Publora write integration.

## Codex marketplace package

- Codex requires marketplace entries to point at a nested plugin directory. The
  root remains the Claude-facing source layout.
- `.agents/plugins/marketplace.json` points to `.codex-marketplace/tiktok-skills`.
- `scripts/sync_codex_marketplace.py` copies the root Codex manifest, `SKILL.md`,
  `skills/`, `references/`, `lib/`, `scripts/`, `requirements.txt`, `.env.example`,
  and `LICENSE` into the hidden package.
- After editing any copied file, run the sync script before testing or committing.

## testing/ is gitignored

- `testing/` is the local scratch directory: API keys, sample API responses,
  validation reports, integration scripts.
- Never write secrets above `testing/` (the rest of the repo is public).
- The `.gitignore` rule for `testing/` is load-bearing; do not change it.

## Validation before push

Run from repo root:

```bash
python3 -c "from lib import publish, parse_tiktok_url, PubloraClient, tiktok_settings; print('OK')"
python3 scripts/sync_codex_marketplace.py
ls skills/ | wc -l        # must equal 5
grep -rnP '\x{2014}|\x{2013}' skills/*/SKILL.md SKILL.md | grep -i 'description:'   # must be empty
python3 -m json.tool .codex-plugin/plugin.json >/dev/null
python3 -m json.tool .agents/plugins/marketplace.json >/dev/null
python3 -m json.tool .claude-plugin/plugin.json >/dev/null
python3 -m json.tool .claude-plugin/marketplace.json >/dev/null
```

If any of these fail, do not push.