Skip to content

09 — Onboarding a site ​

The template for onboarding a new site. The two design decisions it is based on (server-observable reports, a storage-agnostic contract) are defined in doc 02. An installation's own onboarding record is kept in its notes; this page contains what applies to any site.

Onboarding a site settles four things: what its nightly report can count, which search queries its panel tracks, which served surfaces a deploy could break, and what its own repository's agents must read instead of pulling data from providers themselves.

For the first release, the operator implements the report endpoint and writes the asset's AGENTS.md. NoticeOS shows observed report coverage; asset agent execution stays manual, and its pause check is unavailable. Completing data setup does not grant agent execution authority. See doc 14 for the onboarding screen as built and the kill-switch and resume contract.


Design decision 1: a report counts only what the site can observe — and a database is not the contract ​

A site need not have a database. A prerendered static site with a thin Worker (an affiliate beacon, hosted share links, short-link redirects) stores its engagement data in GA4 custom events, which its own worker cannot query when it sends its report.

Two rules follow, and they belong in the contract, not in one site:

  1. The site's report contains only what its own infrastructure can measure (worker requests, storage counts, first-party database rows). Client-side analytics (GA4) are an external signal — NoticeOS pulls them centrally (the same collection path as Search Console), and the Tower joins them on the site id. Never make a site call the GA4 API to report on itself.

  2. Cloudflare Analytics Engine is the standard report counter for a site without a database. One binding, writeDataPoint() at the worker routes worth counting, and the SQL API returns last24h / avg7d for the report endpoint. No schema, no migrations, and the free tier covers this usage. A site that already has a database keeps using it; Analytics Engine is the minimum for a site without one, not a required migration.

    Analytics Engine SQL gotchas (every site that counts with it copies these): the dialect is ClickHouse-derived and function names are case-sensitive — now() works, NOW() is an unknown function; if() requires both value branches to share a type, so a sampling-corrected sum needs if(cond, _sample_interval * double1, 0.0) — the Int literal 0 returns a 422 against the Double product. Always include the SQL API's response body in the site's error output: its 422 responses state the exact error, and reducing them to a bare status code costs a deploy-and-retry round trip per mistake. Count in one scan, with a per-window if() slice for each window.

The site's report (the metrics set) ​

Server-observable counters at existing worker routes — no new user-facing behaviour:

jsonc
{
  "asset": "example.com",
  "metrics": {
    "affiliateClicks":  { "last24h": 0, "avg7d": 0.0, "total": 0 },  // an existing click beacon
    "receiptsHosted":   { "last24h": 0, "avg7d": 0.0, "total": 0 },  // a hosted share (dedupe hits count separately)
    "receiptVisits":    { "last24h": 0, "avg7d": 0.0, "total": 0 },  // a shared link opened — the viral loop closing
    "apiRequests":      { "last24h": 0, "avg7d": 0.0, "total": 0 },  // public API use (total = within retention)
    "catalogItems":     { "last24h": 0, "avg7d": 0.0, "total": 2402 }
  },
  "capabilities": ["affiliateClicks", "receiptsHosted", "receiptVisits", "apiRequests"],
  "flags": []
}

A site reports only the metrics it has: no signups, no plans, no feedback until it has them. A stock fact such as catalogItems is sent in metrics for the compact card's current-stock row, but stays out of capabilities and the anomaly rules, because an unchanged catalogue is not a zero-activity day.

The product-use stage view comes from GA4. The archive collects one rolling 28-complete-day eventName × eventCount × totalUsers report a day. Each count is unique within its own action and window, so the aggregate cannot prove that one person went from one step to the next: the Tower draws no causal arrows, labels a step percentage as a stage-volume ratio rather than a cohort conversion, and never adds daily uniques together. A missing event row is unknown ("None recorded"), never a zero.

A counter that records almost no use is a finding about the product, not a card to keep: a share counter that records one opener in 28 days belongs in diagnostics, not in a headline total.

S1 — the SERP panel (port a site's own, don't rebuild) ​

If the site already runs its own SERP panel (a live-SERP script writing dated snapshots), port it into the central panel and add the device dimension rather than building another. Seed the panel from the site's own Search Console: its brand and navigational terms, the head terms it gets impressions for, and the strategic bets its current plan is testing — for example example · example login · example calculator · best free calculators. Record the baseline values the alerts will compare against: on how many seeded queries an AI Overview appears, and on how many it cites the site.

S2 — the views a site needs first ​

  • Striking distance: the queries at positions 4–10 and the share of all query impressions they hold. Position-bucket migration (4–10 → 1–3) is the primary success metric of most SEO plans.
  • CTR against position by page type: a title-template defect can keep a page type at a fraction of the expected CTR for a quarter before anyone notices. Make page type a first-class Search Console dimension — derivable from the path prefix (/guides/, /calculators/, /{category}/{item}).
  • A brand regex for the brand-vs-generic split, for example /\bexample\b|\bexample score\b/i.

S5 — regression guards (the site's own list) ​

  • The served tables and answers AI crawlers read: check the count of table rows in the served HTML per page family; a 50% drop is a deploy regression. Check front-loaded answer sentences on a five-page sample.
  • robots.txt and the AI-crawler allowlist, and the consent-mode head: the consent default must precede the analytics loaders in served HTML, and a reject or Global Privacy Control must skip the loaders. A consent regression is a compliance incident, not only an SEO one.
  • A site whose own CI already audits its build (link graph, sitemap, canonicals) needs fetch guards on the served layer only; don't duplicate what the site's CI checks at build time.

S6 — monetization signals (platform state — canonical in doc 08) ​

A site with ads or affiliate revenue uses the S6 family: ads.txt reachability and content, an ad-network review window, a storage object count against its lifecycle rule (unbounded growth means the rule was removed without anyone noticing), the affiliate-click trend from its report, and the consent check it shares with S5.

Deploy annotation ​

A site whose deploy pipeline already posts a deploy notification (a Discord webhook, for example) sends the same notification to the NoticeOS ingest to mark deploys on its S1/S2 timelines — no new integration code in the site. Its first natural experiment is then already set up: the first deploy batch after the panel baseline, compared against that baseline.

AGENTS.md mapping (before a builder touches the site) ​

The source material usually exists; the task is to summarize it, not to research it. Per doc 04, dated and volatile facts (a review window, a pending application) go in the pack's expiring STATE section, not its rules.

AGENTS.md sectionWhere it usually comes from
Positioning / thesisthe site's current SEO or growth plan (its one-sentence strategy)
Brand & voice rulesthe site's design doc (copy rules, banned openers, typography); trademark nominative-use rules
Never-fabricate ruleclaims only from the site's research sources or computed data; never invent a number
Do NOT touchscoring weights and thresholds without data review; consent and privacy surfaces; byte-stability constraints
CI barthe site's verify, build and smoke commands
Deploy modelwho pushes and deploys, and what the pre-commit hook regenerates
Report endpointadded with the report (this doc)
Gotchasfiles outside the type checker; hook races worth a retry

Onboarding order ​

  1. The Analytics Engine binding and counters at the worker routes; the report endpoint and the nightly push — contract-compliant from day one.
  2. Port the site's own SERP panel into the central S1 panel; keep the in-repo command for ad-hoc runs.
  3. The S5 fetch guards (served tables, answers, robots, consent head).
  4. Search Console and GA4 central pulls include the site with no site-side work.
  5. Write the site's AGENTS.md from the mapping above, with the panel section from doc 20.

Released under the AGPL-3.0 license.