diff --git a/AGENTS.md b/AGENTS.md index 05a139f..c9a73fe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,8 @@ Authority sources in descending order: - When code depends on a D1 schema change, a migration requires explicit approval and remote verification before any Worker deployment. - Do not print or commit secret values. - The optional `REPORT_READ_TOKEN` is a secret-binding capability, not repository data. Do not generate, provision, inspect, remove, or rotate its production value without explicit secret-operation approval. It must be independently generated and cryptographically random, contain 32 to 128 URL-safe ASCII characters (`A-Z`, `a-z`, `0-9`, `_`, and `-`), and differ from `ADMIN_TOKEN`; an identical non-empty configuration intentionally fails every protected read and write closed. -- `npm run --silent diagnostic:ceo` is the sole canonical operator helper for a Lighthouse production CEO read. Its fixed, non-echoing behavior does not grant endpoint approval; every invocation still requires explicit production-read scope. +- `npm run --silent diagnostic:ceo` is the sole canonical operator helper for a Lighthouse production CEO read. It strictly validates CEO response contract `1.1` or `1.2` with metric-definition version `1.1`. Its fixed, non-echoing behavior does not grant endpoint approval; every invocation still requires explicit production-read scope. +- Active-production receipt: the separately approved 2026-08-27 release/audit established Lighthouse `1.30.0`, Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` at 100% traffic, deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`, from merged `main` commit `4e80d65de01606c15100a99e7852ce19c5e6cd98`; the approved CEO read at `2026-08-27T15:08:12.012Z` verified the report-read path. `SOT.md` and `OPERATIONS.md` are the detailed authority. This receipt does not authorize another endpoint request, deployment, or secret operation. - Cross-repository contracts with Agent Smith, BUS Core, buscore-site, or the leads database must be documented when touched. ## Canonical Analytics Access and Diagnostics @@ -59,7 +60,8 @@ Operational constraints: - `ADMIN_TOKEN` remains broad: it protects report reads and is the only credential accepted by the mutating `POST /campaign`, `POST /notes`, and `POST /report/snapshot` routes. Optional `REPORT_READ_TOKEN`, when separately provisioned, is accepted only through `X-Report-Token` for `GET /report`; the admin credential remains a backward-compatible report fallback and the read credential never authorizes a write. - The report-read split does not make report requests zero-mutation. Bare/fleet/site reports refresh stored traffic, and every report view—including CEO—can increment `metrics_daily.errors` when assembly fails. The helper is therefore Class 2 and approval-gated. - Treat the helper's three static failure lines literally: `access blocked` is credential/access evidence, `report unavailable` is application evidence with a possible error-counter increment, and generic `diagnostic failed` is an unclassified transport/safety/contract failure. None authorizes a retry or alternate probe. -- The local 1.30.0 bundle contains no secret value, provisioning, deployment, or production verification. Do not claim production read-token availability until separately approved external activation and readback prove it. +- The local 1.31.0 target advances the CEO response contract to `1.2` while metric definitions remain `1.1`; active production remains Lighthouse 1.30.0/CEO `1.1` until an explicitly approved promotion. Direct CEO `1.2` sources use an activity watermark, which is not health, freshness, completeness, or failure evidence. A composite-source watermark is the conservative earliest required-component watermark; a day-bucket watermark is a reporting bound, not an event timestamp. Only `service_probes` uses scheduled-probe fresh/stale semantics. +- Do not promote Lighthouse 1.31.0 until Agent Smith's owning governed bundle accepts both strict CEO `1.1` and `1.2`, and retain its `1.1` support for Worker rollback. Agent Smith `0.26.0` established deployed dual-version compatibility on 2026-08-27: merged `main` commit `5519764d959cf1d6a505280814c59e82854f9bda` passed CI run `33105121602` and deployment run `33105121605`, including Discord command registration, and the owner confirmed `/report` works against active Lighthouse CEO `1.1`. A final Lighthouse audit then tightened the `1.2` schema to reject non-null inquiry totals when that source is unavailable. The deployed consumer accepts every valid producer output but its pinned validator predates that additional rejection; deploy and verify the matching Agent Smith validator patch before Lighthouse promotion. Until then, report `BLOCKED BEFORE LIGHTHOUSE 1.31 PROMOTION`. - Agent Smith still carries `LIGHTHOUSE_ADMIN_TOKEN` for report reads and monthly snapshot writes. Do not remove or rotate that credential until an owner-approved snapshot-specific authorization split or write removal is implemented and verified in the owning repositories. - Many GET/HEAD surfaces change evidence. Bare/fleet/site reports refresh stored traffic; report failures can increment errors; artifact HEAD records raw/HEAD truth; update, redirect, artifact, telemetry, and admin-write routes are not passive probes. - Git publication is not zero-mutation. The 1.29.3 `main` merge proved that the former Workers Builds production command could promote active traffic independently of the checked-in gate. Durable readback on 2026-08-26 confirmed that both non-production and production publication now use `npx wrangler versions upload`. Pushes still create version or preview state, while active production promotion is reserved for the explicitly approved manual GitHub workflow. Any future external-setting drift reinstates `BLOCKED BEFORE MERGE`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 56ac9db..03f5e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [1.31.0] - 2026-08-27 + +- Advanced only the strict CEO report contract from `1.1` to `1.2`; `metric_definition_version` remains `1.1`, so metric definitions, window boundaries, counts, and historical meaning do not reset. +- Added `freshness_basis` to every CEO source state. Direct aggregate sources use `activity`, remain `freshness: "unknown"`, use `activity_only` when an activity watermark exists, and expose `data_through` only as that watermark. A single-component watermark is its latest trusted observation; a composite watermark is the conservative earliest required-component watermark; a day-bucket watermark is a bounded reporting timestamp rather than an event timestamp. Sparse silence is no longer mislabeled `fresh`, `stale`, or failed; a successful source with no history remains `unknown`/`source_history_missing`. +- Reserved `freshness_basis: "scheduled_probe"` and `fresh`/`stale` for `service_probes`. A successful query with no probe history is available/unknown with `probe_history_missing` and empty details; a partial active-target set is available/unknown with `probe_history_incomplete`, observed details, and the latest observed watermark; query failure alone is unavailable. A complete six-target set retains its oldest-required-target 36-hour fresh/stale rule. Exact latest-timestamp ties now resolve deterministically with failure before success and a stable row-ID tie-break, preventing a tied pass/fail pair from producing order-dependent status. +- Clarified that voluntary-inquiry windows count unique lead records by `created_at`; a later update is not counted as a new inquiry record and does not move that record into a later created-at window. Added the aggregate-only `details.voluntary_inquiry_records` diagnostic with `total_records`, `last_created_at`, and `last_updated_at`, plus the exact limitation `existing_lead_updates_increment_inquiry_windows: false`; it returns no lead PII, record identifiers, email, form content, or raw attribution. The strict schema now rejects non-null inquiry window totals whenever the inquiry source is unavailable. +- Preserved the strict `1.1` schema for rollback and made the canonical CEO diagnostic helper strictly validate either `1.1` or `1.2` while requiring metric-definition version `1.1`. Agent Smith `0.26.0` established deployed dual-version compatibility on 2026-08-27: merged `main` commit `5519764d959cf1d6a505280814c59e82854f9bda` passed CI run `33105121602` and deployment run `33105121605`, including Discord command registration, and the owner confirmed `/report` works against active Lighthouse CEO `1.1`. The final Lighthouse audit subsequently added the unavailable-source inquiry-count rejection. Deployed Smith accepts every valid `1.2` producer output and retains `1.1` rollback support, but exact validator parity requires a matching governed Agent Smith patch before Lighthouse production promotion. +- Preserved all routes, headers, authorization behavior, CORS, Worker configuration and bindings, D1 schema and storage semantics, retention, schedules, service-probe/canary requests, ingestion, external integrations, and secret requirements. No migration, secret operation, direct SQL, configuration change, or producer change is required. +- Corrected the production baseline from the separately approved 2026-08-27 receipts: release workflow run `33086080869` succeeded for merged `main` commit `4e80d65de01606c15100a99e7852ce19c5e6cd98`; Lighthouse `1.30.0` Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` is active at 100% under deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`; and the approved CEO read occurred at `2026-08-27T15:08:12.012Z`. No build timestamp is inferred. +- This local 1.31.0 bundle made no production endpoint request, Cloudflare read, upload, deployment, traffic change, D1 mutation, secret operation, or external-system change. The production facts above are retained receipts from the separately approved prior audit and release steps; production remains on CEO contract `1.1` until a separately approved 1.31.0 promotion. + ## [1.30.0] - 2026-08-26 - Added the optional `REPORT_READ_TOKEN` Worker secret and exact `X-Report-Token` authentication path for every `GET /report` view. The read secret must be independently generated, cryptographically random, contain 32 to 128 URL-safe ASCII characters, and differ from `ADMIN_TOKEN`; existing exact `X-Admin-Token`/`ADMIN_TOKEN` report access remains backward-compatible when the configuration is distinct. diff --git a/OPERATIONS.md b/OPERATIONS.md index 3ff6a79..efe54fc 100644 --- a/OPERATIONS.md +++ b/OPERATIONS.md @@ -2,13 +2,16 @@ - Status: current operational runbook - Scope: Lighthouse analytics access, evidence interpretation, incident diagnosis, and release-control boundaries -- Repository baseline: Lighthouse `1.30.0` least-privilege report diagnostics local governed bundle; CEO report and metric-definition contract `1.1` -- Active production baseline: Lighthouse `1.29.3`, Worker version `ba611ac1-653d-47a2-a465-a85f4124b6b6`, 100% traffic, created `2026-08-26T22:57:30.628Z` from `main` commit `59231d09084d0fa4db71012b6f29550886c5b605` through Cloudflare build `793715ef-6123-4d98-a4a7-797634d07812` -- Last reconciled: 2026-08-26 -- Diagnostic access used for the 1.30.0 bundle: local repository inspection and tests only; the retained infrastructure baseline comes from the separately recorded, owner-approved 1.29.4 Cloudflare control-plane audit -- Production interaction for the 1.30.0 bundle: none; no Lighthouse endpoint, Cloudflare read, D1 query, log tail, scheduled invocation, deployment, route change, secret operation, or traffic change -- Evidence side effects: no Lighthouse application evidence was refreshed, counted, persisted, archived, or otherwise changed -- Audit snapshot: the preceding 24-hour Cloudflare overview showed 67 invocations and zero Cloudflare platform/runtime errors. This is historical platform telemetry, not proof of Lighthouse endpoint, report, source, probe, or delivery health +- Repository baseline: Lighthouse `1.31.0` local governed bundle; CEO response contract `1.2`, metric-definition contract `1.1`; production promotion pending +- Active production baseline: Lighthouse `1.30.0`, CEO response and metric-definition contract `1.1`, Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` at 100% traffic, deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`, from merged `main` commit `4e80d65de01606c15100a99e7852ce19c5e6cd98` +- Production release receipt: GitHub Actions run [33086080869](https://github.com/True-Good-Craft/lighthouse/actions/runs/33086080869) succeeded; no build timestamp is inferred from that receipt +- Consumer compatibility receipt: Agent Smith `0.26.0` merged as `5519764d959cf1d6a505280814c59e82854f9bda`; CI run [33105121602](https://github.com/truegoodcraft/Agent_Smith/actions/runs/33105121602) and deployment run [33105121605](https://github.com/truegoodcraft/Agent_Smith/actions/runs/33105121605) succeeded, including Discord command registration; the owner then confirmed `/report` works against active Lighthouse CEO `1.1`. Exact `1.2` validator parity remains pending after a final Lighthouse schema tightening. +- Last reconciled: 2026-08-27 +- Diagnostic access used for the 1.31.0 bundle: local repository inspection and tests plus read-only GitHub receipt checks for the Agent Smith merge, CI, and deployment; no Cloudflare or Discord control-plane read was performed for this change +- Retained approved audit receipt: the prior read-only Cloudflare audit confirmed the active version, 100% traffic, deployment, and source merge above; the separately approved CEO read occurred at `2026-08-27T15:08:12.012Z` +- Production interaction for the 1.31.0 source bundle: no 1.31.0 endpoint request, Lighthouse deployment, Cloudflare read, D1 query, log tail, scheduled invocation, route change, secret operation, or traffic change. The owner separately exercised Agent Smith `/report` against active Lighthouse 1.30.0/CEO `1.1` to verify the deployed rollback-compatible consumer. +- Evidence side effects: local work and GitHub receipt reads were non-mutating. The owner-confirmed Agent Smith `/report` verification was an external read-mostly analytics path that returned a Discord interaction response; a failed Lighthouse assembly could have incremented `metrics_daily.errors`, but no failure was reported and exact downstream audit/evidence state was not independently read. +- Historical 2026-08-26 audit snapshot: the preceding 24-hour Cloudflare overview showed 67 invocations and zero Cloudflare platform/runtime errors. This is historical platform telemetry, not proof of Lighthouse endpoint, report, source, probe, or delivery health ## Authority and Purpose @@ -42,8 +45,8 @@ If the approved endpoint, credential source, account context, or tool authorizat | System | Owns | Does not prove | |---|---|---| -| Lighthouse | Analytics ingestion, aggregate storage, source availability/freshness/coverage, scheduled service-probe evidence, protected report payloads, and CEO contract `1.1` | Agent Smith delivery, Discord delivery, or producer-side transmission success | -| Agent Smith | Report-mode selection, strict CEO validation, status/trust wording, daily/weekly/monthly presentation, Discord delivery orchestration/attempts, and monthly archive attempts | That every Lighthouse scheduled task ran, every source is fresh, Discord received a message, the watch channel is configured, posting permission exists, or a monthly archive write succeeded merely because Smith's private `/health` command responds | +| Lighthouse | Analytics ingestion, aggregate storage, source availability/coverage, activity-only source recency, scheduled service-probe health evidence, protected report payloads, and CEO contracts `1.1`/`1.2` | Agent Smith delivery, Discord delivery, or producer-side transmission success | +| Agent Smith | Report-mode selection, strict CEO validation, status/trust wording, daily/weekly/monthly presentation, Discord delivery orchestration/attempts, and monthly archive attempts; it must accept both CEO `1.1` and `1.2` before Lighthouse `1.2` promotion | That every Lighthouse scheduled task ran, every direct source is healthy, Discord received a message, the watch channel is configured, posting permission exists, or a monthly archive write succeeded merely because Smith's private `/health` command responds | | BUS Core | Optional, fail-soft product-event production; default-config startup/manual reads of Lighthouse `/update/check`; and user-triggered staging that re-reads the configured manifest URL without analytics query parameters before GETting the manifest-declared artifact | Lighthouse acceptance, availability, producer authenticity, or completed staging; the canonical client does not call Lighthouse `/manifest/core/stable.json` or `/download/latest` | | `buscore-site` | BUS Core public-site event production; `dev_mode`/`noAnalytics` suppression; early-access and Managed BUS routes; Turnstile/KV controls; and D1 lead writes | Lighthouse acceptance or persistence after a fail-soft browser send | | `tgc-site` | Consent-gated TGC event and Cloudflare Web Analytics production; `dev_mode`, privacy-signal, origin, and `test_mode` controls; and the static `/api/intake` client | Lighthouse acceptance or persistence after a fail-soft browser send, or proven backend delivery for `/api/intake` | @@ -62,7 +65,7 @@ Lighthouse remains independently runnable. A failure in one producer, consumer, | Runtime bindings and cron | `wrangler.toml` | | Route implementation | `src/index.ts` | | D1 schema history | `migrations/` | -| CEO response contract | `contracts/ceo-v1/report.schema.json` | +| CEO response contracts | `contracts/ceo-v1/report.schema.json` for strict `1.2`; `contracts/ceo-v1/report-1.1.schema.json` for strict rollback `1.1` | | BUS Core product telemetry contract | `contracts/buscore-product-telemetry-v1.json` and `src/productTelemetry.ts` | | Representative CEO states | `contracts/ceo-v1/*.json` | | Metric meanings | `SOT.md`, `README.md`, `BUS_CORE_TRAFFIC_TRUTH.md`, and `TGC_SITE_ANALYTICS_POLICY.md` | @@ -72,11 +75,11 @@ Lighthouse remains independently runnable. A failure in one producer, consumer, | BUS Core site producer | `../buscore-site/SOT.md`, `../buscore-site/CHANGELOG.md`, `../buscore-site/SITE_ANALYTICS_IMPLEMENTATION.md`, `../buscore-site/manifest/core/stable.json`, `../buscore-site/assets/js/site-analytics.js`, and `../buscore-site/tests/browser/deploy-analytics.test.mjs` | | TGC site producer | `../tgc-site/SOT.md`, `../tgc-site/contracts/lighthouse-analytics-contract.md`, `../tgc-site/assets/js/telemetry.js`, and `../tgc-site/tests/telemetry-payload.test.js` | -Sibling paths above describe the audited local checkout layout. If a repository is absent, stop that cross-repository branch rather than searching unrelated locations. A checked-in buscore-site manifest is a repository release projection that can lag production; it is not live endpoint proof. BUS Core facts were last reconciled against released version `1.4.2`, and buscore-site source facts against its merged `1.4.2` release-sync change, on 2026-08-26. Agent Smith facts remain reconciled against shipped version `0.25.2`; an unshipped local `0.25.3` documentation bundle is not current authority. Older Agent Smith SOT paragraphs that call `/report` a raw diagnostic product are superseded: in current production `ceo_v1`, the private `/report` command renders the CEO business/decision product. The retained `formatDiagnosticReport()` is test-only and has no command/runtime route. +Sibling paths above describe the audited local checkout layout. If a repository is absent, stop that cross-repository branch rather than searching unrelated locations. A checked-in buscore-site manifest is a repository release projection that can lag production; it is not live endpoint proof. BUS Core facts were last reconciled against released version `1.4.2`, and buscore-site source facts against its merged `1.4.2` release-sync change, on 2026-08-26. Agent Smith facts were reconciled on 2026-08-27 against deployed version `0.26.0`, merged `main` commit `5519764d959cf1d6a505280814c59e82854f9bda`, and the owner-confirmed CEO `1.1` `/report` check. Older Agent Smith SOT paragraphs that call `/report` a raw diagnostic product are superseded: in current production `ceo_v1`, the private `/report` command renders the CEO business/decision product. The retained `formatDiagnosticReport()` is test-only and has no command/runtime route. ## Deployed Resource Map -The following production identities were verified by approved read-only control-plane access on 2026-08-26. Repository-controlled values are pinned in `wrangler.toml`; external attachments remain Cloudflare control-plane state. +The following production identities were verified by approved read-only control-plane access on 2026-08-26 and the active deployment was reconciled again through the supplied approved 2026-08-27 audit receipt. Repository-controlled values are pinned in `wrangler.toml`; external attachments remain Cloudflare control-plane state. | Binding, attachment, or trigger | Verified production resource | Authority and purpose | |---|---|---| @@ -93,7 +96,7 @@ The following production identities were verified by approved read-only control- | Cron | `5 0 * * *` | Active at `00:05 UTC`; matches checked-in configuration | | Persistent logs and traces | Disabled | No retained Worker log/trace evidence is available unless separately approved and enabled | -The last approved control-plane inventory found active Worker secret names `ADMIN_TOKEN`, `CF_API_TOKEN`, `CF_ZONE_TAG`, `DISCORD_WEBHOOK_URL`, `IGNORED_IP`, `PRICE_GUARD_KEY`, and `TELEMETRY_RATE_LIMIT_SECRET`. Values were not read or exposed. Version 1.30.0 adds the optional `REPORT_READ_TOKEN` secret-binding contract locally, but this bundle does not create, provision, or verify a production value. Until a separately approved secret operation and readback prove otherwise, do not claim that production has the new credential. `DISCORD_WEBHOOK_URL` and `PRICE_GUARD_KEY` are not referenced by current source; retaining, rotating, or removing them is a separate secret operation requiring explicit approval. +The 2026-08-26 control-plane inventory found active Worker secret names `ADMIN_TOKEN`, `CF_API_TOKEN`, `CF_ZONE_TAG`, `DISCORD_WEBHOOK_URL`, `IGNORED_IP`, `PRICE_GUARD_KEY`, and `TELEMETRY_RATE_LIMIT_SECRET`; values were not read or exposed. Version 1.30.0 added the optional `REPORT_READ_TOKEN` binding contract. The separately approved CEO read at `2026-08-27T15:08:12.012Z` proves that the deployed report-read path worked at that time, but it does not authorize another request, disclose a value, or prove later secret state. `DISCORD_WEBHOOK_URL` and `PRICE_GUARD_KEY` are not referenced by current source; retaining, rotating, or removing them is a separate secret operation requiring explicit approval. Cloudflare currently displays the historical repository name `True-Good-Craft/buscore-lighthouse`; GitHub redirects it to `True-Good-Craft/lighthouse`. Package metadata now names the canonical repository, but changing the external integration label is a separate control-plane action. @@ -130,7 +133,7 @@ A version upload needs no traffic rollback because it does not intentionally pro 1. Confirm that no `lighthouse-production` workflow run, Workers Builds production job, direct deployment, or rollback is in progress. GitHub concurrency serializes only that workflow; it cannot prevent a direct Wrangler or Workers Builds race. If any production mutation is active or uncertain, stop and wait. 2. With approved Class 1 access, read deployment status/history and record the current version, traffic split, and exact immutable candidate rollback version ID. Never select a rollback target by package label, timestamp guess, or branch name. -3. Obtain explicit owner approval naming that version ID. For the first 1.29.4 production promotion, the audited pre-change candidate is `ba611ac1-653d-47a2-a465-a85f4124b6b6`, but it must still be present and verified before use. +3. Obtain explicit owner approval naming that version ID. For the first 1.31.0 production promotion, the audited pre-change candidate is 1.30.0 version `ab29c0fb-ca9e-4074-a379-18d0943ec02c`, but it must still be present and verified before use. 4. Run `npx wrangler rollback --message ""` only under that approval. Do not add `--yes`; preserve the interactive confirmation boundary. 5. Read `npm run release:status` and verify that the intended version receives the expected traffic. Record the JSON receipt and time. A successful command without the readback is not rollback proof. 6. Reconcile repository source separately. Reverting a deployment does not revert Git, Cloudflare Workers Builds settings, secrets, custom domains, D1 data, or migrations. Version 1.29.4 has no migration, so its release-control rollback requires no D1 rollback. @@ -155,21 +158,24 @@ The pre-merge Workers Builds repair was completed on 2026-08-26: the prior produ `ADMIN_TOKEN` is not a read-only credential. It remains a backward-compatible report credential and is the only credential accepted by the mutating `POST /campaign`, `POST /notes`, and `POST /report/snapshot` routes. `REPORT_READ_TOKEN` grants GET-report authorization only when it is independently generated, cryptographically random, contains 32 to 128 URL-safe ASCII characters (`A-Z`, `a-z`, `0-9`, `_`, and `-`), and differs from `ADMIN_TOKEN`, but it does not make the report implementation zero-write. A malformed report secret disables that path while preserving a distinct admin fallback; identical non-empty admin/read secrets intentionally fail every protected read and write closed before database or deferred work. Possession of either credential does not itself authorize a production request. Do not paste a production value into commands, logs, chat, files, screenshots, or `.env`; `.dev.vars.example` contains non-secret local placeholders and is not read by the diagnostic helper. -### 1.30.0 rollout and rollback boundary +### 1.30.0 deployment receipt and auth rollback boundary -The local governed bundle adds the credential contract and helper but performs no secret generation, provisioning, deployment, production request, or readback. Production remains on the previously verified admin-only report contract until separate evidence proves activation. +The 1.30.0 credential split is deployed as Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` at 100% traffic under deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`. The approved CEO read at `2026-08-27T15:08:12.012Z` validated the production read path without exposing the credential. This receipt does not authorize another request or secret operation. -Roll out in reversible stages, each under its own approval: +Rolling back below Lighthouse 1.30.0 restores the prior admin-only report-read behavior and does not roll back secret state or another repository. A read-token-only client will receive `401` after such a rollback; use the retained, separately authorized admin fallback only as an intentional compatibility path, not an automatic retry. Secret removal or rotation is a separate destructive security operation and is not implied by code rollback. -1. Validate the complete local Code + SOT + CHANGELOG + Version bundle. -2. Independently generate and provision a distinct cryptographically random 32-to-128-character URL-safe-ASCII production `REPORT_READ_TOKEN` through an approved non-echoing mechanism without removing or rotating `ADMIN_TOKEN`; record only secret-name/version metadata and the format/distinctness assertion, never the value. -3. Deploy the approved Worker bundle through the sole authorized production workflow and obtain its deployment receipt. -4. With separate Class 2 endpoint approval, run exactly one `npm run --silent diagnostic:ceo` request and verify the strict CEO `1.1` output without exposing the credential. -5. Migrate individual consumers only through their owning repository's governed change. Keep administrative fallback until each consumer is verified. +Agent Smith is not least-privilege after this Lighthouse-only change: it still needs `LIGHTHOUSE_ADMIN_TOKEN` for `POST /report/snapshot`. Splitting its report reads without first separating that write leaves the broad credential in the same runtime. A snapshot-specific credential or removal of the archive write requires a later owner-approved Agent Smith/Lighthouse contract change before the broad Smith secret can be removed and `ADMIN_TOKEN` rotated. -Rolling back the Lighthouse Worker restores the prior admin-only report-read behavior and does not roll back secret state or another repository. A read-token-only client will receive `401` after such a rollback; use the retained, separately authorized admin fallback only as an intentional compatibility path, not an automatic retry. Secret removal or rotation is a separate destructive security operation and is not implied by code rollback. +### 1.31.0 CEO contract rollout and rollback boundary -Agent Smith is not least-privilege after this Lighthouse-only change: it still needs `LIGHTHOUSE_ADMIN_TOKEN` for `POST /report/snapshot`. Splitting its report reads without first separating that write leaves the broad credential in the same runtime. A snapshot-specific credential or removal of the archive write requires a later owner-approved Agent Smith/Lighthouse contract change before the broad Smith secret can be removed and `ADMIN_TOKEN` rotated. +The local 1.31.0 bundle changes the CEO response contract to `1.2` while leaving metric definitions at `1.1`. It performs no production request, secret operation, migration, upload, deployment, or traffic change. Roll out in reversible order, with each external step under its own approval: + +1. Validate the complete local Code + contracts + SOT + CHANGELOG + Version bundle, including strict schemas/fixtures and helper validation for both CEO `1.1` and `1.2`. +2. **Compatibility deployed; exact parity pending:** Agent Smith `0.26.0` accepts both response versions, retains `1.1`, passed CI/deployment and Discord command registration at merged commit `5519764d959cf1d6a505280814c59e82854f9bda`, and the owner confirmed `/report` works against active CEO `1.1`. The final Lighthouse audit then added one strict `1.2` rejection for non-null inquiry totals when that source is unavailable. Deploy and verify the matching Agent Smith validator patch before continuing to step 3. +3. Only after step 2 is proven, promote the approved Lighthouse 1.31.0 Worker through the sole authorized production workflow and obtain the immutable deployment receipt. +4. With separate Class 2 endpoint approval, run exactly one `npm run --silent diagnostic:ceo` request and verify strict CEO `1.2`, metric-definition version `1.1`, activity-basis source states, scheduled-probe sparse-history states, and aggregate inquiry-record diagnostics without exposing the credential. + +If the 1.31.0 Worker must be rolled back, select and explicitly approve the still-present immutable 1.30.0 version ID, follow the production rollback/receipt sequence above, and expect CEO contract `1.1` again. The dual-version helper and Agent Smith consumer must continue to accept `1.1`; do not roll them back merely because Lighthouse was rolled back. Version 1.31.0 has no migration, secret, route, auth, binding, storage, retention, or schedule change, so those domains require no rollback. Agent Smith is a rollout prerequisite but is not a runtime dependency: Lighthouse remains independently runnable if Smith is unavailable. ## Diagnostic Access Classes @@ -196,7 +202,7 @@ These protected views skip the best-effort Cloudflare traffic refresh and read c | Request | Primary use | |---|---| -| `GET /report?view=ceo` | First diagnostic read; strict CEO `1.1` facts, windows, source state, limitations, failures, and latest service probes | +| `GET /report?view=ceo` | First diagnostic read; strict CEO `1.1` (active 1.30.0/rollback) or `1.2` (1.31.0 target) facts, windows, source state, limitations, failures, and latest service probes; metric definitions remain `1.1` | | `GET /report?view=source_health` | Ingestion integrity by tracked site; not service-probe truth | | `GET /report?view=asset` | Stored Phase 2 rollup, GitHub snapshot, service checks, and campaign evidence | | `GET /report?view=tgc` | Detailed TGC compatibility diagnostics | @@ -204,7 +210,7 @@ These protected views skip the best-effort Cloudflare traffic refresh and read c These are read-mostly, not guaranteed zero-write: if report assembly throws, Lighthouse best-effort increments `metrics_daily.errors` before returning `503 report_unavailable`. -For the CEO row only, the canonical operator mechanism is `npm run --silent diagnostic:ceo`. The helper has a fixed production URL, `Accept: application/json`, and `X-Report-Token`; makes at most one request with no retry or redirect; rejects non-200 responses; times out after 15 seconds; caps the response at 1 MiB; and validates strict CEO contract `1.1` before emitting pretty-printed JSON plus one newline. It accepts a 32-to-128-character URL-safe-ASCII credential through either a hidden TTY prompt or `LIGHTHOUSE_REPORT_READ_TOKEN` supplied by an approved non-echoing automation environment, and removes that environment variable immediately after capture. It accepts no arguments, URL override, token file, report file, or `.env`. Noninteractive failures are nonzero and finite: missing/invalid credentials or HTTP `401`/`403` emit `Lighthouse CEO diagnostic access blocked.`; HTTP `503` emits `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.`; every other transport, response-safety, parse, schema, or output failure emits `Lighthouse CEO diagnostic failed.`. No response body, numeric HTTP status, schema detail, or token reaches output. These controls protect credential handling but do not authorize the request or remove the possible error-counter side effect. +For the CEO row only, the canonical operator mechanism is `npm run --silent diagnostic:ceo`. The helper has a fixed production URL, `Accept: application/json`, and `X-Report-Token`; makes at most one request with no retry or redirect; rejects non-200 responses; times out after 15 seconds; caps the response at 1 MiB; and selects the strict schema by `report_contract_version`, accepting exactly `1.1` or `1.2` with `metric_definition_version: "1.1"` before emitting pretty-printed JSON plus one newline. Cross-version shapes and every other contract version fail closed. It accepts a 32-to-128-character URL-safe-ASCII credential through either a hidden TTY prompt or `LIGHTHOUSE_REPORT_READ_TOKEN` supplied by an approved non-echoing automation environment, and removes that environment variable immediately after capture. It accepts no arguments, URL override, token file, report file, or `.env`. Noninteractive failures are nonzero and finite: missing/invalid credentials or HTTP `401`/`403` emit `Lighthouse CEO diagnostic access blocked.`; HTTP `503` emits `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.`; every other transport, response-safety, parse, schema, or output failure emits `Lighthouse CEO diagnostic failed.`. No response body, numeric HTTP status, schema detail, or token reaches output. These controls protect credential handling but do not authorize the request or remove the possible error-counter side effect. Agent Smith surfaces inherit downstream behavior: @@ -247,10 +253,10 @@ Use this order for a Lighthouse `WATCH`, `ALERT`, unavailable report, or analyti 2. **Classify the layer before probing.** Separate report preparation, Lighthouse facts, scheduled service probes, source ingestion, Agent Smith presentation, Discord delivery, and Cloudflare access. 3. **Read local authority.** Read `AGENTS.md`, `SOT.md`, this runbook, the relevant changelog entry, and the CEO schema/fixture matching the observed state. If `source_health` or supplied evidence identifies a producer, also read that producer's canonical files listed above. 4. **Confirm authorization and access material.** Use only the approved production endpoint and the repository's fixed non-echoing helper with an owner-approved report-read credential mechanism. Agent Smith's Cloudflare runtime binding is not a user-facing diagnostic credential source. Never reveal or copy a credential. If the endpoint, mechanism, authorization, or deployed/provisioned `REPORT_READ_TOKEN` is unavailable, report `ACCESS_BLOCKED` and continue only with local evidence. -5. **Read the CEO view first only when both production access and a safe credential mechanism are approved.** Run exactly `npm run --silent diagnostic:ceo`; do not recreate its request with a broader command, substitute an admin credential, add arguments, redirect output to a file, or retry automatically. On validated success, inspect `report_contract_version`, `metric_definition_version`, `generated_at`, exact windows, `sources`, `details.service_probes`, and `limitations` before interpreting totals. Classify `Lighthouse CEO diagnostic access blocked.` as `ACCESS_BLOCKED`; classify `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.` as report-unavailable application evidence with the stated possible mutation; classify generic `Lighthouse CEO diagnostic failed.` as an unresolved transport/safety/contract failure. None permits response exposure, an alternate endpoint, or an automatic retry. +5. **Read the CEO view first only when both production access and a safe credential mechanism are approved.** Run exactly `npm run --silent diagnostic:ceo`; do not recreate its request with a broader command, substitute an admin credential, add arguments, redirect output to a file, or retry automatically. On validated success, inspect `report_contract_version`, require `metric_definition_version: "1.1"`, then inspect `generated_at`, exact windows, `sources`, `details.service_probes`, `details.voluntary_inquiry_records` when present, and `limitations` before interpreting totals. For contract `1.2`, interpret direct `freshness_basis: "activity"` separately from `service_probes.freshness_basis: "scheduled_probe"`; do not turn activity silence into health or failure. Classify `Lighthouse CEO diagnostic access blocked.` as `ACCESS_BLOCKED`; classify `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.` as report-unavailable application evidence with the stated possible mutation; classify generic `Lighthouse CEO diagnostic failed.` as an unresolved transport/safety/contract failure. None permits response exposure, an alternate endpoint, or an automatic retry. 6. **Narrow to one secondary view only when evidence requires it.** Use `source_health` for producer-ingestion integrity, `asset` for stored probe/GitHub/rollup detail, or `tgc` for the TGC compatibility diagnostic. The fixed CEO helper cannot select these views, so their production access remains `ACCESS_BLOCKED` unless a separately approved endpoint scope and non-echoing mechanism are provided; do not alter the helper or improvise a raw request. Do not fan out across every surface. 7. **Check Agent Smith separately.** Confirm configured mode and active lane. Production is checked in as `ceo_v1`. A Smith `/health` response can show its configuration and CEO readiness; it does not independently prove Discord delivery, Lighthouse cron completion, TGC source freshness, or monthly archive success. -8. **Correlate by timestamp and ownership.** Compare the alert time, CEO `generated_at`, each source's `data_through`, probe `checked_at` values, and the applicable complete or partial window. Do not compare partial today with a complete day as though they were equivalent. +8. **Correlate by timestamp and ownership.** Compare the alert time, CEO `generated_at`, each source's `freshness_basis` and `data_through`, probe `checked_at` values, and the applicable complete or partial window. In contract `1.2`, direct-source `data_through` is an activity watermark—possibly a conservative composite or bounded day-bucket timestamp—not a scheduled heartbeat; only a complete scheduled-probe set supports the 36-hour fresh/stale classification. Do not compare partial today with a complete day as though they were equivalent. 9. **Report diagnosis with confidence and gaps.** State what is proven, what is inferred, what is unavailable, whether any diagnostic action could have changed evidence, and the smallest next check requiring approval. Do not start by calling bare reports, update checks, download redirects, artifact GET/HEAD routes, POST routes, D1 SQL, or deployment commands. @@ -264,9 +270,11 @@ Do not start by calling bare reports, update checks, download redirects, artifac | `product_telemetry` | `buscore_product_events_daily` plus bounded event-ID deduplication | Accepted allowlisted events are aggregated once per event ID; duplicate IDs are acknowledged but not re-counted. The canonical BUS Core client transmits only after local enablement plus disclosure acknowledgement, but this public unauthenticated receiver cannot prove sender provenance or consent. Native posts use `Content-Type: application/json` and `User-Agent: BUS-Core/` as honest transport identity only; the user agent is not authentication, a user/device identifier, or sender-provenance proof, and Lighthouse does not persist it as product identity. No persistent installation identity is stored | | `buscore_site` | `site_events_raw` plus `buscore_download_intent_daily` | Accepted production BUS Core page views and rate-bounded canonical artifact-click interest from its definition boundary | | `tgc_site` | `site_events_raw` for `tgc_site` | Consented, allowlisted TGC site events with bounded sanitized detail | -| `voluntary_inquiries` | Aggregate reads from optional `BUSCORE_LEADS_DB` | Voluntary inquiry totals and fixed privacy-safe attribution buckets; never raw lead data | +| `voluntary_inquiries` | Aggregate reads from optional `BUSCORE_LEADS_DB` | Unique lead-record totals windowed by `created_at`, fixed privacy-safe attribution buckets, and aggregate record diagnostics; never submissions, people, raw lead data, or PII | | `lighthouse_errors` | `metrics_daily.errors` | Best-effort counter updated only by selected route failures: manifest GET, `/update/check`, `/download/latest`, and report assembly. It is not a complete Worker error log: manifest HEAD, artifact-route failures, product-ingest failures, and scheduled-probe failures do not increment it | -| `service_probes` | Latest active rows in `health_checks` | Scheduled liveness evidence for six named surfaces | +| `service_probes` | Latest active rows in `health_checks` | Scheduled liveness evidence for six named surfaces; only a complete target set supports the oldest-target 36-hour fresh/stale rule | + +In CEO contract `1.2`, every direct aggregate row above uses the activity freshness basis. Its `data_through` is an activity watermark, not proof of current producer, transport, or ingestion health. A single-component watermark is its latest trusted observation. A composite watermark is present only when every required component has evidence and uses the conservative earliest required-component watermark. A date-only daily-bucket watermark is normalized to the end of a completed UTC day, or bounded by report generation for the current/future day; it is a reporting bound rather than an exact event timestamp. `service_probes` alone uses the scheduled-probe basis. The two bases are not interchangeable. `GET /report?view=source_health` is ingestion-integrity evidence. It reports recent accepted signals and persisted drop counters by tracked site. It is not the service-probe table and must not be used as proof that the manifest, artifact, lead, GitHub, or site endpoints are currently reachable. @@ -279,7 +287,7 @@ Producer-specific comparisons are not one-to-one: - BUS Core site emission is suppressed by `dev_mode` or `localStorage.noAnalytics === "1"` and deduplicates same-path page views within three seconds. It has no producer-side origin guard or `test_mode` label, so an unsuppressed local/staged page can attempt the production endpoint even though Lighthouse may reject it. - TGC emission requires current optional-analytics consent, honors GPC/DNT and `dev_mode`, refuses non-production origins, and can label `test_mode` traffic for report exclusion. A quiet `tgc_site` source can therefore reflect consent/suppression rather than emitter or Lighthouse failure. - BUS Core's primary `/download/latest` CTA intentionally emits no `download_click`; only exact versioned Lighthouse artifact links qualify. Website click evidence and artifact delivery should not be expected to match. -- Early-access success may emit `early_access_submit_success`, but suppression can prevent it. Managed BUS success emits no dedicated Lighthouse success event. BUS Core lead rows are unique by email and shared across both forms, so row totals are not submission totals. +- Early-access success may emit `early_access_submit_success`, but suppression can prevent it. Managed BUS success emits no dedicated Lighthouse success event. CEO inquiry totals count unique lead records by `created_at`; a later legitimate submission may update an existing email's record, but it is not counted as a new inquiry record and does not move that record into a later created-at window. `last_updated_at` is an aggregate diagnostic only. Row totals are not submissions or people. - A TGC intake endpoint failure emits both `form_submit_failure` and `form_submit_fallback` for one attempt; do not sum them as two failed inquiries. The current TGC form reports category `contact` even when a service is preselected. - A true return from `navigator.sendBeacon()` proves only browser queue acceptance. Neither it nor a swallowed/fail-soft fetch result proves Lighthouse HTTP acceptance or persistence. @@ -298,6 +306,8 @@ The current scheduled probe set is: The Lighthouse daily cron is `5 0 * * *` (00:05 UTC). Agent Smith's checked-in delivery crons are 13:00 UTC daily, 14:00 UTC on Mondays, and 15:00 UTC on the first of each month. These are separate jobs: correlate their timestamps, and do not treat a missing Smith post as proof that the earlier Lighthouse cron failed. Probe rows are bounded to about 90 days. Each Lighthouse probe and scheduled writer is fail-soft; one failure does not prove that the rest of the run failed. +CEO contract `1.2` separates query availability from history completeness. A successful query with zero active-target rows is source `available`, freshness `unknown`, reason `probe_history_missing`, `data_through: null`, and empty probe details. A successful query with fewer than all six active targets is source `available`, freshness `unknown`, reason `probe_history_incomplete`, preserves the observed details, and uses the latest observed `checked_at` as `data_through`. Query failure alone is `unavailable` and makes probe details `null`. When all six targets are present, the oldest target timestamp is `data_through`; no older than 36 hours is `fresh`, and older than 36 hours is `stale`/`probe_data_stale`. These response semantics do not change probe execution or create a canary. + These are BUS Core-oriented liveness probes. They do not probe `truegoodcraft.ca`, either browser emitter, Cloudflare Web Analytics injection, TGC `/api/intake`, Managed BUS intake, or browser-to-Lighthouse delivery. A passing manifest HEAD proves route/object metadata only, not valid manifest JSON, required fields, CORS, or browser hydration. A passing early-access GET/405 proves only that the route/method boundary responded; it does not exercise Turnstile, KV rate control, D1 lead storage, production POST success, Managed BUS, or TGC intake. ## Vocabulary and Interpretation @@ -314,8 +324,14 @@ These are BUS Core-oriented liveness probes. They do not probe `truegoodcraft.ca | `ACCESS_BLOCKED` | The diagnostic could not reach an approved endpoint/control plane or lacked approved credentials. This is an operator-access result, not a service-health result. | | Source `available` | The source query/binding succeeded. Zero may be a real observed zero only when the metric and window support it. | | Source `unavailable` | The source could not be queried or its binding is absent. Dependent values must be `null`, not zero. | -| Freshness `unknown` / `source_history_missing` | No trustworthy watermark exists yet. This is not the same as a failed current endpoint. | -| Freshness `stale` / `source_data_stale` | The latest stored evidence is older than the contract threshold. It may indicate an emitter, ingestion, schedule, or access issue; narrow the layer. | +| `freshness_basis: "activity"` (`1.2`) | Direct aggregate source state. `data_through` is a single-source or conservative composite activity watermark; a date-only bucket becomes a bounded reporting timestamp. It is not a heartbeat, exact event time, completeness proof, or producer-health result. | +| `unknown` / `activity_only` (`1.2`) | The direct query succeeded and has at least one observation. Activity age alone cannot classify the source fresh or stale. | +| `unknown` / `source_history_missing` | The direct query succeeded but no trustworthy observation watermark exists. This is not a failed current endpoint. | +| `freshness_basis: "scheduled_probe"` (`1.2`) | Scheduled six-target probe state. Only this basis can be fresh/stale under the 36-hour rule. | +| `unknown` / `probe_history_missing` (`1.2`) | The probe query succeeded with zero active-target rows; source is available and details are empty. | +| `unknown` / `probe_history_incomplete` (`1.2`) | The probe query succeeded with only part of the active target set; observed rows remain visible, but no whole-set freshness claim is allowed. | +| `stale` / `probe_data_stale` | All six scheduled targets are present and the oldest required target is more than 36 hours old. Narrow the exact target before inferring impact. | +| `stale` / `source_data_stale` (`1.1` only) | Historical rollback-contract direct-source classification. Contract `1.2` replaces it with activity-only/unknown; never apply this 1.1 label to a 1.2 direct source. | | Coverage `partial` | The source does not establish complete coverage for the whole requested window, often because it is sparse or began later. Partial coverage is expected for multiple current sources and is not automatically an error. | Additional failure boundaries: @@ -336,7 +352,7 @@ Every diagnosis should state: - Agent Smith mode/lane if relevant. - Lighthouse HTTP and contract versions if read. - Exact report windows and whether each is complete. -- Source availability, freshness, coverage, `data_through`, and reason code. +- Source availability, `freshness_basis`, freshness, coverage, `data_through`, and reason code; identify whether the response is contract `1.1` or `1.2` before interpreting those fields. - Latest named service-probe states and `checked_at` times. - Proven impact, likely layer, and alternative explanations. - Whether any diagnostic request could have changed evidence. @@ -346,12 +362,13 @@ Never report a WATCH as an outage, an access failure as application failure, a s ## Known Gaps — Do Not Work Around -- Version 1.30.0 defines a least-privilege report-read credential and fixed non-echoing CEO helper locally, but no production `REPORT_READ_TOKEN` value, deployment, or verification receipt exists in this bundle. Until separately approved external activation is proven, production read-token access is `ACCESS_BLOCKED`. +- Lighthouse 1.31.0 and CEO contract `1.2` are local until an explicitly approved production promotion produces a new deployment receipt. Active production remains 1.30.0/CEO `1.1`; do not diagnose the absence of 1.2 fields there as drift. +- Agent Smith `0.26.0` proves deployed dual-version compatibility and CEO `1.1` rollback operation, but its pinned `1.2` validator predates the final unavailable-inquiry-count rejection. It accepts every valid Lighthouse output, yet exact strict-schema parity remains pending. Retain `1.1`, do not weaken Lighthouse validation or remove the rollback schema, and report `BLOCKED BEFORE LIGHTHOUSE 1.31 PROMOTION` until the matching Agent Smith patch is deployed and verified. - The helper safely consumes a credential but is not a credential-retrieval system. There is still no owner-approved operator secret-retrieval mechanism; do not copy a value from Agent Smith, Cloudflare displays, shell history, files, or another service. - Agent Smith still holds the broad `LIGHTHOUSE_ADMIN_TOKEN` because it archives monthly snapshots. Full cross-service least privilege remains blocked on a later snapshot-specific credential split or removal of that write. - The active `lighthouse.buscore.ca` attachment is an externally managed Production Custom Domain rather than checked-in route configuration. It was verified on 2026-08-26, but future attachment changes require another control-plane read. - Cloudflare Workers Builds commands remain externally managed rather than checked into this repository. Durable readback on 2026-08-26 verified both production Deploy and Version commands as `npx wrangler versions upload`. If that setting becomes inaccessible or differs, report `BLOCKED BEFORE MERGE` until it is reconciled. -- The checked-in manual deployment workflow is the sole authorized production path. The availability and scope of its GitHub `CLOUDFLARE_API_TOKEN` have not been proven by this local bundle. +- The checked-in manual deployment workflow is the sole authorized production path. Successful release run `33086080869` proved its configured GitHub `CLOUDFLARE_API_TOKEN` could perform the approved 1.30.0 deployment at that time; current availability or broader scope must not be inferred from that historical success. - Persistent Worker logs and traces are disabled, limiting retained runtime diagnostic evidence. - Unused legacy secret names `DISCORD_WEBHOOK_URL` and `PRICE_GUARD_KEY` remain attached. Do not inspect, rotate, or remove them without separate approval. - Cloudflare displays the historical redirecting repository name `True-Good-Craft/buscore-lighthouse`; package metadata now points to canonical `True-Good-Craft/lighthouse`, but the external label remains unreconciled. diff --git a/README.md b/README.md index f08ca46..23d9957 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,26 @@ # buscore-lighthouse +## 1.31.0 CEO activity truth and sparse probe health + +Version 1.31.0 advances the strict CEO response contract to `1.2` while keeping `metric_definition_version: "1.1"`. Direct aggregate sources now declare `freshness_basis: "activity"`: `data_through` is an activity watermark, freshness stays `unknown`, and `activity_only` prevents a quiet sparse source from being presented as a fresh, stale, healthy, or failed scheduled feed. A single-component watermark is its latest trusted observation; a composite watermark is the conservative earliest required-component watermark. A day-bucket watermark is normalized to a bounded reporting timestamp and is not an event timestamp. Only `service_probes` uses `freshness_basis: "scheduled_probe"` and the existing 36-hour scheduled fresh/stale rule. + +Sparse probe history is no longer converted into source unavailability. A successful probe query with no rows is available/unknown with `probe_history_missing` and empty details; a partial active-target set is available/unknown with `probe_history_incomplete`, retains the observed rows, and reports the latest observed timestamp. Query failure alone is unavailable. A complete six-target set still uses the oldest target timestamp. Exact latest-timestamp ties resolve deterministically with failure before success and a stable row-ID tie-break; no scheduled check, request method, ordinary pass/fail rule, canary, cadence, or storage changes. + +Voluntary-inquiry windows count unique lead records by `created_at`; a later update is not counted as a new inquiry record and does not move that record into a later created-at window. `details.voluntary_inquiry_records` exposes only `total_records`, `last_created_at`, and `last_updated_at` as aggregate diagnostics, never lead PII or record identifiers. The strict schema rejects non-null inquiry totals when the source is unavailable. No D1 migration is required. Routes, auth, headers, CORS, bindings, configuration, retention, ingestion, schedules, and metric definitions are unchanged. + +The diagnostic helper strictly accepts both CEO `1.1` and `1.2` with metric-definition version `1.1`, so the deployed `1.30.0` Worker remains readable after rollback. Deployed Agent Smith `0.26.0` at merged `main` commit `5519764d959cf1d6a505280814c59e82854f9bda` proved dual-version compatibility: its CI, Cloudflare deployment, and Discord command registration succeeded, and the owner confirmed `/report` works against active CEO `1.1`. Agent Smith retains `1.1` acceptance and accepts every valid `1.2` producer output, but its pinned validator predates the final unavailable-inquiry-count rejection; exact validator parity requires a matching Agent Smith patch before Lighthouse promotion. Active production remains Lighthouse `1.30.0`, Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` at 100% under deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`, from merged `main` commit `4e80d65de01606c15100a99e7852ce19c5e6cd98`; the 1.31.0 bundle itself performs no production promotion. + ## 1.30.0 least-privilege report diagnostics -Repository version 1.30.0 adds an optional report-read credential to the local governed bundle while preserving the existing administrative contract. An independently generated cryptographically random `REPORT_READ_TOKEN` containing 32 to 128 URL-safe ASCII characters and differing from `ADMIN_TOKEN` is accepted only through `X-Report-Token` for `GET /report`; `X-Admin-Token`/`ADMIN_TOKEN` remains a backward-compatible report credential and remains the only credential accepted by `POST /campaign`, `POST /notes`, and `POST /report/snapshot`. An identical non-empty admin/read-secret configuration fails every protected read and write closed before database or deferred work. The new read token changes authorization scope only: report views, response payloads, status semantics, schemas, D1 data, retention, schedules, and metrics are unchanged. +Version 1.30.0 added an optional report-read credential while preserving the existing administrative contract. An independently generated cryptographically random `REPORT_READ_TOKEN` containing 32 to 128 URL-safe ASCII characters and differing from `ADMIN_TOKEN` is accepted only through `X-Report-Token` for `GET /report`; `X-Admin-Token`/`ADMIN_TOKEN` remains a backward-compatible report credential and remains the only credential accepted by `POST /campaign`, `POST /notes`, and `POST /report/snapshot`. An identical non-empty admin/read-secret configuration fails every protected read and write closed before database or deferred work. The read token changes authorization scope only: report views, response payloads, status semantics, schemas, D1 data, retention, schedules, and metrics were unchanged in 1.30.0. The fixed `npm run --silent diagnostic:ceo` helper makes at most one non-echoing request to the canonical CEO view, validates the strict CEO `1.1` response, and prints pretty-printed JSON only after validation. It has no URL override, arguments, redirect following, retry, user-supplied credential/report file, or `.env` input. Its finite static failures distinguish access blockage, HTTP `503` report unavailability with a possible `metrics_daily.errors` increment, and an otherwise unclassified diagnostic failure without printing response bodies or dynamic details. Production use still requires explicit access approval and is read-mostly rather than zero-write. -This local bundle creates or exposes no secret value and performs no secret provisioning, production request, upload, deployment, migration, or active-traffic change. Production activation requires separately approved secret provisioning, deployment, and readback. Agent Smith remains on its broad `LIGHTHOUSE_ADMIN_TOKEN` until a later snapshot-specific authorization split allows its monthly archive write to be separated safely. +The local 1.30.0 work created or exposed no secret value and performed no secret provisioning, production request, upload, deployment, migration, or active-traffic change. It was later promoted through successful release workflow run `33086080869`; the separately approved read-only receipt and CEO read at `2026-08-27T15:08:12.012Z` are recorded in `OPERATIONS.md`. Agent Smith remains on its broad `LIGHTHOUSE_ADMIN_TOKEN` until a later snapshot-specific authorization split allows its monthly archive write to be separated safely. ## 1.29.4 release-control reconciliation -Repository version 1.29.4 records the verified 1.29.3 production promotion and repairs repository-controlled release authority. Active production is Worker version `ba611ac1-653d-47a2-a465-a85f4124b6b6`, promoted from merged `main` commit `59231d09084d0fa4db71012b6f29550886c5b605` by Cloudflare build `793715ef-6123-4d98-a4a7-797634d07812`. The canonical production hostname is `lighthouse.buscore.ca`, attached as a Production Custom Domain rather than a Worker Route. The additional public `workers.dev` surface and branch/version previews are not canonical diagnostic endpoints. The cron remains `5 0 * * *`; CEO report and metric-definition contract remain `1.1`. +Repository version 1.29.4 recorded the then-active 1.29.3 production promotion and repaired repository-controlled release authority. That audit found Worker version `ba611ac1-653d-47a2-a465-a85f4124b6b6`, promoted from merged `main` commit `59231d09084d0fa4db71012b6f29550886c5b605` by Cloudflare build `793715ef-6123-4d98-a4a7-797634d07812`. The canonical production hostname is `lighthouse.buscore.ca`, attached as a Production Custom Domain rather than a Worker Route. The additional public `workers.dev` surface and branch/version previews are not canonical diagnostic endpoints. The cron remains `5 0 * * *`; CEO metric-definition contract remains `1.1`. The repository now pins the verified Cloudflare account, uses the primary D1 control-plane name `lighthouse` without changing its database ID, provides explicit version-upload, status, and history commands, and deliberately provides no direct production-deploy script. The checked-in production workflow is manual-only, main-only, serialized, fully validated, strict, variable-preserving, and receipted. No Worker runtime contract, endpoint, auth, schema, retention, schedule, metric, secret, or active deployment changes in this local bundle. @@ -28,7 +38,7 @@ Use [`OPERATIONS.md`](OPERATIONS.md) as the canonical runbook for Lighthouse ale npm run --silent diagnostic:ceo ``` -Automation may supply a 32-to-128-character URL-safe-ASCII `LIGHTHOUSE_REPORT_READ_TOKEN` through an approved non-echoing environment mechanism. Never place the value in command arguments, files, `.env`, logs, chat, or screenshots. The helper removes the variable from its process after capture, does not grant authorization, and must not be run merely because the command exists. `PHASE2_ANALYTICS_NOTES.md` and `PHASE3_ANALYTICS_NOTES.md` are historical implementation records, not current runbooks. +Automation may supply a 32-to-128-character URL-safe-ASCII `LIGHTHOUSE_REPORT_READ_TOKEN` through an approved non-echoing environment mechanism. Never place the value in command arguments, files, `.env`, logs, chat, or screenshots. The helper removes the variable from its process after capture, strictly validates CEO contract `1.1` or `1.2` with metric-definition version `1.1`, does not grant authorization, and must not be run merely because the command exists. `PHASE2_ANALYTICS_NOTES.md` and `PHASE3_ANALYTICS_NOTES.md` are historical implementation records, not current runbooks. ## 1.29.2 service-probe truth repair @@ -42,19 +52,19 @@ Worker 1.29.1 was deployed on `2026-08-09T16:41:20.004814Z` as Cloudflare Versio ## CEO decision report -Version 1.28.0 introduced authenticated `GET /report?view=ceo`; contract 1.0 was deployed with Worker 1.29.0, and strict contract and metric-definition version 1.1 are deployed with Worker 1.29.1. It reports exact UTC windows, source availability/freshness/coverage, literal BUS Core discovery/distribution/product/reliability facts, consented TGC page views, and voluntary inquiry aggregates. Query failures are `null` and unavailable, never zero; a successful source with no observation history is explicitly `unknown`, not believable fresh zero. The contract and aggregate-only fixtures live in `contracts/ceo-v1/`. +Version 1.28.0 introduced authenticated `GET /report?view=ceo`; contract 1.0 was deployed with Worker 1.29.0, contract and metric-definition version 1.1 were deployed with Worker 1.29.1 and remain the active 1.30.0 production response, and repository version 1.31.0 advances the response contract to `1.2` without changing metric-definition version `1.1`. It reports exact UTC windows, literal BUS Core discovery/distribution/product/reliability facts, consented TGC page views, voluntary-inquiry lead-record aggregates, and explicit source-state semantics. Query failures are `null` and unavailable, while sparse direct-source silence remains activity-only/unknown rather than becoming false health evidence. Strict current and rollback schemas plus aggregate-only fixtures live in `contracts/ceo-v1/`. -The CEO lane changes no existing report view or database table. It uses the current UTC day only for explicit partial activity, uses completed UTC days for daily and weekly decisions, and never calls artifact responses or daily source credits people, installations, completed downloads, or adoption. Agent Smith owns the final status and wording. +The CEO lane changes no existing report view or database table. It uses the current UTC day only for explicit partial activity, uses completed UTC days for daily and weekly decisions, and never calls artifact responses, daily source credits, or lead records people, installations, completed downloads, or adoption. Agent Smith owns the final status and wording and must consume both `1.1` and `1.2` before the Lighthouse contract is promoted. ## BUS Core minimal product telemetry -Worker 1.29.0 introduced the retained 1.27.0 exact event-ID acknowledgement contract with bounded deduplication and aggregate-only product reporting; verified active Worker 1.29.3 retains that behavior. Current BUS Core product events contain no persistent installation identifier and are limited to first launch, locally deduplicated release/first-success milestones, startup/manual update checks, staged updates, and reliability. Module opens, active days, returning-installation measures, engagement, sessions, retention, and cross-day profiles are prohibited. +Worker 1.29.0 introduced the retained 1.27.0 exact event-ID acknowledgement contract with bounded deduplication and aggregate-only product reporting; verified active Worker 1.30.0 retains that behavior. Current BUS Core product events contain no persistent installation identifier and are limited to first launch, locally deduplicated release/first-success milestones, startup/manual update checks, staged updates, and reliability. Module opens, active days, returning-installation measures, engagement, sessions, retention, and cross-day profiles are prohibited. Migration `0015_minimize_buscore_product_telemetry.sql` was applied before the 2026-07-24 Worker deployment. Product telemetry retains event-ID deduplication keys for 30 UTC-day buckets, aggregate counters for 400 UTC-day buckets, and rate-control buckets for two days. It retains no raw product-event history. ## TGC website analytics -Worker 1.29.0 introduced the narrowed consented `site_key=tgc_site` lane; verified active Worker 1.29.3 retains it. Lighthouse is the raw-event and aggregate-report source of truth; the protected operator view remains `GET /report?view=tgc` using the existing `X-Admin-Token` contract. +Worker 1.29.0 introduced the narrowed consented `site_key=tgc_site` lane; verified active Worker 1.30.0 retains it. Lighthouse is the raw-event and aggregate-report source of truth; the protected operator view remains `GET /report?view=tgc` using the existing report-auth contract. The server accepts page views, selected commercial/contact/outbound interest, form start/attempt/outcome, and sanitized errors. Version 1.29.1 accepts a producer-supplied coarse viewport label or, for rolling compatibility, exact lowercase `WIDTHxHEIGHT`; exact dimensions are immediately normalized by width to `small` below 768, `medium` from 768 through 1199, or `large` from 1200 upward, and only that bucket is stored. Event-specific value sanitization turns recognized form, error, and outbound values into bounded categories, unrecognized non-empty values into `other`, and discards absent/blank values or values on the remaining TGC events. Lighthouse discards visitor/session fields and rejects the superseded lifecycle, field-level form, scroll/engagement/section, and first-party web-vital event families. The existing TGC report shape remains available for rollback and historical rows; Smith's CEO lane uses page views and voluntary inquiries only. Raw TGC events are retained for 90 days; other standardized-site raw events retain the general 30-day policy, and rotating keyed rate identifiers are retained for two days. See `TGC_SITE_ANALYTICS_POLICY.md` for the current boundaries. @@ -280,11 +290,15 @@ Notes: ## Report Response -### CEO V1.1 +### CEO V1.2 with V1.1 rollback + +`GET /report?view=ceo` is the preferred decision-report input. `contracts/ceo-v1/report.schema.json` is strict contract `1.2`; `contracts/ceo-v1/report-1.1.schema.json` pins strict deployed/rollback contract `1.1`. Both require `metric_definition_version: "1.1"`. The window keys remain `today`, `latest_complete_day`, `last_7_complete_days`, `previous_7_complete_days`, and `last_30_complete_days`. Every metric carries those same keys and is either a finite aggregate or `null` when its source is unavailable. + +Contract `1.2` adds `freshness_basis` without changing metric definitions. Direct sparse event/counter sources use `activity`, remain freshness `unknown`, use `activity_only` when an activity watermark exists, and expose `data_through` only as that watermark. For composite sources it is the earliest required-component watermark, and a day-bucket value is a reporting bound rather than an exact event time. No-history direct sources use `source_history_missing`; failed sources are unavailable. `service_probes` alone uses `scheduled_probe`: no history and partial target history remain available/unknown with truthful empty or observed details, while a full six-target set uses the oldest target's 36-hour fresh/stale rule. Current sparse windows remain partial because an activity watermark is not a completeness ledger. -`GET /report?view=ceo` is the preferred decision-report input. Its strict schema is `contracts/ceo-v1/report.schema.json`. The window keys are `today`, `latest_complete_day`, `last_7_complete_days`, `previous_7_complete_days`, and `last_30_complete_days`. Every metric carries those same keys and is either a finite aggregate or `null` when its source is unavailable. +Trusted artifact-click interest still begins on `2026-08-10`: earlier intent rows are excluded from sums and the watermark, wholly earlier windows are `null`, and spanning or later windows contain partial totals from the boundary forward. Voluntary-inquiry windows count unique lead records by `created_at`; `details.voluntary_inquiry_records` adds only `total_records`, `last_created_at`, and `last_updated_at`, and a later update neither adds another inquiry-record count nor moves the record into a later created-at window. Attribution uses only 14 fixed privacy-safe buckets and never emits a raw label. When the source is unavailable, inquiry attribution and the record diagnostic are `null`. -Source state distinguishes `available` from `unavailable`, `fresh` from `stale` or `unknown`, and `full`, `partial`, or `unavailable` coverage. The contract retains `full` for a future source with explicit completeness proof. Current sparse event/counter sources do not prove that every day in a decision window was observable, so available current windows remain partial; they do not claim full coverage from a recent watermark alone. Trusted artifact-click interest begins on `2026-08-10`: earlier intent rows are excluded from sums and the watermark, wholly earlier windows are `null`, and spanning or later windows contain partial totals from the boundary forward. The `buscore_site` source definition reflects that boundary and the limitations explicitly state that pre-definition history is excluded. A successful aggregate query can return numeric zero, but without an all-history watermark its source is `unknown` with `source_history_missing`; an old watermark is `stale` with `source_data_stale`. When a source is unavailable, dependent inquiry-attribution, product-version/failure, or service-probe detail is `null`, not an empty list. Voluntary-inquiry attribution uses only 14 fixed privacy-safe buckets and never emits a raw label. The endpoint is independently guarded per source, aggregate-only, protected by the current dual report-credential contract, and contains no PII or persistent identifiers. Its configured-leads path uses nine D1 statements in batches of at most three; client-supplied app versions are SQL-ranked and limited to ten before reaching Worker memory. Strict Ajv 2020 tests validate all fixtures plus representative live producer outputs. Existing views remain available for diagnostics and rollback. +The endpoint is independently guarded per source, aggregate-only, protected by the dual report-credential contract, and contains no PII or persistent identifiers. Its configured-leads path remains within nine D1 statements in batches of at most three; client-supplied app versions are SQL-ranked and limited to ten before reaching Worker memory. Strict Ajv 2020 tests validate both contract versions, fixtures, and representative producer outputs. Existing views remain available for diagnostics and rollback. The current protected report families are bare legacy `/report`, `view=fleet`, `view=site`, `view=tgc`, `view=source_health`, `view=asset`, `view=monthly`, and `view=ceo`. `view=site` requires a registered `site_key`; the specialized views retain their existing required parameters and response contracts. Explicit `view=legacy` is invalid; omit `view` for the legacy contract. @@ -817,7 +831,7 @@ Configure that environment's returned database ID explicitly. The production `DB ### 4. Apply migrations -Local migration uses the stable binding name and cannot reach remote D1. A new remote environment must use its explicit configuration. The checked-in configuration targets production, so its remote command requires separate production-migration approval. Version 1.30.0 has no migration. +Local migration uses the stable binding name and cannot reach remote D1. A new remote environment must use its explicit configuration. The checked-in configuration targets production, so its remote command requires separate production-migration approval. Versions 1.30.0 and 1.31.0 have no migration. ```bash # local (for wrangler dev) diff --git a/SOT.md b/SOT.md index ac2206a..b8de56e 100644 --- a/SOT.md +++ b/SOT.md @@ -1,14 +1,28 @@ # Lighthouse — Source of Truth +## CEO activity truth and sparse probe health — v1.31.0 + +Version 1.31.0 advances only the CEO response contract from `1.1` to `1.2`; `metric_definition_version` remains `1.1`. The change prevents sparse activity from being presented as a scheduled health signal, makes partial scheduled-probe evidence truthful, and clarifies that voluntary-inquiry totals count unique lead records. The authenticated route remains `GET /report?view=ceo`. No other report view, route, auth rule, header, CORS permission, Worker binding, environment variable, D1 table, retention rule, cron, probe target, canary, or producer contract changes. + +Every CEO source state in contract `1.2` includes `freshness_basis`. Direct aggregate sources (`artifact_delivery`, `update_checks`, `product_telemetry`, `buscore_site`, `tgc_site`, `voluntary_inquiries`, and `lighthouse_errors`) use `freshness_basis: "activity"`. A successful direct-source query with activity evidence reports `freshness: "unknown"`, `reason_code: "activity_only"`, and `data_through` as its activity watermark. For a single-component source, the watermark is the latest trusted observation. For a composite source, it is present only when every required component has a watermark and uses the conservative earliest component watermark. A date-only daily-bucket watermark is normalized to the end of a completed UTC day, or bounded by `generated_at` for the current/future UTC day; it is a reporting bound, not an exact event timestamp. A successful query with no usable source watermark reports `freshness: "unknown"`, `reason_code: "source_history_missing"`, and `data_through: null`. Silence and an old activity watermark do not establish either pipeline freshness or failure, so direct sources no longer use `fresh`/`stale` or `source_data_stale`. Their existing availability, windows, metric definitions, nullable failure behavior, and partial-coverage rules remain unchanged. + +Only `service_probes` uses `freshness_basis: "scheduled_probe"`. A successful query with no active-target history is `available`/`unknown` with `probe_history_missing`, `data_through: null`, and an empty `details.service_probes` array. A successful query with only part of the six-target active set is `available`/`unknown` with `probe_history_incomplete`; it preserves the observed detail rows and uses the latest observed `checked_at` as `data_through`. A complete six-target set retains the scheduled-health rule: the oldest required target watermark is the source `data_through`, and it is `fresh` when no older than 36 hours or `stale` with `probe_data_stale` when older. When one target has multiple rows at the exact latest `checked_at`, failure wins over success; the row ID supplies a stable final tie-break within the same state. Query failure alone makes the source unavailable and the dependent detail `null`. This is report truth repair only; the six scheduled checks, their request methods, ordinary pass/fail boundaries, cadence, storage, and fail-soft execution do not change. + +Voluntary-inquiry window totals count unique lead records by each record's `created_at`. A later update to an existing record is not counted as a new inquiry record and does not move that record into a later created-at window. When `BUSCORE_LEADS_DB` is available, `details.voluntary_inquiry_records` adds the aggregate-only diagnostics `total_records`, `last_created_at`, and `last_updated_at`; `last_updated_at` is modification-recency evidence only. When that binding/query is unavailable, the inquiry-dependent values and diagnostic remain `null` without affecting unrelated sources. No email, form content, raw attribution, record identifier, or other lead PII is selected or returned, and no D1 migration is required. + +`contracts/ceo-v1/report.schema.json` is the strict `1.2` authority, while the pinned strict `1.1` schema remains available for rollback compatibility. The fixed `npm run --silent diagnostic:ceo` helper selects and strictly validates either contract `1.1` or `1.2`, while requiring metric-definition version `1.1` for both; it rejects any other version or cross-version shape before output. Contract `1.2` also rejects any non-null voluntary-inquiry window count when the voluntary-inquiry source is unavailable. Agent Smith must accept both strict contracts before Lighthouse `1.2` is promoted, and must retain `1.1` acceptance while Worker `1.30.0` remains the rollback candidate. This consumer-first ordering is a deployment safety gate, not a hard Lighthouse runtime dependency. Agent Smith `0.26.0` at merged `main` commit `5519764d959cf1d6a505280814c59e82854f9bda` established deployed dual-version compatibility: CI run `33105121602` and deployment run `33105121605` succeeded, including Discord command registration, and the owner confirmed the deployed `/report` path works against active CEO `1.1`. The final Lighthouse audit added the unavailable-source inquiry-count rejection after that deployment. The deployed consumer accepts every valid Lighthouse `1.2` output, but its pinned validator remains a permissive superset until a matching governed Agent Smith patch is deployed and verified. Lighthouse 1.31.0 promotion remains blocked on that exact-validator parity step. + +The local 1.31.0 work performs no migration, secret operation, production request, upload, deployment, or traffic change. The separately approved 2026-08-27 production receipt remains Lighthouse `1.30.0`, Worker version `ab29c0fb-ca9e-4074-a379-18d0943ec02c` at 100% traffic, deployment `526f9fbc-6432-466c-9b39-2ae90b299cae`, from merged `main` commit `4e80d65de01606c15100a99e7852ce19c5e6cd98`. Production continues to emit CEO contract `1.1` until an explicitly approved 1.31.0 promotion. + ## Least-privilege report diagnostics — v1.30.0 Version 1.30.0 adds an optional, additive report-read credential without removing the established administrative contract. A usable `REPORT_READ_TOKEN` Worker secret is an independently generated cryptographically random string of 32 to 128 URL-safe ASCII characters (`A-Z`, `a-z`, `0-9`, `_`, and `-`), is distinct from `ADMIN_TOKEN`, and is accepted for any `GET /report` view only through an exact `X-Report-Token` match. Existing callers may continue to authenticate report reads through the exact `X-Admin-Token`/`ADMIN_TOKEN` match. `POST /campaign`, `POST /notes`, and `POST /report/snapshot` remain administrative writes and accept only `X-Admin-Token`; the report-read header never authorizes them. A missing, blank, malformed, or incorrect report credential fails closed with the existing `401 {"ok":false,"error":"unauthorized"}` response while a distinct administrative fallback remains usable. If the two configured secrets are identical and non-empty, every protected report read and administrative write fails closed with that same `401` before database or deferred work; this prevents a provisioning collision from silently granting write authority to the nominal read credential. Report routes, views, payloads, schemas, status semantics, storage, retention, and scheduling are otherwise unchanged. `REPORT_READ_TOKEN` is an authorization-scope boundary, not a promise of zero evidence mutation. Bare `/report`, `view=fleet`, and `view=site` retain their best-effort previous-completed-day Cloudflare traffic capture/upsert. The stored-data `view=ceo`, `view=tgc`, `view=source_health`, `view=asset`, and `view=monthly` paths continue to skip that refresh, but any report-assembly exception may still best-effort increment `metrics_daily.errors`. Production access therefore still requires explicit approval, and the canonical first diagnostic remains the stored-data CEO view. -The repository provides one fixed, non-echoing operator helper: `npm run --silent diagnostic:ceo`. With a credential satisfying the same 32-to-128 URL-safe-ASCII contract, it performs at most one `GET https://lighthouse.buscore.ca/report?view=ceo` request with `Accept: application/json` and `X-Report-Token`. It accepts no URL or command-line override, retry, redirect, user-supplied credential/report file, or `.env` input; uses a hidden interactive prompt or the `LIGHTHOUSE_REPORT_READ_TOKEN` automation environment variable; removes that variable from the helper process environment immediately after capture; enforces a 15-second timeout and 1 MiB response limit; rejects redirects and non-200 responses; and writes pretty-printed JSON plus one newline to stdout only after fatal UTF-8 decoding, JSON parsing, reflected-token suppression, and validation against the existing strict CEO contract `1.1`. Noninteractive failures are nonzero and use only three finite static lines: missing/invalid credentials and HTTP `401`/`403` emit `Lighthouse CEO diagnostic access blocked.`; HTTP `503` emits `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.`; every other transport, response-safety, parse, schema, or output failure emits `Lighthouse CEO diagnostic failed.`. The helper never emits the credential, response body, numeric HTTP status, or schema detail. It does not grant production authorization and cannot make a CEO request zero-write when report assembly fails. +The 1.30.0 bundle introduced one fixed, non-echoing operator helper: `npm run --silent diagnostic:ceo`. With a credential satisfying the same 32-to-128 URL-safe-ASCII contract, it performs at most one `GET https://lighthouse.buscore.ca/report?view=ceo` request with `Accept: application/json` and `X-Report-Token`. It accepts no URL or command-line override, retry, redirect, user-supplied credential/report file, or `.env` input; uses a hidden interactive prompt or the `LIGHTHOUSE_REPORT_READ_TOKEN` automation environment variable; removes that variable from the helper process environment immediately after capture; enforces a 15-second timeout and 1 MiB response limit; rejects redirects and non-200 responses; and writes pretty-printed JSON plus one newline to stdout only after fatal UTF-8 decoding, JSON parsing, reflected-token suppression, and strict CEO-contract validation. Version 1.30.0 accepted contract `1.1`; the 1.31.0 section above governs the current dual `1.1`/`1.2` validator dispatch. Noninteractive failures are nonzero and use only three finite static lines: missing/invalid credentials and HTTP `401`/`403` emit `Lighthouse CEO diagnostic access blocked.`; HTTP `503` emits `Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.`; every other transport, response-safety, parse, schema, or output failure emits `Lighthouse CEO diagnostic failed.`. The helper never emits the credential, response body, numeric HTTP status, or schema detail. It does not grant production authorization and cannot make a CEO request zero-write when report assembly fails. -This local governed bundle changes behavior, auth, configuration, and operator workflow, and documents the remaining cross-service boundary. It adds the optional `REPORT_READ_TOKEN` secret-binding capability, but introduces no endpoint, report field, D1 migration, D1/R2 binding or resource change, retention change, scheduled behavior, secret value, secret provisioning, deployment, or active-production interaction. `ADMIN_TOKEN` remains required for administrative writes and remains a backward-compatible report credential. Production use of `REPORT_READ_TOKEN` requires separately approved secret provisioning, deployment, and readback. Rollback to the prior Worker version restores the prior admin-only read contract, so the administrative fallback must remain available until read-token consumers have been verified. +The 1.30.0 governed bundle changed behavior, auth, configuration, and operator workflow, and documented the remaining cross-service boundary. The local work added the optional `REPORT_READ_TOKEN` secret-binding capability but itself performed no endpoint addition, report-field change, D1 migration, D1/R2 binding or resource change, retention change, scheduled change, secret-value handling, secret provisioning, deployment, or active-production interaction. `ADMIN_TOKEN` remains required for administrative writes and remains a backward-compatible report credential. The bundle was subsequently promoted through the approved production workflow and confirmed by the immutable production receipt recorded in the 1.31.0 section above; no secret value was read or recorded. Rollback to the prior Worker version restores the prior admin-only read contract, so the administrative fallback must remain available until read-token consumers have been verified. Agent Smith remains outside this least-privilege completion boundary. Its current runtime still uses `LIGHTHOUSE_ADMIN_TOKEN` for report reads and the administrative monthly snapshot write. Migrating its reads alone would leave the broad credential present. Removing that credential from Agent Smith requires a later owner-approved snapshot-specific authorization split (or removal of that write), coordinated cross-repository documentation/version bundles, external secret changes, verification, and only then any administrative-token rotation. @@ -18,11 +32,11 @@ Version 1.29.4 records the owner-approved read-only Cloudflare control-plane aud The verified owning Cloudflare account is `eb1a8dd5723031d94e57642e3eaaebda`. The primary `DB` binding resolves to D1 database ID `e46f2daa-7e97-45a3-9bf0-49003a42850c`, whose control-plane name is `lighthouse`; the prior checked-in name `buscore-lighthouse` was stale metadata for the same ID. Version 1.29.4 pins the account, reconciles that D1 name, preserves the `BUSCORE_LEADS_DB` and `MANIFEST_R2` bindings, replaces the ambiguous npm deploy command with explicit upload/status/history commands, and deliberately provides no direct production-deploy script. The checked-in production workflow is manual-only, main-only, serialized, and fully validated before deployment. Whenever the deploy step runs, the workflow attempts an active-deployment JSON receipt even if the deploy action reports failure, covering uncertain partial-success states. Production deployment uses Wrangler strict mode and preserves dashboard-managed variables. A deterministic release-control test enforces those repository-owned constraints. -This is an approved release-control conformance change. It changes operator-visible configuration metadata and release automation, but no Worker source, endpoint, response contract, auth behavior, storage schema or semantics, retention, schedule, ingestion, report metric, runtime integration, secret value, D1 resource ID, R2 bucket, custom-domain attachment, or active production traffic. No migration, secret operation, or Worker promotion is part of the local bundle. Active production remains the verified 1.29.3 Worker until a separately approved production deployment occurs; its Worker source, Wrangler configuration, and migrations are unchanged from 1.29.2. +This is an approved release-control conformance change. It changes operator-visible configuration metadata and release automation, but no Worker source, endpoint, response contract, auth behavior, storage schema or semantics, retention, schedule, ingestion, report metric, runtime integration, secret value, D1 resource ID, R2 bucket, custom-domain attachment, or active production traffic. No migration, secret operation, or Worker promotion was part of that local bundle. At the time of the 1.29.4 bundle, active production remained the verified 1.29.3 Worker pending a separately approved deployment; the current production receipt is recorded in the 1.31.0 section above. `OPERATIONS.md` defines the rollback boundary: select and explicitly approve an immutable Worker version ID, confirm that no deployment mutation is in progress, preserve interactive confirmation, verify resulting traffic by JSON status receipt, and treat Git, Workers Builds settings, secrets, domains, D1 data, and migrations as separate rollback domains. -**External release-control verification — 2026-08-26:** with separate owner approval, the Cloudflare Workers Builds production Deploy command was changed from `npx wrangler deploy` to exactly `npx wrangler versions upload`. Durable control-plane readback after reload confirmed that both the Deploy command and Version command are exactly `npx wrangler versions upload`. No build, upload, or deployment action was invoked. Workers Builds may now create version or preview state from branch and `main` pushes but must not promote active traffic; the checked-in manual GitHub workflow is the sole authorized production-promotion path. The availability and scope of that workflow's `CLOUDFLARE_API_TOKEN` remain unverified and must not be inferred from workflow text. +**External release-control verification — 2026-08-26:** with separate owner approval, the Cloudflare Workers Builds production Deploy command was changed from `npx wrangler deploy` to exactly `npx wrangler versions upload`. Durable control-plane readback after reload confirmed that both the Deploy command and Version command are exactly `npx wrangler versions upload`. No build, upload, or deployment action was invoked. Workers Builds may now create version or preview state from branch and `main` pushes but must not promote active traffic; the checked-in manual GitHub workflow is the sole authorized production-promotion path. Successful Lighthouse release run `33086080869` later proved that its configured `CLOUDFLARE_API_TOKEN` could perform the approved 1.30.0 deployment at that time; current availability or broader scope must not be inferred from workflow text or that historical success. ## Canonical operations and diagnostics — v1.29.3 documentation release @@ -46,9 +60,9 @@ CEO source coverage is based on the evidence actually available to each metric. Version 1.29.1 changes no D1 schema and required no migration. It was deployed on `2026-08-09T16:41:20.004814Z` as Cloudflare Version ID `ee320e1a-9ceb-4d88-a848-fd7ae0e9e3bc` after typecheck, 162 tests, a Wrangler dry run, and remote confirmation that no migration was pending. The owner-approved release used the authenticated local Wrangler session; at that time, no Cloudflare credential secret was configured for the checked-in GitHub Actions deployment path, and no secret value changed. Current deployment-token availability remains unverified by the 1.29.4 local bundle. -## CEO decision-report contract — v1.28.0 route deployed; contract 1.1 deployed with Worker 1.29.1 +## CEO decision-report contract — v1.28.0 route; contract 1.1 rollback compatibility -Authenticated `GET /report?view=ceo` is the additive, versioned decision-report contract for Agent Smith. Contract and metric-definition version `1.1` read existing aggregate and bounded raw-event tables directly; they do not depend on the scheduled `daily_rollup`, add a migration, rewrite history, or change any existing report view. Version 1.1 marks the strict nullable-detail and trusted-artifact-click semantics; bare `/report`, `fleet`, `site`, `tgc`, `source_health`, `asset`, and `monthly` remain compatibility contracts. +This section retains the deployed `1.1` contract as rollback authority; the `1.31.0` section above governs the additive `1.2` response contract. Authenticated `GET /report?view=ceo` is the versioned decision-report contract for Agent Smith. Contract and metric-definition version `1.1` read existing aggregate and bounded raw-event tables directly; they do not depend on the scheduled `daily_rollup`, add a migration, rewrite history, or change any existing report view. Version 1.1 marks the strict nullable-detail and trusted-artifact-click semantics; bare `/report`, `fleet`, `site`, `tgc`, `source_health`, `asset`, and `monthly` remain compatibility contracts. The CEO contract exposes current-day activity as partial and exposes the latest complete UTC day, the latest seven complete UTC days, the preceding seven complete UTC days, and the latest thirty complete UTC days with exact boundaries. Each source reports availability, freshness, coverage, data-through time, and a bounded reason code. A successful aggregate query can return numeric zero, but without an all-history observation watermark its source fails closed as `unknown` with `source_history_missing`; an old watermark is `stale` with `source_data_stale`. A failed query or absent optional binding is `null` and unavailable; one failed source does not become a believable zero or prevent unrelated sections from returning. diff --git a/contracts/ceo-v1/compat-1.1/healthy-zero.json b/contracts/ceo-v1/compat-1.1/healthy-zero.json new file mode 100644 index 0000000..251f537 --- /dev/null +++ b/contracts/ceo-v1/compat-1.1/healthy-zero.json @@ -0,0 +1,310 @@ +{ + "view": "ceo", + "report_contract_version": "1.1", + "metric_definition_version": "1.1", + "report_id": "00000000-0000-4000-8000-000000000001", + "generated_at": "2026-08-08T13:00:00.000Z", + "display_timezone": "America/Toronto", + "windows": { + "today": { + "start_at": "2026-08-08T00:00:00.000Z", + "end_at": "2026-08-08T13:00:00.000Z", + "complete": false + }, + "latest_complete_day": { + "start_at": "2026-08-07T00:00:00.000Z", + "end_at": "2026-08-08T00:00:00.000Z", + "complete": true + }, + "last_7_complete_days": { + "start_at": "2026-08-01T00:00:00.000Z", + "end_at": "2026-08-08T00:00:00.000Z", + "complete": true + }, + "previous_7_complete_days": { + "start_at": "2026-07-25T00:00:00.000Z", + "end_at": "2026-08-01T00:00:00.000Z", + "complete": true + }, + "last_30_complete_days": { + "start_at": "2026-07-09T00:00:00.000Z", + "end_at": "2026-08-08T00:00:00.000Z", + "complete": true + } + }, + "sources": { + "artifact_delivery": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-07-18", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "update_checks": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-07-15", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "product_telemetry": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-07-24", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "buscore_site": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-08-10", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "tgc_site": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-07-18", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "voluntary_inquiries": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-06-01", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "lighthouse_errors": { + "availability": "available", + "freshness": "unknown", + "data_through": null, + "definition_start_day": "2026-03-10", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": "source_history_missing" + }, + "service_probes": { + "availability": "available", + "freshness": "fresh", + "data_through": "2026-08-08T00:04:00.000Z", + "definition_start_day": "2026-07-06", + "coverage": { + "today": "partial", + "latest_complete_day": "partial", + "last_7_complete_days": "partial", + "previous_7_complete_days": "partial", + "last_30_complete_days": "partial" + }, + "reason_code": null + } + }, + "bus_core": { + "site_page_views": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "possible_download_interest_actions": { + "today": null, + "latest_complete_day": null, + "last_7_complete_days": null, + "previous_7_complete_days": null, + "last_30_complete_days": null + }, + "full_artifact_responses_offered": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "daily_source_credits": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "repeated_full_responses": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "limited_artifact_requests": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "acknowledged_first_launches": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "version_first_seen_events": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "acknowledged_workflow_milestones": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "known_version_check_requests": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "acknowledged_product_failures": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "artifact_response_failures": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "lighthouse_error_events": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "update_check_reconciliation_delta": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + } + }, + "business": { + "tgc_consented_page_views": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "voluntary_inquiries": { + "today": 0, + "latest_complete_day": 0, + "last_7_complete_days": 0, + "previous_7_complete_days": 0, + "last_30_complete_days": 0 + }, + "inquiry_sources_last_7_complete_days": [] + }, + "details": { + "versions_observed_last_30_complete_days": [], + "recent_product_failures_by_name": [], + "service_probes": [ + { + "target": "site_home", + "state": "pass", + "checked_at": "2026-08-08T00:04:00.000Z" + }, + { + "target": "site_downloads", + "state": "pass", + "checked_at": "2026-08-08T00:05:00.000Z" + }, + { + "target": "manifest", + "state": "pass", + "checked_at": "2026-08-08T00:06:00.000Z" + }, + { + "target": "release_artifact", + "state": "pass", + "checked_at": "2026-08-08T00:07:00.000Z" + }, + { + "target": "lead_endpoint", + "state": "pass", + "checked_at": "2026-08-08T00:08:00.000Z" + }, + { + "target": "github_release", + "state": "pass", + "checked_at": "2026-08-08T00:09:00.000Z" + } + ] + }, + "limitations": { + "artifact_transfer_completion_known": false, + "source_credits_are_people": false, + "source_credits_are_unique_across_days": false, + "download_interest_distinguishes_page_visit_from_file_click": true, + "download_interest_includes_pre_definition_history": false, + "product_telemetry_is_opt_in_only": true + } +} diff --git a/contracts/ceo-v1/core-source-failure.json b/contracts/ceo-v1/core-source-failure.json index 96b4cdf..f474e3c 100644 --- a/contracts/ceo-v1/core-source-failure.json +++ b/contracts/ceo-v1/core-source-failure.json @@ -1,6 +1,6 @@ { "view": "ceo", - "report_contract_version": "1.1", + "report_contract_version": "1.2", "metric_definition_version": "1.1", "report_id": "00000000-0000-4000-8000-000000000005", "generated_at": "2026-08-08T13:00:00.000Z", @@ -35,7 +35,7 @@ "sources": { "artifact_delivery": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -45,11 +45,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "update_checks": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-15", "coverage": { @@ -59,7 +60,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "product_telemetry": { "availability": "unavailable", @@ -73,7 +75,8 @@ "previous_7_complete_days": "unavailable", "last_30_complete_days": "unavailable" }, - "reason_code": "query_failed" + "reason_code": "query_failed", + "freshness_basis": "activity" }, "buscore_site": { "availability": "available", @@ -87,11 +90,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "tgc_site": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -101,11 +105,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "voluntary_inquiries": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-06-01", "coverage": { @@ -115,11 +120,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "lighthouse_errors": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-03-10", "coverage": { @@ -129,7 +135,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "service_probes": { "availability": "available", @@ -143,7 +150,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": null, + "freshness_basis": "scheduled_probe" } }, "bus_core": { @@ -297,7 +305,12 @@ "state": "pass", "checked_at": "2026-08-08T00:09:00.000Z" } - ] + ], + "voluntary_inquiry_records": { + "total_records": 1, + "last_created_at": "2026-08-08T13:00:00.000Z", + "last_updated_at": "2026-08-08T13:00:00.000Z" + } }, "limitations": { "artifact_transfer_completion_known": false, @@ -305,6 +318,9 @@ "source_credits_are_unique_across_days": false, "download_interest_distinguishes_page_visit_from_file_click": true, "download_interest_includes_pre_definition_history": false, - "product_telemetry_is_opt_in_only": true + "product_telemetry_is_opt_in_only": true, + "activity_recency_proves_producer_health": false, + "voluntary_inquiries_are_lead_records": true, + "existing_lead_updates_increment_inquiry_windows": false } } diff --git a/contracts/ceo-v1/current-observed-pattern.json b/contracts/ceo-v1/current-observed-pattern.json index f20b0b9..473fd43 100644 --- a/contracts/ceo-v1/current-observed-pattern.json +++ b/contracts/ceo-v1/current-observed-pattern.json @@ -1,6 +1,6 @@ { "view": "ceo", - "report_contract_version": "1.1", + "report_contract_version": "1.2", "metric_definition_version": "1.1", "report_id": "00000000-0000-4000-8000-000000000002", "generated_at": "2026-08-08T13:00:00.000Z", @@ -35,7 +35,7 @@ "sources": { "artifact_delivery": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -45,11 +45,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "update_checks": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-15", "coverage": { @@ -59,11 +60,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "product_telemetry": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-24", "coverage": { @@ -73,7 +75,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "buscore_site": { "availability": "available", @@ -87,11 +90,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "tgc_site": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -101,11 +105,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "voluntary_inquiries": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-06-01", "coverage": { @@ -115,11 +120,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "lighthouse_errors": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-03-10", "coverage": { @@ -129,7 +135,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "service_probes": { "availability": "available", @@ -143,7 +150,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": null, + "freshness_basis": "scheduled_probe" } }, "bus_core": { @@ -307,7 +315,12 @@ "state": "pass", "checked_at": "2026-08-08T00:09:00.000Z" } - ] + ], + "voluntary_inquiry_records": { + "total_records": 1, + "last_created_at": "2026-08-08T13:00:00.000Z", + "last_updated_at": "2026-08-08T13:00:00.000Z" + } }, "limitations": { "artifact_transfer_completion_known": false, @@ -315,6 +328,9 @@ "source_credits_are_unique_across_days": false, "download_interest_distinguishes_page_visit_from_file_click": true, "download_interest_includes_pre_definition_history": false, - "product_telemetry_is_opt_in_only": true + "product_telemetry_is_opt_in_only": true, + "activity_recency_proves_producer_health": false, + "voluntary_inquiries_are_lead_records": true, + "existing_lead_updates_increment_inquiry_windows": false } } diff --git a/contracts/ceo-v1/healthy-zero.json b/contracts/ceo-v1/healthy-zero.json index 251f537..6a15472 100644 --- a/contracts/ceo-v1/healthy-zero.json +++ b/contracts/ceo-v1/healthy-zero.json @@ -1,6 +1,6 @@ { "view": "ceo", - "report_contract_version": "1.1", + "report_contract_version": "1.2", "metric_definition_version": "1.1", "report_id": "00000000-0000-4000-8000-000000000001", "generated_at": "2026-08-08T13:00:00.000Z", @@ -45,7 +45,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "update_checks": { "availability": "available", @@ -59,7 +60,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "product_telemetry": { "availability": "available", @@ -73,7 +75,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "buscore_site": { "availability": "available", @@ -87,7 +90,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "tgc_site": { "availability": "available", @@ -101,7 +105,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "voluntary_inquiries": { "availability": "available", @@ -115,7 +120,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "lighthouse_errors": { "availability": "available", @@ -129,7 +135,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "service_probes": { "availability": "available", @@ -143,7 +150,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": null, + "freshness_basis": "scheduled_probe" } }, "bus_core": { @@ -297,7 +305,12 @@ "state": "pass", "checked_at": "2026-08-08T00:09:00.000Z" } - ] + ], + "voluntary_inquiry_records": { + "total_records": 0, + "last_created_at": null, + "last_updated_at": null + } }, "limitations": { "artifact_transfer_completion_known": false, @@ -305,6 +318,9 @@ "source_credits_are_unique_across_days": false, "download_interest_distinguishes_page_visit_from_file_click": true, "download_interest_includes_pre_definition_history": false, - "product_telemetry_is_opt_in_only": true + "product_telemetry_is_opt_in_only": true, + "activity_recency_proves_producer_health": false, + "voluntary_inquiries_are_lead_records": true, + "existing_lead_updates_increment_inquiry_windows": false } } diff --git a/contracts/ceo-v1/partial-business-source.json b/contracts/ceo-v1/partial-business-source.json index 27bd52b..951dd7a 100644 --- a/contracts/ceo-v1/partial-business-source.json +++ b/contracts/ceo-v1/partial-business-source.json @@ -1,6 +1,6 @@ { "view": "ceo", - "report_contract_version": "1.1", + "report_contract_version": "1.2", "metric_definition_version": "1.1", "report_id": "00000000-0000-4000-8000-000000000003", "generated_at": "2026-08-08T13:00:00.000Z", @@ -35,7 +35,7 @@ "sources": { "artifact_delivery": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -45,11 +45,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "update_checks": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-15", "coverage": { @@ -59,11 +60,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "product_telemetry": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-24", "coverage": { @@ -73,7 +75,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "buscore_site": { "availability": "available", @@ -87,11 +90,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "tgc_site": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -101,7 +105,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "voluntary_inquiries": { "availability": "unavailable", @@ -115,11 +120,12 @@ "previous_7_complete_days": "unavailable", "last_30_complete_days": "unavailable" }, - "reason_code": "binding_not_configured" + "reason_code": "binding_not_configured", + "freshness_basis": "activity" }, "lighthouse_errors": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-03-10", "coverage": { @@ -129,7 +135,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "service_probes": { "availability": "available", @@ -143,7 +150,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": null, + "freshness_basis": "scheduled_probe" } }, "bus_core": { @@ -297,7 +305,8 @@ "state": "pass", "checked_at": "2026-08-08T00:09:00.000Z" } - ] + ], + "voluntary_inquiry_records": null }, "limitations": { "artifact_transfer_completion_known": false, @@ -305,6 +314,9 @@ "source_credits_are_unique_across_days": false, "download_interest_distinguishes_page_visit_from_file_click": true, "download_interest_includes_pre_definition_history": false, - "product_telemetry_is_opt_in_only": true + "product_telemetry_is_opt_in_only": true, + "activity_recency_proves_producer_health": false, + "voluntary_inquiries_are_lead_records": true, + "existing_lead_updates_increment_inquiry_windows": false } } diff --git a/contracts/ceo-v1/product-failure.json b/contracts/ceo-v1/product-failure.json index 8e7b10f..c17a31c 100644 --- a/contracts/ceo-v1/product-failure.json +++ b/contracts/ceo-v1/product-failure.json @@ -1,6 +1,6 @@ { "view": "ceo", - "report_contract_version": "1.1", + "report_contract_version": "1.2", "metric_definition_version": "1.1", "report_id": "00000000-0000-4000-8000-000000000004", "generated_at": "2026-08-08T13:00:00.000Z", @@ -35,7 +35,7 @@ "sources": { "artifact_delivery": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -45,11 +45,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "update_checks": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-15", "coverage": { @@ -59,11 +60,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "product_telemetry": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-24", "coverage": { @@ -73,7 +75,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "buscore_site": { "availability": "available", @@ -87,11 +90,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": "source_history_missing" + "reason_code": "source_history_missing", + "freshness_basis": "activity" }, "tgc_site": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-07-18", "coverage": { @@ -101,11 +105,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "voluntary_inquiries": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-06-01", "coverage": { @@ -115,11 +120,12 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "lighthouse_errors": { "availability": "available", - "freshness": "fresh", + "freshness": "unknown", "data_through": "2026-08-08T13:00:00.000Z", "definition_start_day": "2026-03-10", "coverage": { @@ -129,7 +135,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": "activity_only", + "freshness_basis": "activity" }, "service_probes": { "availability": "available", @@ -143,7 +150,8 @@ "previous_7_complete_days": "partial", "last_30_complete_days": "partial" }, - "reason_code": null + "reason_code": null, + "freshness_basis": "scheduled_probe" } }, "bus_core": { @@ -302,7 +310,12 @@ "state": "pass", "checked_at": "2026-08-08T00:09:00.000Z" } - ] + ], + "voluntary_inquiry_records": { + "total_records": 1, + "last_created_at": "2026-08-08T13:00:00.000Z", + "last_updated_at": "2026-08-08T13:00:00.000Z" + } }, "limitations": { "artifact_transfer_completion_known": false, @@ -310,6 +323,9 @@ "source_credits_are_unique_across_days": false, "download_interest_distinguishes_page_visit_from_file_click": true, "download_interest_includes_pre_definition_history": false, - "product_telemetry_is_opt_in_only": true + "product_telemetry_is_opt_in_only": true, + "activity_recency_proves_producer_health": false, + "voluntary_inquiries_are_lead_records": true, + "existing_lead_updates_increment_inquiry_windows": false } } diff --git a/contracts/ceo-v1/report-1.1.schema.json b/contracts/ceo-v1/report-1.1.schema.json new file mode 100644 index 0000000..a47646b --- /dev/null +++ b/contracts/ceo-v1/report-1.1.schema.json @@ -0,0 +1,899 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://truegoodcraft.ca/contracts/ceo-v1/report.schema.json", + "title": "TGC CEO Report Contract V1.1", + "type": "object", + "additionalProperties": false, + "required": [ + "view", + "report_contract_version", + "metric_definition_version", + "report_id", + "generated_at", + "display_timezone", + "windows", + "sources", + "bus_core", + "business", + "details", + "limitations" + ], + "properties": { + "view": { + "const": "ceo" + }, + "report_contract_version": { + "const": "1.1" + }, + "metric_definition_version": { + "const": "1.1" + }, + "report_id": { + "type": "string", + "format": "uuid", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "generated_at": { + "type": "string", + "format": "date-time" + }, + "display_timezone": { + "const": "America/Toronto" + }, + "windows": { + "$ref": "#/$defs/reportWindows" + }, + "sources": { + "type": "object", + "additionalProperties": false, + "required": [ + "artifact_delivery", + "update_checks", + "product_telemetry", + "buscore_site", + "tgc_site", + "voluntary_inquiries", + "lighthouse_errors", + "service_probes" + ], + "properties": { + "artifact_delivery": { + "$ref": "#/$defs/sourceState" + }, + "update_checks": { + "$ref": "#/$defs/sourceState" + }, + "product_telemetry": { + "$ref": "#/$defs/sourceState" + }, + "buscore_site": { + "$ref": "#/$defs/sourceState" + }, + "tgc_site": { + "$ref": "#/$defs/sourceState" + }, + "voluntary_inquiries": { + "$ref": "#/$defs/sourceState" + }, + "lighthouse_errors": { + "$ref": "#/$defs/sourceState" + }, + "service_probes": { + "$ref": "#/$defs/sourceState" + } + } + }, + "bus_core": { + "type": "object", + "additionalProperties": false, + "required": [ + "site_page_views", + "possible_download_interest_actions", + "full_artifact_responses_offered", + "daily_source_credits", + "repeated_full_responses", + "limited_artifact_requests", + "acknowledged_first_launches", + "version_first_seen_events", + "acknowledged_workflow_milestones", + "known_version_check_requests", + "acknowledged_product_failures", + "artifact_response_failures", + "lighthouse_error_events", + "update_check_reconciliation_delta" + ], + "properties": { + "site_page_views": { + "$ref": "#/$defs/windowCounts" + }, + "possible_download_interest_actions": { + "$ref": "#/$defs/windowCounts" + }, + "full_artifact_responses_offered": { + "$ref": "#/$defs/windowCounts" + }, + "daily_source_credits": { + "$ref": "#/$defs/windowCounts" + }, + "repeated_full_responses": { + "$ref": "#/$defs/windowCounts" + }, + "limited_artifact_requests": { + "$ref": "#/$defs/windowCounts" + }, + "acknowledged_first_launches": { + "$ref": "#/$defs/windowCounts" + }, + "version_first_seen_events": { + "$ref": "#/$defs/windowCounts" + }, + "acknowledged_workflow_milestones": { + "$ref": "#/$defs/windowCounts" + }, + "known_version_check_requests": { + "$ref": "#/$defs/windowCounts" + }, + "acknowledged_product_failures": { + "$ref": "#/$defs/windowCounts" + }, + "artifact_response_failures": { + "$ref": "#/$defs/windowCounts" + }, + "lighthouse_error_events": { + "$ref": "#/$defs/windowCounts" + }, + "update_check_reconciliation_delta": { + "$ref": "#/$defs/signedWindowCounts" + } + } + }, + "business": { + "type": "object", + "additionalProperties": false, + "required": [ + "tgc_consented_page_views", + "voluntary_inquiries", + "inquiry_sources_last_7_complete_days" + ], + "properties": { + "tgc_consented_page_views": { + "$ref": "#/$defs/windowCounts" + }, + "voluntary_inquiries": { + "$ref": "#/$defs/windowCounts" + }, + "inquiry_sources_last_7_complete_days": { + "oneOf": [ + { + "type": "array", + "maxItems": 10, + "items": { + "$ref": "#/$defs/sourceCount" + } + }, + { + "type": "null" + } + ] + } + } + }, + "details": { + "type": "object", + "additionalProperties": false, + "required": [ + "versions_observed_last_30_complete_days", + "recent_product_failures_by_name", + "service_probes" + ], + "properties": { + "versions_observed_last_30_complete_days": { + "oneOf": [ + { + "type": "array", + "maxItems": 10, + "items": { + "$ref": "#/$defs/versionCount" + } + }, + { + "type": "null" + } + ] + }, + "recent_product_failures_by_name": { + "oneOf": [ + { + "type": "array", + "maxItems": 7, + "items": { + "$ref": "#/$defs/failureCount" + } + }, + { + "type": "null" + } + ] + }, + "service_probes": { + "oneOf": [ + { + "type": "array", + "maxItems": 6, + "items": { + "$ref": "#/$defs/serviceProbe" + } + }, + { + "type": "null" + } + ] + } + } + }, + "limitations": { + "type": "object", + "additionalProperties": false, + "required": [ + "artifact_transfer_completion_known", + "source_credits_are_people", + "source_credits_are_unique_across_days", + "download_interest_distinguishes_page_visit_from_file_click", + "download_interest_includes_pre_definition_history", + "product_telemetry_is_opt_in_only" + ], + "properties": { + "artifact_transfer_completion_known": { + "const": false + }, + "source_credits_are_people": { + "const": false + }, + "source_credits_are_unique_across_days": { + "const": false + }, + "download_interest_distinguishes_page_visit_from_file_click": { + "const": true + }, + "download_interest_includes_pre_definition_history": { + "const": false + }, + "product_telemetry_is_opt_in_only": { + "const": true + } + } + } + }, + "allOf": [ + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["voluntary_inquiries"], + "properties": { + "voluntary_inquiries": { + "type": "object", + "required": ["availability"], + "properties": { + "availability": { "const": "unavailable" } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "business": { + "type": "object", + "properties": { + "inquiry_sources_last_7_complete_days": { "type": "null" } + } + } + } + }, + "else": { + "type": "object", + "properties": { + "business": { + "type": "object", + "properties": { + "inquiry_sources_last_7_complete_days": { "type": "array" } + } + } + } + } + }, + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["product_telemetry"], + "properties": { + "product_telemetry": { + "type": "object", + "required": ["availability"], + "properties": { + "availability": { "const": "unavailable" } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "versions_observed_last_30_complete_days": { "type": "null" }, + "recent_product_failures_by_name": { "type": "null" } + } + } + } + }, + "else": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "versions_observed_last_30_complete_days": { "type": "array" }, + "recent_product_failures_by_name": { "type": "array" } + } + } + } + } + }, + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["service_probes"], + "properties": { + "service_probes": { + "type": "object", + "required": ["availability"], + "properties": { + "availability": { "const": "unavailable" } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "service_probes": { "type": "null" } + } + } + } + }, + "else": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "service_probes": { "type": "array" } + } + } + } + } + } + ], + "$defs": { + "reportWindow": { + "type": "object", + "additionalProperties": false, + "required": [ + "start_at", + "end_at", + "complete" + ], + "properties": { + "start_at": { + "type": "string", + "format": "date-time" + }, + "end_at": { + "type": "string", + "format": "date-time" + }, + "complete": { + "type": "boolean" + } + } + }, + "reportWindows": { + "type": "object", + "additionalProperties": false, + "required": [ + "today", + "latest_complete_day", + "last_7_complete_days", + "previous_7_complete_days", + "last_30_complete_days" + ], + "properties": { + "today": { + "$ref": "#/$defs/reportWindow" + }, + "latest_complete_day": { + "$ref": "#/$defs/reportWindow" + }, + "last_7_complete_days": { + "$ref": "#/$defs/reportWindow" + }, + "previous_7_complete_days": { + "$ref": "#/$defs/reportWindow" + }, + "last_30_complete_days": { + "$ref": "#/$defs/reportWindow" + } + } + }, + "coverage": { + "type": "object", + "additionalProperties": false, + "required": [ + "today", + "latest_complete_day", + "last_7_complete_days", + "previous_7_complete_days", + "last_30_complete_days" + ], + "properties": { + "today": { + "$ref": "#/$defs/coverageValue" + }, + "latest_complete_day": { + "$ref": "#/$defs/coverageValue" + }, + "last_7_complete_days": { + "$ref": "#/$defs/coverageValue" + }, + "previous_7_complete_days": { + "$ref": "#/$defs/coverageValue" + }, + "last_30_complete_days": { + "$ref": "#/$defs/coverageValue" + } + } + }, + "coverageValue": { + "enum": [ + "full", + "partial", + "unavailable" + ] + }, + "availableCoverage": { + "type": "object", + "$ref": "#/$defs/coverage", + "properties": { + "today": { + "const": "partial" + }, + "latest_complete_day": { + "enum": [ + "full", + "partial" + ] + }, + "last_7_complete_days": { + "enum": [ + "full", + "partial" + ] + }, + "previous_7_complete_days": { + "enum": [ + "full", + "partial" + ] + }, + "last_30_complete_days": { + "enum": [ + "full", + "partial" + ] + } + } + }, + "partialCoverage": { + "type": "object", + "$ref": "#/$defs/coverage", + "properties": { + "today": { + "const": "partial" + }, + "latest_complete_day": { + "const": "partial" + }, + "last_7_complete_days": { + "const": "partial" + }, + "previous_7_complete_days": { + "const": "partial" + }, + "last_30_complete_days": { + "const": "partial" + } + } + }, + "unavailableCoverage": { + "type": "object", + "$ref": "#/$defs/coverage", + "properties": { + "today": { + "const": "unavailable" + }, + "latest_complete_day": { + "const": "unavailable" + }, + "last_7_complete_days": { + "const": "unavailable" + }, + "previous_7_complete_days": { + "const": "unavailable" + }, + "last_30_complete_days": { + "const": "unavailable" + } + } + }, + "sourceState": { + "type": "object", + "additionalProperties": false, + "required": [ + "availability", + "freshness", + "data_through", + "definition_start_day", + "coverage", + "reason_code" + ], + "properties": { + "availability": { + "enum": [ + "available", + "unavailable" + ] + }, + "freshness": { + "enum": [ + "fresh", + "stale", + "unknown" + ] + }, + "data_through": { + "oneOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "definition_start_day": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$" + }, + { + "type": "null" + } + ] + }, + "coverage": { + "$ref": "#/$defs/coverage" + }, + "reason_code": { + "enum": [ + null, + "query_failed", + "binding_not_configured", + "probe_history_missing", + "probe_data_stale", + "source_history_missing", + "source_data_stale" + ] + } + }, + "oneOf": [ + { + "type": "object", + "properties": { + "availability": { + "const": "available" + }, + "freshness": { + "const": "fresh" + }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { + "$ref": "#/$defs/availableCoverage" + }, + "reason_code": { + "const": null + } + } + }, + { + "type": "object", + "properties": { + "availability": { + "const": "available" + }, + "freshness": { + "const": "stale" + }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { + "$ref": "#/$defs/availableCoverage" + }, + "reason_code": { + "enum": [ + "probe_data_stale", + "source_data_stale" + ] + } + } + }, + { + "type": "object", + "properties": { + "availability": { + "const": "available" + }, + "freshness": { + "const": "unknown" + }, + "data_through": { + "type": "null" + }, + "coverage": { + "$ref": "#/$defs/partialCoverage" + }, + "reason_code": { + "const": "source_history_missing" + } + } + }, + { + "type": "object", + "properties": { + "availability": { + "const": "unavailable" + }, + "freshness": { + "const": "unknown" + }, + "data_through": { + "type": "null" + }, + "coverage": { + "$ref": "#/$defs/unavailableCoverage" + }, + "reason_code": { + "enum": [ + "query_failed", + "binding_not_configured", + "probe_history_missing" + ] + } + } + } + ] + }, + "windowCounts": { + "type": "object", + "additionalProperties": false, + "required": [ + "today", + "latest_complete_day", + "last_7_complete_days", + "previous_7_complete_days", + "last_30_complete_days" + ], + "properties": { + "today": { + "$ref": "#/$defs/nullableCount" + }, + "latest_complete_day": { + "$ref": "#/$defs/nullableCount" + }, + "last_7_complete_days": { + "$ref": "#/$defs/nullableCount" + }, + "previous_7_complete_days": { + "$ref": "#/$defs/nullableCount" + }, + "last_30_complete_days": { + "$ref": "#/$defs/nullableCount" + } + } + }, + "signedWindowCounts": { + "type": "object", + "additionalProperties": false, + "required": [ + "today", + "latest_complete_day", + "last_7_complete_days", + "previous_7_complete_days", + "last_30_complete_days" + ], + "properties": { + "today": { + "$ref": "#/$defs/nullableSignedCount" + }, + "latest_complete_day": { + "$ref": "#/$defs/nullableSignedCount" + }, + "last_7_complete_days": { + "$ref": "#/$defs/nullableSignedCount" + }, + "previous_7_complete_days": { + "$ref": "#/$defs/nullableSignedCount" + }, + "last_30_complete_days": { + "$ref": "#/$defs/nullableSignedCount" + } + } + }, + "nullableCount": { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "null" + } + ] + }, + "nullableSignedCount": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "sourceCount": { + "type": "object", + "additionalProperties": false, + "required": [ + "source", + "count" + ], + "properties": { + "source": { + "enum": [ + "(direct)", + "github", + "reddit", + "hacker_news", + "discord", + "google", + "bing", + "linkedin", + "x_twitter", + "meta", + "youtube", + "email", + "partner", + "other" + ] + }, + "count": { + "type": "integer", + "minimum": 0 + } + } + }, + "versionCount": { + "type": "object", + "additionalProperties": false, + "required": [ + "version", + "count" + ], + "properties": { + "version": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$" + }, + "count": { + "type": "integer", + "minimum": 0 + } + } + }, + "failureCount": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "count" + ], + "properties": { + "name": { + "enum": [ + "update_failure", + "startup_failure", + "backup_failure", + "restore_failure", + "migration_failure", + "import_failed", + "unhandled_application_error" + ] + }, + "count": { + "type": "integer", + "minimum": 0 + } + } + }, + "serviceProbe": { + "type": "object", + "additionalProperties": false, + "required": [ + "target", + "state", + "checked_at" + ], + "properties": { + "target": { + "enum": [ + "site_home", + "site_downloads", + "manifest", + "release_artifact", + "lead_endpoint", + "github_release" + ] + }, + "state": { + "enum": [ + "pass", + "fail" + ] + }, + "checked_at": { + "type": "string", + "format": "date-time" + } + } + } + } +} diff --git a/contracts/ceo-v1/report.schema.json b/contracts/ceo-v1/report.schema.json index a47646b..f0e87e8 100644 --- a/contracts/ceo-v1/report.schema.json +++ b/contracts/ceo-v1/report.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://truegoodcraft.ca/contracts/ceo-v1/report.schema.json", - "title": "TGC CEO Report Contract V1.1", + "$id": "https://truegoodcraft.ca/contracts/ceo-v1/report-1.2.schema.json", + "title": "TGC CEO Report Contract V1.2", "type": "object", "additionalProperties": false, "required": [ @@ -23,7 +23,7 @@ "const": "ceo" }, "report_contract_version": { - "const": "1.1" + "const": "1.2" }, "metric_definition_version": { "const": "1.1" @@ -58,28 +58,28 @@ ], "properties": { "artifact_delivery": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "update_checks": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "product_telemetry": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "buscore_site": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "tgc_site": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "voluntary_inquiries": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "lighthouse_errors": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/activitySourceState" }, "service_probes": { - "$ref": "#/$defs/sourceState" + "$ref": "#/$defs/probeSourceState" } } }, @@ -184,7 +184,8 @@ "required": [ "versions_observed_last_30_complete_days", "recent_product_failures_by_name", - "service_probes" + "service_probes", + "voluntary_inquiry_records" ], "properties": { "versions_observed_last_30_complete_days": { @@ -218,11 +219,17 @@ "service_probes": { "oneOf": [ { - "type": "array", - "maxItems": 6, - "items": { - "$ref": "#/$defs/serviceProbe" - } + "$ref": "#/$defs/serviceProbeArray" + }, + { + "type": "null" + } + ] + }, + "voluntary_inquiry_records": { + "oneOf": [ + { + "$ref": "#/$defs/voluntaryInquiryRecords" }, { "type": "null" @@ -240,7 +247,10 @@ "source_credits_are_unique_across_days", "download_interest_distinguishes_page_visit_from_file_click", "download_interest_includes_pre_definition_history", - "product_telemetry_is_opt_in_only" + "product_telemetry_is_opt_in_only", + "activity_recency_proves_producer_health", + "voluntary_inquiries_are_lead_records", + "existing_lead_updates_increment_inquiry_windows" ], "properties": { "artifact_transfer_completion_known": { @@ -260,6 +270,15 @@ }, "product_telemetry_is_opt_in_only": { "const": true + }, + "activity_recency_proves_producer_health": { + "const": false + }, + "voluntary_inquiries_are_lead_records": { + "const": true + }, + "existing_lead_updates_increment_inquiry_windows": { + "const": false } } } @@ -291,8 +310,24 @@ "business": { "type": "object", "properties": { + "voluntary_inquiries": { + "type": "object", + "properties": { + "today": { "type": "null" }, + "latest_complete_day": { "type": "null" }, + "last_7_complete_days": { "type": "null" }, + "previous_7_complete_days": { "type": "null" }, + "last_30_complete_days": { "type": "null" } + } + }, "inquiry_sources_last_7_complete_days": { "type": "null" } } + }, + "details": { + "type": "object", + "properties": { + "voluntary_inquiry_records": { "type": "null" } + } } } }, @@ -304,6 +339,12 @@ "properties": { "inquiry_sources_last_7_complete_days": { "type": "array" } } + }, + "details": { + "type": "object", + "properties": { + "voluntary_inquiry_records": { "$ref": "#/$defs/voluntaryInquiryRecords" } + } } } } @@ -390,7 +431,120 @@ "details": { "type": "object", "properties": { - "service_probes": { "type": "array" } + "service_probes": { "$ref": "#/$defs/serviceProbeArray" } + } + } + } + } + }, + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["service_probes"], + "properties": { + "service_probes": { + "type": "object", + "required": ["reason_code"], + "properties": { + "reason_code": { "const": "probe_history_missing" } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "service_probes": { + "allOf": [ + { "$ref": "#/$defs/serviceProbeArray" }, + { "type": "array", "maxItems": 0 } + ] + } + } + } + } + } + }, + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["service_probes"], + "properties": { + "service_probes": { + "type": "object", + "required": ["reason_code"], + "properties": { + "reason_code": { "const": "probe_history_incomplete" } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "service_probes": { + "allOf": [ + { "$ref": "#/$defs/serviceProbeArray" }, + { "type": "array", "minItems": 1, "maxItems": 5 } + ] + } + } + } + } + } + }, + { + "if": { + "type": "object", + "required": ["sources"], + "properties": { + "sources": { + "type": "object", + "required": ["service_probes"], + "properties": { + "service_probes": { + "type": "object", + "required": ["reason_code"], + "properties": { + "reason_code": { + "enum": [null, "probe_data_stale"] + } + } + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "details": { + "type": "object", + "properties": { + "service_probes": { + "allOf": [ + { "$ref": "#/$defs/serviceProbeArray" }, + { "type": "array", "minItems": 6, "maxItems": 6 } + ] + } } } } @@ -558,12 +712,13 @@ } } }, - "sourceState": { + "sourceStateBase": { "type": "object", "additionalProperties": false, "required": [ "availability", "freshness", + "freshness_basis", "data_through", "definition_start_day", "coverage", @@ -583,6 +738,12 @@ "unknown" ] }, + "freshness_basis": { + "enum": [ + "activity", + "scheduled_probe" + ] + }, "data_through": { "oneOf": [ { @@ -611,102 +772,203 @@ "reason_code": { "enum": [ null, + "activity_only", "query_failed", "binding_not_configured", "probe_history_missing", + "probe_history_incomplete", "probe_data_stale", - "source_history_missing", - "source_data_stale" + "source_history_missing" ] } - }, - "oneOf": [ + } + }, + "activitySourceState": { + "allOf": [ { - "type": "object", - "properties": { - "availability": { - "const": "available" - }, - "freshness": { - "const": "fresh" - }, - "data_through": { - "type": "string", - "format": "date-time" + "$ref": "#/$defs/sourceStateBase" + }, + { + "oneOf": [ + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "activity" }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { "$ref": "#/$defs/availableCoverage" }, + "reason_code": { "const": "activity_only" } + } }, - "coverage": { - "$ref": "#/$defs/availableCoverage" + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "activity" }, + "data_through": { "type": "null" }, + "coverage": { "$ref": "#/$defs/partialCoverage" }, + "reason_code": { "const": "source_history_missing" } + } }, - "reason_code": { - "const": null + { + "type": "object", + "properties": { + "availability": { "const": "unavailable" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "activity" }, + "data_through": { "type": "null" }, + "coverage": { "$ref": "#/$defs/unavailableCoverage" }, + "reason_code": { + "enum": [ + "query_failed", + "binding_not_configured" + ] + } + } } - } + ] + } + ] + }, + "probeSourceState": { + "allOf": [ + { + "$ref": "#/$defs/sourceStateBase" }, { - "type": "object", - "properties": { - "availability": { - "const": "available" + "oneOf": [ + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "fresh" }, + "freshness_basis": { "const": "scheduled_probe" }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { "$ref": "#/$defs/availableCoverage" }, + "reason_code": { "const": null } + } }, - "freshness": { - "const": "stale" + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "stale" }, + "freshness_basis": { "const": "scheduled_probe" }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { "$ref": "#/$defs/availableCoverage" }, + "reason_code": { "const": "probe_data_stale" } + } }, - "data_through": { - "type": "string", - "format": "date-time" + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "scheduled_probe" }, + "data_through": { "type": "null" }, + "coverage": { "$ref": "#/$defs/partialCoverage" }, + "reason_code": { "const": "probe_history_missing" } + } }, - "coverage": { - "$ref": "#/$defs/availableCoverage" + { + "type": "object", + "properties": { + "availability": { "const": "available" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "scheduled_probe" }, + "data_through": { + "type": "string", + "format": "date-time" + }, + "coverage": { "$ref": "#/$defs/partialCoverage" }, + "reason_code": { "const": "probe_history_incomplete" } + } }, - "reason_code": { - "enum": [ - "probe_data_stale", - "source_data_stale" - ] + { + "type": "object", + "properties": { + "availability": { "const": "unavailable" }, + "freshness": { "const": "unknown" }, + "freshness_basis": { "const": "scheduled_probe" }, + "data_through": { "type": "null" }, + "coverage": { "$ref": "#/$defs/unavailableCoverage" }, + "reason_code": { "const": "query_failed" } + } } - } + ] + } + ] + }, + "voluntaryInquiryRecords": { + "type": "object", + "additionalProperties": false, + "required": [ + "total_records", + "last_created_at", + "last_updated_at" + ], + "properties": { + "total_records": { + "type": "integer", + "minimum": 0 }, - { - "type": "object", - "properties": { - "availability": { - "const": "available" - }, - "freshness": { - "const": "unknown" + "last_created_at": { + "oneOf": [ + { + "type": "string", + "format": "date-time" }, - "data_through": { + { "type": "null" - }, - "coverage": { - "$ref": "#/$defs/partialCoverage" - }, - "reason_code": { - "const": "source_history_missing" } - } + ] }, - { - "type": "object", - "properties": { - "availability": { - "const": "unavailable" - }, - "freshness": { - "const": "unknown" + "last_updated_at": { + "oneOf": [ + { + "type": "string", + "format": "date-time" }, - "data_through": { + { "type": "null" - }, - "coverage": { - "$ref": "#/$defs/unavailableCoverage" - }, - "reason_code": { - "enum": [ - "query_failed", - "binding_not_configured", - "probe_history_missing" - ] + } + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "total_records": { "const": 0 } + } + }, + "then": { + "properties": { + "last_created_at": { "type": "null" }, + "last_updated_at": { "type": "null" } + } + }, + "else": { + "properties": { + "last_created_at": { + "type": "string", + "format": "date-time" + }, + "last_updated_at": { + "type": "string", + "format": "date-time" + } } } } @@ -894,6 +1156,75 @@ "format": "date-time" } } + }, + "serviceProbeArray": { + "type": "array", + "maxItems": 6, + "items": { + "$ref": "#/$defs/serviceProbe" + }, + "allOf": [ + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "site_home" } } + }, + "minContains": 2 + } + }, + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "site_downloads" } } + }, + "minContains": 2 + } + }, + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "manifest" } } + }, + "minContains": 2 + } + }, + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "release_artifact" } } + }, + "minContains": 2 + } + }, + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "lead_endpoint" } } + }, + "minContains": 2 + } + }, + { + "not": { + "contains": { + "type": "object", + "required": ["target"], + "properties": { "target": { "const": "github_release" } } + }, + "minContains": 2 + } + } + ] } } } diff --git a/package-lock.json b/package-lock.json index 9241c2b..837e253 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "buscore-lighthouse", - "version": "1.30.0", + "version": "1.31.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "buscore-lighthouse", - "version": "1.30.0", + "version": "1.31.0", "license": "ISC", "devDependencies": { "@cloudflare/workers-types": "^4.20260305.0", diff --git a/package.json b/package.json index b5b5bf1..17b3eab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "buscore-lighthouse", - "version": "1.30.0", + "version": "1.31.0", "description": "Standalone deterministic metrics worker: manifest proxy + fixed daily counters + protected on-demand reporting.", "scripts": { "dev": "wrangler dev", diff --git a/scripts/read-ceo-report.mjs b/scripts/read-ceo-report.mjs index f7c0537..724561c 100644 --- a/scripts/read-ceo-report.mjs +++ b/scripts/read-ceo-report.mjs @@ -18,7 +18,10 @@ export const ACCESS_BLOCKED_LINE = "Lighthouse CEO diagnostic access blocked.\n" export const REPORT_UNAVAILABLE_LINE = "Lighthouse CEO report unavailable; metrics_daily.errors may have been incremented.\n"; export const TOKEN_PROMPT = "Lighthouse report token: "; -const CEO_SCHEMA_URL = new URL("../contracts/ceo-v1/report.schema.json", import.meta.url); +const CEO_SCHEMA_URLS = Object.freeze({ + "1.1": new URL("../contracts/ceo-v1/report-1.1.schema.json", import.meta.url), + "1.2": new URL("../contracts/ceo-v1/report.schema.json", import.meta.url), +}); const REPORT_TOKEN_PATTERN = /^[A-Za-z0-9_-]{32,128}$/; const FAILURE_KIND = Object.freeze({ ACCESS_BLOCKED: "access_blocked", @@ -34,7 +37,7 @@ class DiagnosticFailure extends Error { } } -let reportValidator; +let reportValidators; function fail(kind = FAILURE_KIND.DIAGNOSTIC) { throw new DiagnosticFailure(kind); @@ -46,14 +49,16 @@ export function isUsableReportToken(token) { return bytes >= MIN_REPORT_TOKEN_BYTES && bytes <= MAX_REPORT_TOKEN_BYTES; } -function getReportValidator() { - if (reportValidator) return reportValidator; - - const schema = JSON.parse(readFileSync(CEO_SCHEMA_URL, "utf8")); - const ajv = new Ajv2020({ strict: true, allErrors: true }); - addFormats(ajv); - reportValidator = ajv.compile(schema); - return reportValidator; +function getReportValidator(version) { + if (!reportValidators) { + reportValidators = new Map(Object.entries(CEO_SCHEMA_URLS).map(([contractVersion, schemaUrl]) => { + const schema = JSON.parse(readFileSync(schemaUrl, "utf8")); + const ajv = new Ajv2020({ strict: true, allErrors: true }); + addFormats(ajv); + return [contractVersion, ajv.compile(schema)]; + })); + } + return reportValidators.get(version) ?? null; } export function takeEnvironmentToken(environment) { @@ -231,7 +236,8 @@ export async function fetchValidatedCeoReport({ if (text.includes(token)) fail(); const report = JSON.parse(text); - if (!getReportValidator()(report)) fail(); + const validator = getReportValidator(report?.report_contract_version); + if (!validator || !validator(report)) fail(); const output = `${JSON.stringify(report, null, 2)}\n`; if (output.includes(token)) fail(); diff --git a/src/index.ts b/src/index.ts index 5503113..0b48a45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -458,15 +458,17 @@ export type CeoWindowValues = Record; type CeoCoverage = Record; type CeoSourceReason = | null + | "activity_only" | "query_failed" | "binding_not_configured" | "probe_history_missing" + | "probe_history_incomplete" | "probe_data_stale" - | "source_history_missing" - | "source_data_stale"; + | "source_history_missing"; type CeoSourceState = { availability: "available" | "unavailable"; freshness: "fresh" | "stale" | "unknown"; + freshness_basis: "activity" | "scheduled_probe"; data_through: string | null; definition_start_day: string | null; coverage: CeoCoverage; @@ -501,7 +503,7 @@ type CeoMetricMap = { }; type CeoReportPayload = { view: "ceo"; - report_contract_version: "1.1"; + report_contract_version: "1.2"; metric_definition_version: "1.1"; report_id: string; generated_at: string; @@ -527,6 +529,11 @@ type CeoReportPayload = { versions_observed_last_30_complete_days: Array<{ version: string; count: number }> | null; recent_product_failures_by_name: Array<{ name: string; count: number }> | null; service_probes: Array<{ target: string; state: "pass" | "fail"; checked_at: string }> | null; + voluntary_inquiry_records: { + total_records: number; + last_created_at: string | null; + last_updated_at: string | null; + } | null; }; limitations: { artifact_transfer_completion_known: false; @@ -535,6 +542,9 @@ type CeoReportPayload = { download_interest_distinguishes_page_visit_from_file_click: true; download_interest_includes_pre_definition_history: false; product_telemetry_is_opt_in_only: true; + activity_recency_proves_producer_health: false; + voluntary_inquiries_are_lead_records: true; + existing_lead_updates_increment_inquiry_windows: false; }; }; type CloudflareGraphQLResponse = { @@ -3956,11 +3966,15 @@ export function buildCeoReportWindows(now: Date): { windows: CeoWindows; ranges: return { windows, ranges }; } -type GuardedCeoSource = +type GuardedCeoQuerySource = | { available: true; value: T } - | { available: false; reason: "query_failed" | "binding_not_configured" }; + | { available: false; reason: "query_failed" }; + +type GuardedCeoSource = + | GuardedCeoQuerySource + | { available: false; reason: "binding_not_configured" }; -async function guardCeoSource(name: string, run: () => Promise): Promise> { +async function guardCeoSource(name: string, run: () => Promise): Promise> { try { return { available: true, value: await run() }; } catch (error) { @@ -4004,9 +4018,9 @@ function selectCeoWindowValuesFromDefinition( function coverageForCeoSource(available: boolean): CeoCoverage { // These sources are sparse event/counter tables rather than a daily - // completeness ledger. A recent watermark proves freshness, not that every - // day inside a decision window was observable. Until a source provides an - // explicit completeness proof, its available windows remain partial. + // completeness ledger. A watermark proves only the latest observed activity, + // not producer health or that every day in a decision window was observable. + // Until a source provides explicit completeness proof, its windows stay partial. return Object.fromEntries( CEO_WINDOW_KEYS.map((key) => [key, available ? "partial" : "unavailable"]) ) as CeoCoverage; @@ -4019,17 +4033,18 @@ function partialCeoCoverage(): CeoCoverage { function normalizeCeoDataThrough(value: string | null, now: Date): string | null { if (!value) return null; if (/^\d{4}-\d{2}-\d{2}$/.test(value)) { - return value === utcDay(now) ? now.toISOString() : `${value}T23:59:59.999Z`; + const endOfDay = new Date(`${value}T23:59:59.999Z`); + if (!Number.isFinite(endOfDay.getTime())) return null; + return new Date(Math.min(endOfDay.getTime(), now.getTime())).toISOString(); } const parsed = new Date(value); if (!Number.isFinite(parsed.getTime())) return null; return new Date(Math.min(parsed.getTime(), now.getTime())).toISOString(); } -function directCeoSourceState( +function activityCeoSourceState( source: GuardedCeoSource, definitionStartDay: string, - ranges: CeoWindowRanges, now: Date, observedDataThrough: string | null ): CeoSourceState { @@ -4037,6 +4052,7 @@ function directCeoSourceState( return { availability: "unavailable", freshness: "unknown", + freshness_basis: "activity", data_through: null, definition_start_day: definitionStartDay, coverage: coverageForCeoSource(false), @@ -4048,20 +4064,21 @@ function directCeoSourceState( return { availability: "available", freshness: "unknown", + freshness_basis: "activity", data_through: null, definition_start_day: definitionStartDay, coverage: partialCeoCoverage(), reason_code: "source_history_missing", }; } - const stale = dataThrough.slice(0, 10) < ranges.latest_complete_day.end_day; return { availability: "available", - freshness: stale ? "stale" : "fresh", + freshness: "unknown", + freshness_basis: "activity", data_through: dataThrough, definition_start_day: definitionStartDay, coverage: coverageForCeoSource(true), - reason_code: stale ? "source_data_stale" : null, + reason_code: "activity_only", }; } @@ -4095,8 +4112,11 @@ function ceoMetricValue(row: Record | null, metric: string, key function earliestCeoDataThrough(...values: Array): string | null { const present = values.filter((value): value is string => Boolean(value)); + const comparable = (value: string): string => /^\d{4}-\d{2}-\d{2}$/.test(value) + ? `${value}T23:59:59.999Z` + : value; return present.length === values.length && present.length > 0 - ? present.reduce((earliest, value) => value < earliest ? value : earliest) + ? present.reduce((earliest, value) => comparable(value) < comparable(earliest) ? value : earliest) : null; } @@ -4311,6 +4331,11 @@ type CeoLeadAggregate = { windows: Record; sources: Array<{ source: CeoInquirySourceBucket; count: number }>; data_through: string | null; + records: { + total_records: number; + last_created_at: string | null; + last_updated_at: string | null; + }; }; async function queryCeoLeadAggregates(db: D1Database, ranges: CeoWindowRanges): Promise { @@ -4345,21 +4370,43 @@ async function queryCeoLeadAggregates(db: D1Database, ranges: CeoWindowRanges): ), bucketed AS ( SELECT created_day, created_at, ${bucketCase} AS source FROM normalized + ), + record_summary AS ( + SELECT COUNT(*) AS total_records, + MAX(created_at) AS last_created_at, + MAX(updated_at) AS last_updated_at + FROM early_access_leads ) - SELECT source, ${countColumns.join(", ")}, MAX(created_at) AS data_through + SELECT source, ${countColumns.join(", ")}, MAX(created_at) AS data_through, + NULL AS total_records, NULL AS last_created_at, NULL AS last_updated_at FROM bucketed CROSS JOIN bounds GROUP BY source UNION ALL - SELECT NULL, ${sentinelCounts.join(", ")}, (SELECT MAX(created_at) FROM early_access_leads)` + SELECT NULL, ${sentinelCounts.join(", ")}, last_created_at, + total_records, last_created_at, last_updated_at + FROM record_summary` ).bind(...ceoBoundsBindings(ranges)).all>(); const sourceCounts = new Map(); const windows = Object.fromEntries(CEO_WINDOW_KEYS.map((key) => [key, 0])) as Record; let dataThrough: string | null = null; + let records = { + total_records: 0, + last_created_at: null as string | null, + last_updated_at: null as string | null, + }; for (const row of result.results ?? []) { if (typeof row.data_through === "string" && (!dataThrough || row.data_through > dataThrough)) { dataThrough = row.data_through; } - if (typeof row.source !== "string") continue; + if (typeof row.source !== "string") { + const totalRecords = Number(row.total_records ?? 0); + records = { + total_records: Number.isSafeInteger(totalRecords) && totalRecords >= 0 ? totalRecords : 0, + last_created_at: typeof row.last_created_at === "string" ? row.last_created_at : null, + last_updated_at: typeof row.last_updated_at === "string" ? row.last_updated_at : null, + }; + continue; + } const source = (CEO_INQUIRY_SOURCE_BUCKET_SET.has(row.source) ? row.source : "other") as CeoInquirySourceBucket; for (const key of CEO_WINDOW_KEYS) windows[key] += Number(row[`count_${key}`] ?? 0); const count = Number(row.count_last_7_complete_days ?? 0); @@ -4370,7 +4417,7 @@ async function queryCeoLeadAggregates(db: D1Database, ranges: CeoWindowRanges): .filter((row) => row.count > 0) .sort((left, right) => (right.count - left.count) || left.source.localeCompare(right.source)) .slice(0, 10); - return { windows, sources, data_through: dataThrough }; + return { windows, sources, data_through: records.last_created_at ?? dataThrough, records }; } async function queryCeoLighthouseErrorWindows( @@ -4397,43 +4444,71 @@ function activeCeoProbeRows(rows: CeoProbeRow[]): CeoProbeRow[] { for (const row of rows) { if (!ACTIVE_HEALTH_CHECK_TARGETS.has(row.target)) continue; const current = latestByTarget.get(row.target); - if (!current || row.checked_at > current.checked_at) latestByTarget.set(row.target, row); + const newer = !current || row.checked_at > current.checked_at; + const tiedFailure = current !== undefined + && row.checked_at === current.checked_at + && Number(row.ok) < Number(current.ok); + if (newer || tiedFailure) latestByTarget.set(row.target, row); } return [...latestByTarget.values()].sort((left, right) => left.target.localeCompare(right.target)); } function probeCeoSourceState( - source: GuardedCeoSource>>, - ranges: CeoWindowRanges, + source: GuardedCeoQuerySource>>, now: Date ): CeoSourceState { const definitionStartDay = CEO_SOURCE_DEFINITION_START.service_probes; if (!source.available) { - return directCeoSourceState(source, definitionStartDay, ranges, now, null); + return { + availability: "unavailable", + freshness: "unknown", + freshness_basis: "scheduled_probe", + data_through: null, + definition_start_day: definitionStartDay, + coverage: coverageForCeoSource(false), + reason_code: "query_failed", + }; } const activeRows = activeCeoProbeRows(source.value); const observedTargets = new Set(activeRows.map((row) => row.target)); const hasCompleteProbeSet = [...ACTIVE_HEALTH_CHECK_TARGETS].every((target) => observedTargets.has(target)); - const dataThrough = activeRows.reduce( + const oldestDataThrough = activeRows.reduce( (oldest, row) => !oldest || row.checked_at < oldest ? row.checked_at : oldest, null ); - if (!hasCompleteProbeSet || !dataThrough) { + const latestDataThrough = activeRows.reduce( + (latest, row) => !latest || row.checked_at > latest ? row.checked_at : latest, + null + ); + if (activeRows.length === 0) { return { - availability: "unavailable", + availability: "available", freshness: "unknown", + freshness_basis: "scheduled_probe", data_through: null, definition_start_day: definitionStartDay, - coverage: coverageForCeoSource(false), + coverage: partialCeoCoverage(), reason_code: "probe_history_missing", }; } - const ageMs = now.getTime() - new Date(dataThrough).getTime(); + if (!hasCompleteProbeSet || !oldestDataThrough) { + return { + availability: "available", + freshness: "unknown", + freshness_basis: "scheduled_probe", + data_through: latestDataThrough, + definition_start_day: definitionStartDay, + coverage: partialCeoCoverage(), + reason_code: "probe_history_incomplete", + }; + } + const ageMs = now.getTime() - new Date(oldestDataThrough).getTime(); const stale = !Number.isFinite(ageMs) || ageMs > 36 * 60 * 60 * 1000; return { availability: "available", freshness: stale ? "stale" : "fresh", - data_through: dataThrough, + freshness_basis: "scheduled_probe", + data_through: oldestDataThrough, definition_start_day: definitionStartDay, coverage: coverageForCeoSource(true), reason_code: stale ? "probe_data_stale" : null, @@ -4483,7 +4558,7 @@ export async function buildCeoReport( : nullCeoWindowValues(); const probeRows = probes.available ? activeCeoProbeRows(probes.value) : []; - const probeState = probeCeoSourceState(probes, ranges, now); + const probeState = probeCeoSourceState(probes, now); const last30Product = product.available ? product.value.windows.last_30_complete_days : null; const recentProductWindows = product.available ? [product.value.windows.today, product.value.windows.latest_complete_day] @@ -4496,20 +4571,20 @@ export async function buildCeoReport( return { view: "ceo", - report_contract_version: "1.1", + report_contract_version: "1.2", metric_definition_version: "1.1", report_id: crypto.randomUUID(), generated_at: generatedAt, display_timezone: "America/Toronto", windows, sources: { - artifact_delivery: directCeoSourceState(artifact, CEO_SOURCE_DEFINITION_START.artifact_delivery, ranges, now, artifact.available ? artifact.value.data_through : null), - update_checks: directCeoSourceState(update, CEO_SOURCE_DEFINITION_START.update_checks, ranges, now, update.available ? update.value.data_through : null), - product_telemetry: directCeoSourceState(product, CEO_SOURCE_DEFINITION_START.product_telemetry, ranges, now, product.available ? product.value.data_through : null), - buscore_site: directCeoSourceState(buscoreSite, CEO_SOURCE_DEFINITION_START.buscore_site, ranges, now, buscoreSite.available ? buscoreSite.value.data_through : null), - tgc_site: directCeoSourceState(tgcSite, CEO_SOURCE_DEFINITION_START.tgc_site, ranges, now, tgcSite.available ? tgcSite.value.data_through : null), - voluntary_inquiries: directCeoSourceState(leads, CEO_SOURCE_DEFINITION_START.voluntary_inquiries, ranges, now, leads.available ? leads.value.data_through : null), - lighthouse_errors: directCeoSourceState(errors, CEO_SOURCE_DEFINITION_START.lighthouse_errors, ranges, now, errors.available ? errors.value.data_through : null), + artifact_delivery: activityCeoSourceState(artifact, CEO_SOURCE_DEFINITION_START.artifact_delivery, now, artifact.available ? artifact.value.data_through : null), + update_checks: activityCeoSourceState(update, CEO_SOURCE_DEFINITION_START.update_checks, now, update.available ? update.value.data_through : null), + product_telemetry: activityCeoSourceState(product, CEO_SOURCE_DEFINITION_START.product_telemetry, now, product.available ? product.value.data_through : null), + buscore_site: activityCeoSourceState(buscoreSite, CEO_SOURCE_DEFINITION_START.buscore_site, now, buscoreSite.available ? buscoreSite.value.data_through : null), + tgc_site: activityCeoSourceState(tgcSite, CEO_SOURCE_DEFINITION_START.tgc_site, now, tgcSite.available ? tgcSite.value.data_through : null), + voluntary_inquiries: activityCeoSourceState(leads, CEO_SOURCE_DEFINITION_START.voluntary_inquiries, now, leads.available ? leads.value.data_through : null), + lighthouse_errors: activityCeoSourceState(errors, CEO_SOURCE_DEFINITION_START.lighthouse_errors, now, errors.available ? errors.value.data_through : null), service_probes: probeState, }, bus_core: { @@ -4567,6 +4642,13 @@ export async function buildCeoReport( checked_at: row.checked_at, })) : null, + voluntary_inquiry_records: leads.available + ? { + total_records: leads.value.records.total_records, + last_created_at: normalizeCeoDataThrough(leads.value.records.last_created_at, now), + last_updated_at: normalizeCeoDataThrough(leads.value.records.last_updated_at, now), + } + : null, }, limitations: { artifact_transfer_completion_known: false, @@ -4575,6 +4657,9 @@ export async function buildCeoReport( download_interest_distinguishes_page_visit_from_file_click: true, download_interest_includes_pre_definition_history: false, product_telemetry_is_opt_in_only: true, + activity_recency_proves_producer_health: false, + voluntary_inquiries_are_lead_records: true, + existing_lead_updates_increment_inquiry_windows: false, }, }; } @@ -5845,7 +5930,7 @@ async function queryHealthLatestPerTarget(db: D1Database): Promise< > { const rows = await db .prepare( - "SELECT h.target, h.ok, h.status_code, h.latency_ms, h.checked_at, h.note FROM health_checks h JOIN (SELECT target, MAX(checked_at) AS max_checked FROM health_checks GROUP BY target) latest ON h.target = latest.target AND h.checked_at = latest.max_checked ORDER BY h.target ASC" + "SELECT h.target, h.ok, h.status_code, h.latency_ms, h.checked_at, h.note FROM health_checks h WHERE h.id = (SELECT candidate.id FROM health_checks candidate WHERE candidate.target = h.target ORDER BY candidate.checked_at DESC, candidate.ok ASC, candidate.id ASC LIMIT 1) ORDER BY h.target ASC" ) .all<{ target: string; ok: number; status_code: number | null; latency_ms: number | null; checked_at: string; note: string | null }>(); return rows.results ?? []; diff --git a/tests/ceo-report.test.mjs b/tests/ceo-report.test.mjs index 821ed44..e643c8f 100644 --- a/tests/ceo-report.test.mjs +++ b/tests/ceo-report.test.mjs @@ -74,7 +74,14 @@ function productAggregateRow({ zero, watermark, includeProductFailures }) { } function defaultLeadRows({ zero, watermark }) { - if (zero) return [{ source: null, data_through: null }]; + if (zero) return [{ + source: null, + data_through: null, + total_records: 0, + last_created_at: null, + last_updated_at: null, + }]; + const observedAt = `${watermark}T12:00:00.000Z`; return [{ source: "reddit", count_today: 0, @@ -82,8 +89,14 @@ function defaultLeadRows({ zero, watermark }) { count_last_7_complete_days: 2, count_previous_7_complete_days: 0, count_last_30_complete_days: 2, - data_through: `${watermark}T12:00:00.000Z`, - }, { source: null, data_through: `${watermark}T12:00:00.000Z` }]; + data_through: observedAt, + }, { + source: null, + data_through: observedAt, + total_records: 2, + last_created_at: observedAt, + last_updated_at: observedAt, + }]; } function makeCeoDb({ @@ -232,7 +245,7 @@ test("CEO report routing is additive and protected", async () => { assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.view, "ceo"); - assert.equal(payload.report_contract_version, "1.1"); + assert.equal(payload.report_contract_version, "1.2"); assert.equal(payload.metric_definition_version, "1.1"); const wrongMethod = await worker.fetch( @@ -303,12 +316,26 @@ test("CEO metrics use literal sources, page_view only, exact lead rows, and all assert.equal(report.business.tgc_consented_page_views.latest_complete_day, 4); assert.equal(report.business.voluntary_inquiries.latest_complete_day, 2); assert.deepEqual(report.business.inquiry_sources_last_7_complete_days, [{ source: "reddit", count: 2 }]); - assert.equal(report.details.service_probes, null, "an incomplete probe source has no believable detail list"); + assert.deepEqual(report.details.service_probes?.map((row) => row.target), ["manifest", "release_artifact"]); + assert.equal(report.sources.service_probes.availability, "available"); + assert.equal(report.sources.service_probes.freshness_basis, "scheduled_probe"); + assert.equal(report.sources.service_probes.reason_code, "probe_history_incomplete"); + assert.deepEqual(report.details.voluntary_inquiry_records, { + total_records: 2, + last_created_at: "2026-08-08T12:00:00.000Z", + last_updated_at: "2026-08-08T12:00:00.000Z", + }); + assert.equal(report.sources.tgc_site.freshness_basis, "activity"); + assert.equal(report.sources.tgc_site.freshness, "unknown"); + assert.equal(report.sources.tgc_site.reason_code, "activity_only"); assert.equal(report.sources.artifact_delivery.coverage.today, "partial"); assert.equal(report.sources.artifact_delivery.coverage.latest_complete_day, "partial"); assert.equal(report.sources.artifact_delivery.coverage.last_30_complete_days, "partial"); assert.equal(report.limitations.download_interest_distinguishes_page_visit_from_file_click, true); assert.equal(report.limitations.download_interest_includes_pre_definition_history, false); + assert.equal(report.limitations.activity_recency_proves_producer_health, false); + assert.equal(report.limitations.voluntary_inquiries_are_lead_records, true); + assert.equal(report.limitations.existing_lead_updates_increment_inquiry_windows, false); assert.equal(report.sources.buscore_site.definition_start_day, TRUSTED_ARTIFACT_CLICK_START_DAY); assert.equal(report.sources.buscore_site.data_through, null); assert.ok(sqlLog.some((sql) => sql.includes("event_name = 'page_view'"))); @@ -342,7 +369,7 @@ test("trusted artifact-click intent starts at cutover without relabeling histori previous_7_complete_days: null, last_30_complete_days: 1, }); - assert.equal(spanningReport.sources.buscore_site.data_through, "2026-08-11T23:59:59.999Z"); + assert.equal(spanningReport.sources.buscore_site.data_through, "2026-08-11T12:00:00.000Z"); assert.equal(spanningReport.sources.buscore_site.coverage.last_7_complete_days, "partial"); const intentSql = cutoverSql.find((sql) => sql.includes("buscore_download_intent_daily")); @@ -350,6 +377,27 @@ test("trusted artifact-click intent starts at cutover without relabeling histori assert.match(intentSql, /AND day >= '2026-08-10'/); }); +test("composite watermarks compare date-only components at end-of-day semantics", async () => { + const db = makeCeoDb({ watermark: "2026-08-11" }); + const report = await buildCeoReport(db, db, new Date("2026-08-12T12:00:00.000Z")); + + assert.equal( + report.sources.buscore_site.data_through, + "2026-08-11T12:00:00.000Z", + "an exact noon observation is earlier than the same daily watermark's eventual end of day" + ); +}); + +test("activity watermarks never extend beyond report generation", async () => { + const now = new Date("2026-08-12T12:00:00.000Z"); + const db = makeCeoDb({ watermark: "2026-08-13" }); + const report = await buildCeoReport(db, db, now); + + assert.equal(report.sources.artifact_delivery.data_through, now.toISOString()); + assert.equal(report.sources.update_checks.data_through, now.toISOString()); + assert.equal(report.sources.product_telemetry.data_through, now.toISOString()); +}); + test("CEO report stays far below D1 Free query and simultaneous connection limits", async () => { const tracker = { count: 0, active: 0, peak: 0 }; const db = makeCeoDb({ tracker }); @@ -376,6 +424,18 @@ test("inquiry attribution is a fixed privacy bucket and never a raw label", asyn count_last_30_complete_days: 1, data_through: `2026-08-08T12:00:0${index}.000Z`, })); + leadRows.push({ + source: null, + count_today: 0, + count_latest_complete_day: 0, + count_last_7_complete_days: 0, + count_previous_7_complete_days: 0, + count_last_30_complete_days: 0, + data_through: "2026-08-08T12:00:02.000Z", + total_records: 3, + last_created_at: "2026-08-08T12:00:02.000Z", + last_updated_at: "2026-08-08T21:00:00.000Z", + }); const sqlLog = []; const db = makeCeoDb({ leadRows, sqlLog }); const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); @@ -383,20 +443,80 @@ test("inquiry attribution is a fixed privacy bucket and never a raw label", asyn assert.deepEqual(report.business.inquiry_sources_last_7_complete_days, [{ source: "other", count: 3 }]); assert.equal(report.business.voluntary_inquiries.last_7_complete_days, 3); + assert.deepEqual(report.details.voluntary_inquiry_records, { + total_records: 3, + last_created_at: "2026-08-08T12:00:02.000Z", + last_updated_at: "2026-08-08T21:00:00.000Z", + }); + assert.equal(report.sources.voluntary_inquiries.data_through, "2026-08-08T12:00:02.000Z"); + assert.equal( + report.details.voluntary_inquiry_records.last_created_at, + report.sources.voluntary_inquiries.data_through, + "the aggregate record watermark and source activity watermark must stay identical" + ); for (const raw of rawLabels) assert.equal(serialized.includes(raw), false); const leadSql = sqlLog.find((sql) => sql.includes("FROM early_access_leads")); assert.match(leadSql, /CASE .* ELSE 'other' END/); assert.match(leadSql, /GROUP BY source/); + assert.match(leadSql, /COUNT\(\*\) AS total_records/); + assert.match(leadSql, /MAX\(created_at\) AS last_created_at/); + assert.match(leadSql, /MAX\(updated_at\) AS last_updated_at/); }); -test("service probes remain explicitly incomplete until every active target has run", async () => { +test("partial service-probe history remains available and returns only observed evidence", async () => { const db = makeCeoDb(); const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); - assert.equal(report.sources.service_probes.availability, "unavailable"); + assert.equal(report.sources.service_probes.availability, "available"); assert.equal(report.sources.service_probes.freshness, "unknown"); + assert.equal(report.sources.service_probes.freshness_basis, "scheduled_probe"); + assert.equal(report.sources.service_probes.data_through, "2026-08-08T12:00:00.000Z"); + assert.equal(report.sources.service_probes.reason_code, "probe_history_incomplete"); + assert.equal(report.sources.service_probes.coverage.today, "partial"); + assert.deepEqual(report.details.service_probes?.map((row) => row.target), ["manifest", "release_artifact"]); +}); + +test("exact service-probe timestamp ties deterministically keep the failing result", async () => { + const checkedAt = "2026-08-08T12:00:00.000Z"; + const pass = { target: "manifest", ok: 1, status_code: 200, latency_ms: 2, checked_at: checkedAt, note: null }; + const fail = { target: "manifest", ok: 0, status_code: 500, latency_ms: 3, checked_at: checkedAt, note: "failed" }; + + for (const healthRows of [[pass, fail], [fail, pass]]) { + const sqlLog = []; + const db = makeCeoDb({ healthRows, sqlLog }); + const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); + + assert.deepEqual(report.details.service_probes, [ + { target: "manifest", state: "fail", checked_at: checkedAt }, + ]); + assert.ok(sqlLog.some((sql) => ( + sql.includes("ORDER BY candidate.checked_at DESC, candidate.ok ASC, candidate.id ASC LIMIT 1") + ))); + } +}); + +test("successful empty service-probe history is available without invented detail", async () => { + const db = makeCeoDb({ healthRows: [] }); + const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); + + assert.equal(report.sources.service_probes.availability, "available"); + assert.equal(report.sources.service_probes.freshness, "unknown"); + assert.equal(report.sources.service_probes.freshness_basis, "scheduled_probe"); assert.equal(report.sources.service_probes.data_through, null); assert.equal(report.sources.service_probes.reason_code, "probe_history_missing"); + assert.equal(report.sources.service_probes.coverage.today, "partial"); + assert.deepEqual(report.details.service_probes, []); +}); + +test("a service-probe query failure remains unavailable with no detail", async () => { + const db = makeCeoDb({ failPattern: "FROM health_checks" }); + const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); + + assert.equal(report.sources.service_probes.availability, "unavailable"); + assert.equal(report.sources.service_probes.freshness, "unknown"); + assert.equal(report.sources.service_probes.freshness_basis, "scheduled_probe"); + assert.equal(report.sources.service_probes.data_through, null); + assert.equal(report.sources.service_probes.reason_code, "query_failed"); assert.equal(report.sources.service_probes.coverage.today, "unavailable"); assert.equal(report.details.service_probes, null); }); @@ -415,6 +535,7 @@ test("service probe freshness uses the oldest required target watermark", async assert.equal(report.sources.service_probes.availability, "available"); assert.equal(report.sources.service_probes.freshness, "stale"); + assert.equal(report.sources.service_probes.freshness_basis, "scheduled_probe"); assert.equal(report.sources.service_probes.data_through, "2026-08-06T00:00:00.000Z"); assert.equal(report.sources.service_probes.reason_code, "probe_data_stale"); assert.equal(report.sources.service_probes.coverage.today, "partial"); @@ -440,19 +561,21 @@ test("successful empty queries are zero while an unavailable source is null", as assert.equal(partial.bus_core.site_page_views.latest_complete_day, 3, "other sources stay available"); }); -test("an old direct-source watermark fails closed as stale without erasing observed counts", async () => { +test("direct-source watermarks remain activity evidence without claiming producer freshness", async () => { const sqlLog = []; const db = makeCeoDb({ watermark: "2026-08-01", sqlLog }); const report = await buildCeoReport(db, db, new Date("2026-08-08T22:00:00.000Z")); assert.equal(report.sources.artifact_delivery.availability, "available"); - assert.equal(report.sources.artifact_delivery.freshness, "stale"); + assert.equal(report.sources.artifact_delivery.freshness, "unknown"); + assert.equal(report.sources.artifact_delivery.freshness_basis, "activity"); assert.equal(report.sources.artifact_delivery.data_through, "2026-08-01T23:59:59.999Z"); - assert.equal(report.sources.artifact_delivery.reason_code, "source_data_stale"); + assert.equal(report.sources.artifact_delivery.reason_code, "activity_only"); assert.equal(report.bus_core.full_artifact_responses_offered.latest_complete_day, 104); for (const source of ["update_checks", "tgc_site", "lighthouse_errors"]) { - assert.equal(report.sources[source].freshness, "stale", `${source} must retain an all-history watermark`); - assert.equal(report.sources[source].reason_code, "source_data_stale"); + assert.equal(report.sources[source].freshness, "unknown", `${source} must not infer producer health from activity`); + assert.equal(report.sources[source].freshness_basis, "activity"); + assert.equal(report.sources[source].reason_code, "activity_only"); } assert.equal(report.sources.buscore_site.freshness, "unknown", "pre-cutover intent cannot become a trusted watermark"); assert.equal(report.sources.buscore_site.data_through, null); @@ -467,9 +590,11 @@ test("missing voluntary inquiry binding is explicit and never rendered as zero", const db = makeCeoDb(); const report = await buildCeoReport(db, undefined, new Date("2026-08-08T22:00:00.000Z")); assert.equal(report.sources.voluntary_inquiries.availability, "unavailable"); + assert.equal(report.sources.voluntary_inquiries.freshness_basis, "activity"); assert.equal(report.sources.voluntary_inquiries.reason_code, "binding_not_configured"); assert.equal(report.business.voluntary_inquiries.latest_complete_day, null); assert.equal(report.business.inquiry_sources_last_7_complete_days, null); + assert.equal(report.details.voluntary_inquiry_records, null); }); test("strict CEO schema accepts every fixture and representative live producer state", async () => { @@ -479,11 +604,17 @@ test("strict CEO schema accepts every fixture and representative live producer s addFormats(ajv); const validate = ajv.compile(schema); const fixtureNames = readdirSync(contractDir) - .filter((name) => name.endsWith(".json") && name !== "report.schema.json") + .filter((name) => name.endsWith(".json") && !name.endsWith(".schema.json")) .sort(); for (const name of fixtureNames) { assertSchemaValid(JSON.parse(readFileSync(new URL(name, contractDir), "utf8")), validate, `fixture ${name}`); } + const rollbackSchema = JSON.parse(readFileSync(new URL("report-1.1.schema.json", contractDir), "utf8")); + const rollbackAjv = new Ajv2020({ strict: true, allErrors: true }); + addFormats(rollbackAjv); + const validateRollback = rollbackAjv.compile(rollbackSchema); + const rollbackFixture = JSON.parse(readFileSync(new URL("compat-1.1/healthy-zero.json", contractDir), "utf8")); + assertSchemaValid(rollbackFixture, validateRollback, "rollback fixture 1.1"); const now = new Date("2026-08-08T22:00:00.000Z"); const currentDb = makeCeoDb({ includeProductFailures: false }); @@ -532,8 +663,40 @@ test("strict CEO schema accepts every fixture and representative live producer s unavailablePartial.sources.artifact_delivery.coverage.today = "partial"; const unavailableStaleReason = structuredClone(runtimeReports[4][1]); unavailableStaleReason.sources.artifact_delivery.reason_code = "source_data_stale"; + const directWithProbeBasis = structuredClone(base); + directWithProbeBasis.sources.artifact_delivery.freshness_basis = "scheduled_probe"; + const probeWithActivityBasis = structuredClone(base); + probeWithActivityBasis.sources.service_probes.freshness_basis = "activity"; + const freshProbeWithEmptyDetails = structuredClone(base); + freshProbeWithEmptyDetails.sources.service_probes.freshness = "fresh"; + freshProbeWithEmptyDetails.sources.service_probes.reason_code = null; + freshProbeWithEmptyDetails.details.service_probes = []; + const missingProbeWithDetails = structuredClone(runtimeReports[0][1]); + missingProbeWithDetails.details.service_probes = structuredClone(base.details.service_probes); + const incompleteProbeWithCompleteDetails = structuredClone(base); + incompleteProbeWithCompleteDetails.details.service_probes = [ + "site_home", + "site_downloads", + "manifest", + "release_artifact", + "lead_endpoint", + "github_release", + ].map((target) => ({ target, state: "pass", checked_at: "2026-08-08T12:00:00.000Z" })); + const duplicateProbeTarget = structuredClone(base); + duplicateProbeTarget.details.service_probes[1] = { + ...duplicateProbeTarget.details.service_probes[1], + target: duplicateProbeTarget.details.service_probes[0].target, + checked_at: "2026-08-08T12:00:01.000Z", + }; + const zeroLeadRecordsWithTimestamps = structuredClone(runtimeReports[0][1]); + zeroLeadRecordsWithTimestamps.details.voluntary_inquiry_records.last_created_at = "2026-08-08T12:00:00.000Z"; + zeroLeadRecordsWithTimestamps.details.voluntary_inquiry_records.last_updated_at = "2026-08-08T12:00:00.000Z"; + const nonzeroLeadRecordsWithNullTimestamp = structuredClone(base); + nonzeroLeadRecordsWithNullTimestamp.details.voluntary_inquiry_records.last_updated_at = null; const unavailableLeadDetails = structuredClone(runtimeReports[2][1]); unavailableLeadDetails.business.inquiry_sources_last_7_complete_days = []; + const unavailableLeadCount = structuredClone(runtimeReports[2][1]); + unavailableLeadCount.business.voluntary_inquiries.today = 1; const productSourceFailure = await buildCeoReport( makeCeoDb({ failPattern: "FROM buscore_product_events_daily" }), makeCeoDb(), @@ -557,11 +720,22 @@ test("strict CEO schema accepts every fixture and representative live producer s ["unknown full coverage", unknownFull], ["unavailable partial coverage", unavailablePartial], ["unavailable stale reason", unavailableStaleReason], + ["direct source with probe basis", directWithProbeBasis], + ["probe source with activity basis", probeWithActivityBasis], + ["fresh probe with empty details", freshProbeWithEmptyDetails], + ["missing probe with observed details", missingProbeWithDetails], + ["incomplete probe with complete details", incompleteProbeWithCompleteDetails], + ["duplicate probe target", duplicateProbeTarget], + ["zero lead records with timestamps", zeroLeadRecordsWithTimestamps], + ["nonzero lead records with null timestamp", nonzeroLeadRecordsWithNullTimestamp], ["unavailable lead details", unavailableLeadDetails], + ["unavailable lead source with observed count", unavailableLeadCount], ["unavailable product details", unavailableProductDetails], ]) { assert.equal(validate(invalid), false, `${label} must fail strict schema validation`); } + assert.equal(validate(rollbackFixture), false, "1.1 shape must fail 1.2 validation"); + assert.equal(validateRollback(base), false, "1.2 shape must fail 1.1 validation"); }); test("health checks exercise only non-counted public manifest HEAD and artifact HEAD routes", async () => { diff --git a/tests/release-control.test.mjs b/tests/release-control.test.mjs index fa2e462..4fa1c96 100644 --- a/tests/release-control.test.mjs +++ b/tests/release-control.test.mjs @@ -54,9 +54,11 @@ test("operator scripts expose reads and upload without a direct production bypas }); test("the current governed bundle and historical release-control receipt stay synchronized", () => { - assert.equal(packageJson.version, "1.30.0"); + assert.equal(packageJson.version, "1.31.0"); assert.equal(packageLock.version, packageJson.version); assert.equal(packageLock.packages[""].version, packageJson.version); + assert.match(sot, /^## CEO activity truth and sparse probe health — v1\.31\.0$/m); + assert.match(changelog, /^## \[1\.31\.0\] - 2026-08-27$/m); assert.match(sot, /^## Least-privilege report diagnostics — v1\.30\.0$/m); assert.match(changelog, /^## \[1\.30\.0\] - 2026-08-26$/m); assert.match(sot, /^## Release-control and infrastructure reconciliation — v1\.29\.4$/m); @@ -66,6 +68,9 @@ test("the current governed bundle and historical release-control receipt stay sy /\*\*External release-control verification — 2026-08-26:\*\*[\s\S]*Deploy command[\s\S]*Version command[\s\S]*exactly `npx wrangler versions upload`/ ); assert.match(sot, /Workers Builds may now create version or preview state[\s\S]*must not promote active traffic/); - assert.match(sot, /`CLOUDFLARE_API_TOKEN` remain unverified/); + assert.match( + sot, + /release run `33086080869`[\s\S]*proved[\s\S]*approved 1\.30\.0 deployment[\s\S]*current availability or broader scope must not be inferred/ + ); assert.doesNotMatch(sot, /production command (?:remains|still uses|still runs) `npx wrangler deploy`/i); }); diff --git a/tests/report-auth.test.mjs b/tests/report-auth.test.mjs index d76edb4..3e4df94 100644 --- a/tests/report-auth.test.mjs +++ b/tests/report-auth.test.mjs @@ -71,7 +71,7 @@ async function assertUnauthorized(response) { assert.deepEqual(await response.json(), { ok: false, error: "unauthorized" }); } -test("GET /report accepts the exact report-read token without changing the report contract", async () => { +test("GET /report accepts the exact report-read token for the current report contract", async () => { const { db, calls } = makeDb(); const { ctx } = makeContext(); const response = await worker.fetch( @@ -85,7 +85,7 @@ test("GET /report accepts the exact report-read token without changing the repor assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.view, "ceo"); - assert.equal(payload.report_contract_version, "1.1"); + assert.equal(payload.report_contract_version, "1.2"); assert.ok(calls.prepare > 0, "an authorized report reaches stored-data reads"); assert.equal(calls.run, 0, "the stored-data CEO view performs no writes"); assert.equal(response.headers.get("Access-Control-Allow-Headers"), "Content-Type"); diff --git a/tests/report-helper.test.mjs b/tests/report-helper.test.mjs index 74cf2cb..db4b5c1 100644 --- a/tests/report-helper.test.mjs +++ b/tests/report-helper.test.mjs @@ -41,6 +41,10 @@ const fixtureText = readFileSync( "utf8", ); const fixture = JSON.parse(fixtureText); +const rollbackFixtureText = readFileSync( + new URL("../contracts/ceo-v1/compat-1.1/healthy-zero.json", import.meta.url), + "utf8", +); function writer({ isTTY = false } = {}) { const chunks = []; @@ -128,6 +132,21 @@ test("automation credential is deleted before one fixed validated GET reaches st assert.equal(`${stdout.text}${stderr.text}`.includes(secret), false); }); +test("helper strictly accepts the preserved 1.1 rollback contract", async () => { + const stdout = writer(); + const stderr = writer(); + const exitCode = await runDiagnostic({ + environment: { [REPORT_TOKEN_ENV]: VALID_REPORT_TOKEN }, + stdout, + stderr, + fetchImpl: async () => successfulResponse(rollbackFixtureText), + }); + + assert.equal(exitCode, 0); + assert.equal(JSON.parse(stdout.text).report_contract_version, "1.1"); + assert.equal(stderr.text, ""); +}); + test("hidden TTY prompt does not echo the interactive credential", async () => { class FakeTty extends EventEmitter { constructor() {