Skip to content
cccskills
FreePublished on GitHub

daily-offer-check

Re-verify active offers vs official sources, fix drifted terms, refresh evidence, then issue, PR, review, merge unattended; report-only/no-merge modes. Use for daily checks or stale-content issues. Don't use for adding or editing one offer, or CI.

The English summary is being prepared. Showing the original description.

Works withGitHub

Run score Not tested

See how to install

Files included (14)

  • SKILL.md16.8 KB
  • .gitignore21 B
  • agents/verifier.md5.1 KB
  • docs/README.md3.4 KB
  • evals/evals.json7.9 KB
  • references/apply-and-pr.md6.7 KB
  • references/review-and-merge.md4.1 KB
  • references/trust-policy.md3.7 KB
  • scripts/apply_verdicts.py9.2 KB
  • scripts/check_coverage.py8.5 KB
  • scripts/check_scope.py5.9 KB
  • scripts/list_active.py4.7 KB
  • scripts/prepare_branch.py4.0 KB
  • scripts/render_report.py6.9 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

daily-offer-check

You are the orchestrator. Inventory active offers, fan out verifiers, merge verdicts, apply safe writes, then carry one catalog PR from issue to merge. You do not fetch source_url yourself, and you do not open agents/ or references/ files a phase's worker is supposed to receive as its Input.

This skill is user-invoked and runs unattended: the user asked for the whole pipeline (sweep, issue, PR, review, merge), so do not stop for confirmations. Unattended removes confirmations, never safeguards: every gate below still stops the run. Never push to main; the only way in is the Step 8 merge.

Branch selector

Pick one branch. If several rows match, use this precedence (highest first):

  1. add / publish a new offer, change one offer from user-supplied details, screenshot, pasted pitch → Stop. Point at offer-updater.
  2. "report only", "dry run", "don't open a PR", "don't commit" → Report-only (wins over any apply, PR, or merge phrasing in the same request).
  3. --offers-dir naming a directory other than <repo>/offers → Report-only, so fixtures cannot land in a catalog PR.
  4. "don't merge", "leave the PR open", --no-merge → No-merge: run through a clean review, then stop with the PR open.
  5. /daily-offer-check, "run the daily offer check", "freshness sweep", "re-verify the catalog", "stale-content issue" → Unattended: issue → PR → review → merge.

Named slugs filter the inventory (--slugs a,b) on whichever branch wins. Publishing branches = No-merge and Unattended.

Dependency Preflight (mandatory)

Publishing branches invoke issue-creator (Step 6) and issue-pr-review (Step 7) and need an authenticated gh. Verify all three before Repo Sync, so a run that cannot publish never starts the sweep; Report-only never publishes and skips this check.

gh auth status >/dev/null 2>&1 || { echo "gh is not authenticated; run: gh auth login" >&2; exit 1; }
for skill in issue-creator issue-pr-review; do
  asm list -p agents --json 2>/dev/null | grep -q "\"$skill\"" \
    || test -f "$HOME/.agents/skills/$skill/SKILL.md" \
    || test -f "$HOME/.claude/skills/$skill/SKILL.md" || {
    echo "Missing required skill: $skill" >&2
    echo "Install it:      asm install https://github.com/luongnv89/idd --skill $skill -p agents -s global -y" >&2
    echo "No asm yet:      npm install -g agent-skill-manager" >&2
    echo "Verify:          asm list -p agents --json | grep '\"$skill\"'" >&2
    exit 1
  }
done

On a miss, stop and print the fix commands; do not start a sweep you cannot publish.

Repo Sync Before Edits (mandatory)

A catalog PR must start from origin/main with a clean tree so feature WIP cannot land in the sweep. All later commands run from the repo root:

cd "$(git rev-parse --show-toplevel)"
today="$(date +%F)"
# If the user named a YYYY-MM-DD, use that string instead of date +%F.
branch="$(git rev-parse --abbrev-ref HEAD)"
stashed=0
if [ -n "$(git status --porcelain)" ]; then
  git stash push -u -m "daily-offer-check pre-sync" || exit 1
  stashed=1
fi
git fetch origin && git pull --rebase origin "$branch" || exit 1
if [ "$stashed" = 1 ]; then
  git stash pop || exit 1
