agentsclimarketplace

Hoo daily

Skill shalintripathi/organic-os/plugin/skills/hoo-daily

Run an always-on SEO/AEO growth loop on your own site with Claude: pulls GA4 and Search Console signals, proposes fixes with the reasoning attached, applies approved changes to WordPress, verifies they landed, and learns from outcomes. Every write is human-approved. MIT, no telemetry, no scraping.

Install
npx -y skills add shalintripathi/organic-os --skill hoo-daily

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 18 days oldThe repository was created 18 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use for the daily signal pull - "run the daily", scheduled daily routine, or "pull today's numbers". Appends observations to the brain repo signals; never mutates the site or the skillbook.

SKILL.md

11.0 KB, as published. Nobody here has run it

Daily signal pull (Generator role - append only)

Resolve the brain: use registry.get_active() when running interactively; scheduled runs receive the brain path from the routine configuration.

  1. Run core.contracts.check_schema(brain_path) first. If not compatible, relay the action string and stop before any of the steps below.

  2. Read site-profile.yaml. Determine available sources: GSC connector, GA4 connector, tracked keywords in keywords/tracking.yaml, WordPress endpoint. The sessions and referral pulls read the analytics slot (ADR-0009) - GA4 is the adapter today; see CONTRIBUTING.md's "Contributing an analytics adapter" to add Clarity or Matomo.

  3. Pull, for yesterday (or since the last signal date - read the latest file in signals/): GSC clicks/impressions/CTR/position for top and tracked queries; GA4 sessions; spot-check 3 tracked keywords in one AI engine, rotating. 2.5. AI-referral segmentation (runs only when GA4 is reachable): segment referral sessions whose source matches a known AI surface and record one ai_referrals: line in the daily signal - the session count plus the top landing pages (cap 3, each with its count). The known AI surfaces: chatgpt.com, perplexity.ai, gemini.google.com, copilot.microsoft.com, claude.ai. This list is maintained here, in this skill, and reviewed quarterly - hoo-weekly and hoo-monday-report quote it, never extend it (see docs/INFORMATION-MAP.md in the repo). Zero matching sessions is a real value - write ai_referrals: 0. GA4 unreachable means no ai_referrals: line at all, never a guessed one. 2.6. Headline metrics line: record what steps 2 and 2.5 actually pulled as ONE structured signal line so later runs can parse it - the exact tokens clicks: N, impressions: N, sessions: N on a single line (site-wide daily totals; a source that was not pulled omits its token, never writes 0). Together with step 2.5's ai_referrals: N these are the daily's structured metric forms; the anomaly check below and the CSV export (skills/hoo-export, core.export) parse exactly these tokens. 2.7. Anomaly check: for each headline metric this run actually has - GSC clicks, GSC impressions, GA4 sessions, ai_referrals - collect the same metric's values from the trailing 7 daily signal files (parse the structured lines from steps 2.5 and 2.6; days without the metric are gaps, never zeros) and compare today's value to the median of those trailing values.

    • Baseline discipline: fewer than 4 prior daily signals carrying the metric -> skip that metric with a one-line note in today's signal ("anomaly check: skipped <metric>, no baseline yet") - no baseline, no alert.
    • Noise floor: a trailing median below 10 -> skip the metric; percent swings on single-digit medians are noise, not signals.
    • Threshold: today deviating from the median by more than 40 percent in either direction flags the metric. The default is profile-configurable via the additive alerts: {threshold_pct: 40} key in site-profile.yaml (absence means 40; see docs/site-repo-contract.md). Each flagged metric -> one P1 signal in falsifiable form: the metric, today's value, the 7-day median, the direction, and a cause line that obeys the attribution rule below. These P1 signals JOIN the daily alert below - never a separate message.

    THE ATTRIBUTION RULE, canonical here; hoo-weekly and onsite-measure quote it. A cause may not be asserted without naming the comparison that was actually run. Every causal claim carries three parts:

    • the claim: "clicks fell because it was a weekend";
    • the comparison actually performed: "today vs the 3 most recent same-weekday signals";
    • what would falsify it: "if next Saturday lands at the weekday median, seasonality was not the cause". If the comparison was not run, the cause is recorded as cause: unknown (no same-weekday comparison run) - never a plausible-sounding guess. "Weekend seasonality" is not an explanation unless the same-weekday prior-period comparison was actually made, and a deploy is not an explanation unless the deploy record was actually read. Candidates worth comparing against: same-weekday prior periods, a deploy or release record, a tracking or tag change, a SERP feature shift. An unknown cause is a complete signal, not a failed one: it says what moved and what has not yet been checked. The caveat still rides in the signal itself: this check compares one day against a 7-day median, so seasonality can trip it. Rationale: alerting is the retention feature of every commercial monitor; ours rides the existing channel taxonomy instead of adding a dashboard (ROADMAP, v0.4).
  4. Write one append_signal line per notable observation (threshold: any WoW move > 10% or position change > 2 or a new AI citation appearing/vanishing). Quiet days produce one line: "no notable movement (checked: <sources>)". If neither GSC nor GA4 was reachable this run (both connectors read anything other than verified in connectors:), write the literal line no-data: GSC/GA4 not reachable from this runtime (checked: none) instead

    • this exact string is what step 3.5 below counts and greps for, so do not paraphrase it. 3.5. No-data escalation. After writing today's signal, count consecutive daily signal files - today's plus however many immediately prior days' files also contain a no-data: line, walking backward by filename date and stopping at the first file that does not (a day with real data, or a missing file, breaks the streak). If that count reaches 3:
    • Check the last 7 days of signal files for a nudge-sent: line. If one is already there, skip sending - do not repeat the nudge more than once per 7 days.
    • Otherwise send one nudge through the configured approval channel. This is a plain notification, not an approval item - send_item-style text delivered directly over the channel (telegram: one sendMessage; slack: one post; email: one send; in-session: print it; pr-merge: no live channel mid-cycle, so just log it to the signal and skip delivery), not a create_item/approve-reject proposal: "3 daily runs with no analytics data - GSC/GA4 are not reachable from this runtime. Fix: run /organic-os:setup and use the connector wizard for GSC/GA4, or connect directly - claude.ai Settings -> Connectors, or /mcp / claude mcp add <server> in Claude Code."
    • Mark the nudge sent by appending nudge-sent: no-data escalation (GSC/GA4) to today's signal file via append_signal - this is the state marker; do not create a new state file for it.
  5. If an observation crosses P1 (drop > 30% on a money page), also create_item(kind="onpage-fix"...) or kind="strategy" and notify per the approval channel. Only send items where is_notified(item) is false; call mark_notified(path) right after a successful send.

  6. Outcome follow-ups: for items in outcomes/ with a due measurement date of today, run the measurement per skills/onsite-measure and record. 5.5. Applied-change re-verification: scan outcomes/ for records with a reverify: block whose until is still in the future (written by skills/onsite-apply after a successful verify; see site-repo-contract.md). For each with due <= now, fetch the live values via the CMS adapter - the same fields the apply verified (title, meta description, canonical) - and compare to what the outcome record says was applied. Match: note the passing re-check in the outcome record, nothing else. Mismatch: append one P1 signal - "applied change no longer live - external revert suspected; re-propose" - naming the item id, the field, the applied value, and the live value; it goes out in the daily alert below. Once until passes, stop re-checking: the drift watch owns the long horizon (the baseline was already refreshed at apply time). Records without a reverify: block are never re-checked this way.

  7. If brain mode is git: commit and push with message "signals: YYYY-MM-DD".