fi

Use that same $today for the branch name, --today flags, issue input, and PR title.

  • Stash tracked and untracked work before sync as above. On sync failure retain the stash; on pop conflicts stop and ask. Never drop a stash to get a clean tree.
  • If origin is missing or the rebase conflicts: stop, report the error verbatim, and ask. Never force-push.
  • Report-only: stop after fetch/rebase on the current branch. Do not create or check out chore/daily-offer-check-$today.
  • Publishing branches: require git status --porcelain empty before any checkout; restored WIP stops the run. Run the bundled-files check, then switch branches only with python3 skills/daily-offer-check/scripts/prepare_branch.py --today "$today", which must print OK branch. It creates chore/daily-offer-check-$today from origin/main, or resumes an earlier attempt today by fast-forward only (to the pushed branch, else origin/main); it refuses, without switching branches, a branch that has diverged or lacks origin/main (a leftover cut from an older main lacks this skill's current scripts). On refusal, stop and report its stderr; never rebuild the branch by hand (checkout -B, reset, branch -D). Then run the bundled-files check again and require python3 skills/daily-offer-check/scripts/check_scope.py --base origin/main --head HEAD to print OK scope, proving a resumed branch holds only sweep changes.

Leading words

  • active offer — expiry_date is null or ≥ today (an offer expiring today stays active).
  • official source — the offer's source_url and the provider's own pages (references/trust-policy.md). It wins every disagreement; corroboration never overrides it.
  • safe write — the only writes: bump verified_date on live; set title / amount / expiry_date / signup to the official values in a live verdict's updates; set expiry_date + verified_date to today on expired; merge the verdict's references into the reference trace. Never resolve a conflict.
  • reference trace — offers/details/<slug>.json → social_proof link entries. Evidence URLs are appended (deduped by URL, cap 10), curated entries are never removed, and an excerpt is refreshed only when the offer's terms changed.
  • scope gate — scripts/check_scope.py exits 0: the diff touches only existing offer YAMLs (safe-write fields), reference traces (append or refresh), and the three generated artifacts.
  • clean — an /issue-pr-review round whose summary is its Clean PR block (◆ PR Review: #<pr> (pass N — clean), Result: PASS) and whose round gate committed nothing; head is then the PR's headRefOid.
  • empty-diff exception — zero writes ⇒ no issue, no PR. Report instead; optionally comment on an open issue whose title contains stale content.
  • fail-soft — one dead URL or crashed worker does not abort the catalog. Mark that slug unverifiable and continue.

Step Completion Reports

After each major step, print ◆ <name> (step N of 9), its gate checks with √ / ×, and Result: PASS | FAIL | PARTIAL. Steps 3–8 respectively check coverage N/N, writes against applied length, validator exit 0, issue + PR URLs (or the empty-diff exception), a clean round, and MERGED. On failure print stderr and stop; do not report success for skipped gates. The final report includes all skipped steps.

The bundled-files check requires the six scripts (list_active.py, check_coverage.py, apply_verdicts.py, check_scope.py, prepare_branch.py, render_report.py), agents/verifier.md, references/trust-policy.md, references/apply-and-pr.md, references/review-and-merge.md, and root scripts/offer_model.py to exist. Run it before the first bundled script (in Repo Sync on publishing branches, before Step 1 on Report-only) and again after prepare_branch.py. Missing file: stop and name it. These are bundled files; skill dependencies are gated in Dependency Preflight.

Step 1 — Inventory (you)

From repo root:

python3 skills/daily-offer-check/scripts/list_active.py --today "$today"
# add --offers-dir <dir> and/or --slugs a,b when the branch selector says so

Save stdout to /tmp/daily-offer-check-$today-inventory.json (outside the repo). Done when:

  • exit 0
  • active_count equals len(offers)
  • offers is oldest-verified_date first

If the user named slugs that skipped_expired absorbed, say so in the report (named-but-expired is not a successful empty sweep).

If active_count is 0: print the Step 1 report, skip to Step 9, no PR.

◆ Inventory (step 1 of 9)
··································································
  Script exit 0:          √
  Active listed:          √ N (skipped_expired=M)
  Oldest-first:           √
  ____________________________
  Result:                 PASS

Step 2 — Fan out verifiers (workers, parallel)

Spawn workers in the same turn. One rule: workers = min(8, active_count). Split the oldest-first list into that many contiguous slices, as evenly as possible. Sequential degrade (no Agent tool): the same slices, one after another — say so in the Step 2 report.

Put these paths in each worker prompt as files to Read (you do not Read them):

  • skills/daily-offer-check/agents/verifier.md
  • skills/daily-offer-check/references/trust-policy.md

Plus the batch objects with every inventory field: slug, path, title, provider, amount, expiry_date, source_url, verified_date, verification, sha256; also pass today.

Each worker's Output: a JSON array, one object per input slug — never a silent drop. Shape is pinned in agents/verifier.md.

Workers read pages with TinyFish and retry with the Lightpanda browser when those tools are available, and otherwise with the host's built-in read-only web tools, per agents/verifier.md → Web tools. A missing tool is never a reason to stop the sweep. They must not write offers/, commit, or ask questions. On tool failure they return unverifiable with the error in reason.

Missing slugs after the first wave: respawn once for those slugs only. Still missing → synthesize unverifiable / reason: worker dropped slug / updates: {} / references: [].

◆ Verify (step 2 of 9)
··································································
  Workers spawned:        √ K in one turn (or sequential degrade)
  Every slug returned:    √ N/N
  ____________________________
  Result:                 PASS

Step 3 — Coverage merge (you)

Concatenate worker arrays into /tmp/daily-offer-check-$today-verdicts.json. If a worker wrapped JSON in markdown fences, strip the fences once so the file is a JSON array — that is repair, not rewriting verdicts. Extra slugs, duplicates, and malformed records must fail coverage; request corrected worker output rather than silently dropping or rewriting verdicts. Then:

python3 skills/daily-offer-check/scripts/check_coverage.py \
  /tmp/daily-offer-check-$today-inventory.json \
  /tmp/daily-offer-check-$today-verdicts.json

Done when the script prints OK N/N slugs covered. On exit 1, fix as the stderr says, then re-run once. Still failing → stop, paste stderr, no writes.

Report-only branch stops here. Print the table from Step 9 (no PR URL) and halt.

Step 4 — Apply safe writes (you)

python3 skills/daily-offer-check/scripts/apply_verdicts.py \
  --today "$today" \
  --inventory /tmp/daily-offer-check-$today-inventory.json \
  --verdicts /tmp/daily-offer-check-$today-verdicts.json \
  > /tmp/daily-offer-check-$today-applied.json

The script is the sweep's only writer (Step 7's /issue-pr-review fixes are the one other, bounded by the scope gate): you never hand-edit YAML or detail JSON. Its default trusted write root is repository offers/; inventory cannot redirect it. Fixture orchestration never reaches this step. Only offline helper tests explicitly pass a temporary --offers-dir. Stale inventory fails: regenerate and re-verify. Fresh same-day reruns are idempotent; old snapshots are not reusable. Done when exit 0 and writes equals len(applied): offer YAMLs (bumped / updated / expired) plus reference traces (references). Steps 5, 6, and 9 read results, references, and applied from that file.

Step 4b — Regenerate committed artifacts (you)

Skip when writes == 0 (empty-diff — no artifact changed).

YAML writes drift the committed generated artifacts — repo-root index.json (the load-offers vitest contract deep-equals it against offers/) and app/public/llms.txt / llms-full.txt. Regenerate them so the sweep's PR does not ship a stale catalog:

(cd app && node scripts/load-offers.mjs --index-json ../index.json) || exit 1
(cd app && node scripts/generate-llms.mjs) || exit 1

Done when git status --porcelain shows at most offers/ writes (offer YAMLs and offers/details/ JSON) plus index.json, app/public/llms.txt, app/public/llms-full.txt — anything else is out of scope for the sweep commit.

Step 5 — Validate (you)

python3 scripts/validate_offers.py

Exit 0 required. If it fails, undo only the files in applied[] (prefix each path with offers/; git checkout -- <literal paths> for tracked files, rm -- <literal paths> for detail files this run created per references[].created) and stop — never open a PR on an invalid catalog.

Step 6 — Issue + PR (you, via /issue-creator)

Read skills/daily-offer-check/references/apply-and-pr.md now (not earlier). Empty-diff first: if writes == 0, create no issue and no PR — follow the exception in that file. Otherwise, in this order:

  • tracking issue: the user's named stale-content issue, an open issue from an earlier attempt today, or /issue-creator "<input>" --auto
  • stage only applied[] paths plus index.json, app/public/llms.txt, app/public/llms-full.txt — never git add . — and check_scope.py --staged prints OK scope
  • one commit on chore/daily-offer-check-$today, pushed
  • one PR against main: body rendered by render_report.py --pr-issue into a file and passed with --body-file (first line Closes #<issue>)
  • older open sweep PRs closed as superseded

Done when gh pr view --json url,number returns both, or the exception fired. Bind issue and pr.

Step 7 — Review loop (you, via /issue-pr-review)

Read skills/daily-offer-check/references/review-and-merge.md now. Run up to 3 rounds of /issue-pr-review <pr> --auto --no-merge, each followed by that file's round gate (artifact regeneration, scope gate, validator). Done when a round is clean; bind head. A failed gate (report the offending paths for manual triage), stagnation (the same findings twice in a row), or 3 rounds without a clean one → stop with the PR open; the run is PARTIAL.

Step 8 — Merge (you)

No-merge: skip and report the clean PR URL. Unattended: run the merge gate in references/review-and-merge.md against head (scope gate; PR open, mergeable, on main, at head; every check green including validate), then gh pr merge "$pr" --squash --delete-branch --match-head-commit "$head". Done when the PR reads MERGED. Any failed check → no merge, PR open, PARTIAL.

Step 9 — Report (you)

Render the per-slug table with the script, never by hand (quotes are untrusted page text). Drop --applied on Report-only:

python3 skills/daily-offer-check/scripts/render_report.py \
  --inventory /tmp/daily-offer-check-$today-inventory.json \
  --verdicts /tmp/daily-offer-check-$today-verdicts.json \
  --applied /tmp/daily-offer-check-$today-applied.json

It prints the summary, the official-source updates (proposed ones on Report-only), and one row per inventory slug: slug | prior verified_date | verdict | write | updates | refs | evidence, where refs shows the reference trace change (new, +added, ~refreshed, -dropped). Print it, then:

◆ Daily offer check (<today>)
··································································
  Inventory:              √ N active
  Coverage:               √ N/N
  Safe writes:            √ W files (B bumped, U updated, E expired, R traces)
  validate_offers.py:     √ / — skipped (report-only or empty)
  Issue:                  √ #<n> / — skipped
  PR:                     √ <url> / — empty-diff / — report-only
  Superseded:             #<old PRs> / — none
  Review:                 √ clean in round K / × <why not> / — skipped
  Merge:                  √ merged / — no-merge / × <failed check> / — skipped
  ____________________________
  Result:                 PASS | FAIL | PARTIAL

PARTIAL if any slug is unverifiable or conflict, or an Unattended run ended without a merge (review not clean, a gate refused). FAIL only when a gate stopped the run before a complete table.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Read the Free AI Credits directory — enumerate currently-claimable free AI credit offers, fetch per-offer terms and claim steps, and interpret the site's trust labels — using only static HTTPS GETs.

The English summary is being prepared. Showing the original description.

luongnv89/freetokens11Updated Oct 25, 2026

Find top 10 new or improved free-AI-credit offers on X, Reddit, and the web, verify them officially, and open one review PR. Use for /offer-hunter or hunting new AI credits. Don't use for one known offer or catalog re-checks.

The English summary is being prepared. Showing the original description.

luongnv89/freetokens11Updated Oct 25, 2026

Publish or refresh a few free-AI-credit offers from screenshots, X posts, or text — official sources win, X-first ones ship as early offers; validates, diffs, commits on approval. Don't use for hunting new offers, catalog re-checks, or bulk imports.

The English summary is being prepared. Showing the original description.

luongnv89/freetokens11Updated Oct 25, 2026

See all skills by luongnv89

Report a problem with this skill