Missing sources are stated, never guessed. This skill NEVER writes skillbook.md. The no-data nudge in step 3.5 is a notification, never an approval item - it needs no decision, just a fix.

Run cost: wrapper-invoked runs land one row (date, duration, tokens where the CLI reports them) in ~/.config/organic-os/cost-ledger-YYYYMM.tsv; the wrapper writes it, this skill never does - the Monday report reads it.

Drift watch

Runs only when the profile's WordPress connector is verified - drift is WP-only, there is no page inventory to snapshot without it.

  1. Build the tracked-page set: the WordPress post id recorded in every outcomes/*-rollback.json snapshot (already captured by onsite-apply's verify step), any proposals/ item's target page still in play, plus the homepage - deduped, capped at 20.
  2. First run for this brain (onsite.drift.baseline_path(root) does not exist yet): onsite.drift.snapshot_pages(wp, page_ids), then save_baseline. Append one signal noting the baseline was established and how many pages it covers.
  3. Every later run: snapshot the same tracked-page set again and onsite.drift.compare(root, snap). Empty list: one quiet signal line, nothing else to do. Any diff: one P1 signal per changed page, naming the field, the old value, and the new value - "changed outside the loop: field, was, now - if this change was yours, refresh the baseline; if not, investigate theme or plugin updates."
  4. Refresh the baseline (save_baseline) only AFTER the signal for that run's diff has been written, so a given drift is reported exactly once and never silently re-baselined out from under a pending investigation.

No WordPress connection: skip silently, no note needed - unlike the GSC sections in the weekly routine, there is no page inventory to have skipped pulling.

Daily alert (actionable only)

After every section above has run, decide whether the operator needs to hear anything today. Actionable content is exactly:

  • P1 signals created by this run (anomaly flags from step 2.7, drift "changed outside the loop", money-page drops from step 4, failed re-verifications from step 5.5)
  • the no-data nudge from step 3.5

If any exist, send ONE message through the configured approval channel, using the same channel-neutral delivery as the step 3.5 nudge (telegram: one sendMessage; slack: one post; email: one send; in-session: print it; pr-merge: log to the signal file and skip delivery). When the no-data nudge fires on the same day, it rides inside this one message instead of going out separately - never two messages per day. Quiet days send NOTHING: no "all quiet" spam. The signal file already records the quiet day; silence on the channel means no action needed, never that something was hidden.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.