Skip to content
Back to skills

Monitoring Weekly

ASecurity

Use when: kullanıcı "weekly check", "haftalık monitoring", "drift check + GSC anomaly + budget burn", "weekly health report", "haftalık sağlık raporu", "haftalık monitoring özeti" der ya da /pseo-monitoring-weekly çağırır; week range filter events.jsonl + drift-check output reuse + GSC week-over-week delta + budget burn rate aggregation → outputs/reports/{date}-monitoring-weekly.md. Wave-1a scope (truth-in-advertising): drift + portfolio health snapshot ACTIVE; GSC weekly anomaly ACTIVE (medi...

  • 27 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 2, 2026
ai-agentspythongoapiperformancedocumentation

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 2, 2026

npx -y skills add popiliadam/platinum-seo-engine --skill monitoring-weekly --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Monitoring Weekly?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Monitoring Weekly
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/popiliadam-monitoring-weekly/badge)](https://www.skillsdirectory.com/skills/popiliadam-monitoring-weekly)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: monitoring-weekly
description: |
  Use when: kullanıcı "weekly check", "haftalık monitoring", "drift check
  + GSC anomaly + budget burn", "weekly health report", "haftalık sağlık
  raporu", "haftalık monitoring özeti" der ya da /pseo-monitoring-weekly
  çağırır; week range filter events.jsonl + drift-check output reuse +
  GSC week-over-week delta + budget burn rate aggregation →
  outputs/reports/{date}-monitoring-weekly.md.
  Wave-1a scope (truth-in-advertising): drift + portfolio health
  snapshot ACTIVE; GSC weekly anomaly ACTIVE (median+MAD modified-z,
  R-141, sourced from _state/metrics/gsc-weekly.jsonl — NOT master.xlsx);
  ONLY budget-burn (cost.credits) aggregation remains a PLACEHOLDER
  deferred to Phase 14+.
  Also use when: cron scheduled weekly run her Pazartesi 09:00 UTC
  report-only mode; reporting suite extension Phase 9 8 reporting skill
  no-write paterni reuse; Foundational Principle 1 truth-verifiable
  enforcement audit-only (Wave-1a inline: consistency-report.json +
  portfolio.json + _state/metrics/gsc-weekly.jsonl kaynaklı; master.xlsx
  read YOK, sadece budget-burn Phase 14+ scope;
  fabrikasyon yok); manager portfolio-overview öncesi tek-proje haftalık
  sağlık özeti istediğinde.
  Do not use when: ad-hoc daily check (out of scope, daily skill ayrı —
  bu skill rolling 7-day window aggregator); events.jsonl empty week
  range (DURUR #1 SKIP, no report write); GSC api auth fail (out of
  scope, gsc-bootstrap skill önce çalışmalı); template path missing →
  inline fallback (DURUR #4 AMBER, manuel inceleme); MAD anomali
  severity=RED ise CRITICAL escalation (DURUR #5 — ikinci audit satırı,
  median+MAD modified-z R-141; ≥6 tam ISO hafta yoksa insufficient_history
  dürüst render, alarm değil); master.xlsx WRITE talep edilirse
  YASAK (Phase 9 reporting paterni: 8 skill no-write invariant).
version: "1.0"
status: wip
category: reporting
inputs:
  week_start:
    type: string
    required: true
    description: "YYYY-MM-DD format week start date (inclusive). Window kapsamı: week_start..week_end. Geçersiz format → DURUR validation hata."
  week_end:
    type: string
    required: false
    default: "today"
    description: "YYYY-MM-DD format week end (inclusive). Default 'today' (run-time date.today() ile çözülür). week_start sonrası olmalı."
outputs:
  - "_state/events.jsonl"
  - "outputs/reports/{date}-monitoring-weekly.md"
consumes:
  - "drift-check:_state/cache/consistency-report.json"
  - "init-project:shared/portfolio.json"
  - "templates/reports/monitoring-weekly.template.md"
  - "gsc-pull:_state/metrics/gsc-weekly.jsonl"
produces: []
triggers:
  manual: ["/pseo-monitoring-weekly"]
  natural_language: |
    "weekly check", "haftalık monitoring", "drift check + GSC anomaly +
    budget burn", "weekly health report", "haftalık sağlık raporu",
    "monitoring weekly", "haftalık health check"
  hooks: []
  scheduled:
    - cron: "0 9 * * 1"
      mode: "report-only"
mcp_tools:
  required: []
  optional: []
budget:
  uses_paid_mcp: false
  estimated_credits: 0
autonomy:
  confidence: HIGH
  requires_approval: false
  safe_auto_execute: true
---

# monitoring-weekly — reporting skill (Phase 12 Wave 2, W-G6)

Weekly health check aggregator. **Wave-1a inline scope** (what the
runtime actually reads): drift-check output
(`_state/cache/consistency-report.json`) + project health
(`shared/portfolio.json`) + the GSC weekly anomaly ledger
(`_state/metrics/gsc-weekly.jsonl`, written by gsc-pull Step 7b), then
renders `outputs/reports/{date}-monitoring-weekly.md`. **No MCP**, **no
DFS**, **no master.xlsx WRITE** — strict read-only.

**GSC anomaly is ACTIVE (GAP-M4 / R-141).** It uses the robust
median + MAD modified z-score (NIST/Iglewicz–Hoaglin, |M| ≥ 3.5) with
percent + absolute floors over ≥6 complete ISO weeks, replacing the old
5-standard-deviation / trailing-8-week-mean placeholder (which was both
statistically meaningless at n≈8 and unimplementable — the
`master.xlsx[gsc_performance]` sheet is a recent-vs-previous SNAPSHOT
with **no date column**, so it never held a weekly series; the
`_state/metrics/gsc-weekly.jsonl` ledger does). An anomaly whose week
overlaps a Google Ranking-update window (`google-update-calendar.json`,
R-137) is capped at AMBER with an attribution caution. Detection is
imported from `scripts.reporting.weekly_anomaly` + `scripts.reporting.update_calendar`.

*Still deferred to Phase 14+ (PLACEHOLDER string, NOT an active read):*
budget burn rate (events.jsonl `cost.credits` aggregation per day vs
`project-config.budget_credits_per_day` baseline). The inline runtime
does **not** open `master.xlsx` or `project-config` — it reads the
consistency-report + portfolio + weekly-ledger snapshots above.

This skill follows the Phase 9 reporting **convention authority**
established by `skills/reporting/weekly-summary/SKILL.md` (W-E2 — local
aggregation, READ-ONLY contract on `master.xlsx`) and
`skills/reporting/portfolio-overview/SKILL.md` (W-E5 — multi-project
no-write aggregator). The 8 Phase 9 reporting skills share the
no-write invariant — `transaction.append`, `transaction.update`,
`transaction.delete` and `wb.save` MUST NOT appear anywhere in
production paths. The test suite enforces this with a grep sentinel
(the call-site syntax with the trailing `(` paren is the actual
forbidden token; prose mentions without parens are allowed).

## Schema-First Override (Lesson 7+23+31, W-G1+W-G2+W-G3+W-G4 paterni)

The brief sketched `event_kind=audit + event_type=monitoring_completed`.
Per `schemas/events.schema.json`:

1. `event_type` is **WORK-only** (closed 10-value enum):
   `content_new, content_revise, content_remove, tech_fix,
   quickwin_applied, pillar_launch, schema_fix, redirect_deployed,
   backlink_outreach, manual` (F-8 invariant). `monitoring_completed`
   is NOT in the enum.
2. `event_kind=audit` requires `audit_action` + `audit_target` +
   `actor` (allOf if/then conditional, lines per schema). It does
   **not** carry `event_type`.

Therefore the worker writes:
- `event_kind=audit`
- `audit_action="accessed"` (enum: created/modified/deleted/accessed/
  permission_changed/config_changed)
- `audit_target="reports:monitoring-weekly:{week_start}_{week_end}"`
- `actor="agent:monitoring-weekly"`
- `schema_version="1.0"`, `event_id`, `timestamp`, `project_id`
  (envelope required across all kinds).

This mirrors the Phase 5 `governance/drift-check` pattern
(`audit_action="accessed"`, `audit_target="invariants:25"`,
`actor="drift-check"`) — drift-check's audit-only convention is the
authority this skill reuses. Diverges from W-G1 (indexing-ping)
which used `event_kind=work + event_type=manual + indexing_ping
sub-object`; that path requires a `task_id` mint and is reserved for
work-bearing skills. monitoring-weekly does **no work** — it only
reads + reports — so the `audit` kind is the semantic-correct choice.

## Inputs (frontmatter contract)

| Name         | Type   | Default | Notes                                                       |
|--------------|--------|---------|-------------------------------------------------------------|
| `week_start` | string | —       | Required. YYYY-MM-DD inclusive window start.                |
| `week_end`   | string | today   | Inclusive window end. Default = run-time `date.today()`.    |

Both inputs are validated as ISO-8601 dates before any I/O. Invalid
format raises `MonitoringWeeklyValidationError` (DURUR #0 envelope).

## Outputs (artifacts produced — 2 entries, master.xlsx ABSENT)

- `_state/events.jsonl` — single-row audit append (or 2 rows if
  DURUR #5 MAD anomaly severity=RED fires; the second row carries
  `audit_action="accessed"` + `audit_target=...:anomaly` + a `notes`
  field flagging metric/direction/modified-z; severity is encoded
  report-side, not via a schema field).
- `outputs/reports/{date}-monitoring-weekly.md` — human-readable
  health check report with 5 sections (exec_summary, drift_section,
  gsc_anomaly_section, budget_burn_section, escalations).

`master.xlsx` is **NOT** in `outputs[]` — Phase 9 8-reporting-skill
no-write paterni reuse (cf. `weekly-summary` outputs[0]
`master.xlsx#none` confirm marker; this skill instead omits the entry
because R-WAVE-2 brief requires only 2 outputs).

## DURUR (no fall-back) conditions — 5 sentinels

Stop and surface to the manager — do not patch, do not silently
swallow.

1. **DURUR #1 — events.jsonl empty week range** → SKIP.
   When the events.jsonl filter for the window returns 0 rows, the
   skill writes a single `severity=info` audit event with
   `audit_action="accessed"` + `audit_target="reports:monitoring-
   weekly:{week_start}_{week_end}:empty"` and **does NOT write the
   markdown report**. Report path resolves to a sentinel "empty window"
   marker that the test suite asserts.

2. **DURUR #2 — `budget_credits_per_day` absent in project-config**
   → AMBER + default 500 credits/day fallback.
   **Wave-3 inline scope: PLACEHOLDER (deferred Phase 14+).** The inline
   runtime does not read `project-config` today, so this fallback is not
   yet wired. *When implemented:* the report's `budget_burn_section`
   carries the AMBER badge and a note that the baseline came from the
   hard-coded fallback.

3. **DURUR #3 — drift-check output unavailable** (events filter on
   `event_kind=audit AND audit_target ~ invariants:*` returns empty
   in the window) → AMBER + report empty section.
   The `drift_section` shows "drift-check henüz çalıştırılmamış" and
   the report severity escalates to AMBER.

4. **DURUR #4 — template path
   `templates/reports/monitoring-weekly.template.md` not on disk** →
   AMBER + inline render fallback. The skill embeds an inline template
   string as a defensive fallback (the body of this SKILL.md carries
   the inline template definition under "Inline Template Fallback"
   section below). The report renders, and the audit event records
   the fallback path was used.

5. **DURUR #5 — MAD GSC anomaly severity=RED** → CRITICAL escalation.
   **Wave-1a inline scope: ACTIVE (GAP-M4 / R-141).** Block 3 reads the
   `_state/metrics/gsc-weekly.jsonl` ledger and runs
   `scripts.reporting.weekly_anomaly.detect()` — the robust
   **median + MAD modified z-score** (NIST/Iglewicz–Hoaglin,
   `M = 0.6745·(x − median)/MAD`, flagged at `|M| ≥ 3.5`) gated by
   percent + absolute floors over a trailing window of complete ISO
   weeks. A flagged worsening of `clicks` / `impressions` / `avg_position`
   yields severity **RED**; a `ctr` drop yields **AMBER**; an improvement
   direction is **INFO**. When the anomaly severity is **RED**, the report's
   `escalations` section is populated AND a **second** `append_audit` row is
   written (`audit_target="reports:monitoring-weekly:{week}:anomaly"`,
   `notes` carrying metric / direction / `modified_z`).

   **Calendar guard (R-137):** if the week overlaps a Google *Ranking*
   update window (`google-update-calendar.json`, incl. a 7-day settling
   buffer), the anomaly is capped at **AMBER** with an
   `attribution_caution` — Google's own guidance is not to attribute
   movement during/just after a rollout, so it is NOT escalated to a
   second-row CRITICAL.

   **Insufficient history is NOT a DURUR.** With fewer than 6 complete
   ISO weeks the detector returns `insufficient_history` and the section
   renders the honest `points_used/6` string — no alarm, no second row,
   severity unchanged from the drift/portfolio rollup. (The discredited
   standard-deviation-on-n≈8 placeholder this replaces is described in the intro.)

## Workflow (8 step)

1. **Read project-config** for `budget_credits_per_day` baseline + GSC
   `siteUrl`. Missing key → DURUR #2 (default 500). **Wave-3 inline
   scope: PLACEHOLDER (deferred Phase 14+)** — the inline runtime does
   not open `project-config`; this step lands with the budget-burn
   capability.
2. **Read events.jsonl** with week range filter
   (`week_start..week_end` inclusive). 0 rows → DURUR #1 SKIP.
3. **drift-check output reuse** — filter events on
   `event_kind=audit AND audit_target startswith "invariants:"`.
   0 rows in window → DURUR #3 AMBER.
4. **GSC anomaly detect** — **Wave-1a inline scope: ACTIVE (GAP-M4 /
   R-141).** Block 3 reads `_state/metrics/gsc-weekly.jsonl` (the
   gsc-pull Step 7b ledger), derives `current_iso_week` from the
   validated `week_start`, loads the Ranking-update calendar
   (`google-update-calendar.json` engine seed ∪ `shared/cache/...`
   workspace overlay) and computes the week's overlaps, then calls
   `weekly_anomaly.detect(records, current_iso_week, update_overlaps=...)`.
   Severity rolls up `max(drift/portfolio severity, anomaly severity)`.
   RED → DURUR #5. Missing/sparse ledger → `insufficient_history` honest
   render (no crash, no alarm).
5. **Budget burn rate** — **Wave-3 inline scope: PLACEHOLDER (deferred
   Phase 14+).** The current inline runtime reports a recent-events count
   from `portfolio.json`, not a computed burn rate. *When implemented:*
   sum `cost.credits` from events.jsonl in window per day and compute the
   `daily_mean / budget_credits_per_day` ratio (ratio > 1.0 → AMBER
   badge; > 2.0 → RED badge).
6. **Report render** — `templates/reports/monitoring-weekly.template.md`
   via `string.Template` `$var` substitution (Phase 1 mirası
   `scripts/reporting/render_template.py` paterni reuse). Missing
   template → DURUR #4 inline fallback.
7. **NO master.xlsx update** — read-only aggregation. Phase 9 8
   reporting skill no-write paterni reuse. The transform module never
   imports `scripts.excel.transaction` and never calls `wb.save`.
8. **events.jsonl append** — single audit row (or 2 if DURUR #5
   fires). Schema-first override applied per the section above.

## Inline Orchestration (Phase B Wave 3 — Q-V1.2-MONITORING-WEEKLY-MISSING-SCRIPT-01 RESOLVED)

Skill body inline transform paterni (option b — thin orchestration, no
`scripts/reporting/monitoring_weekly.py` subprocess). 3 Python block
sequential helper exec compatible (rules/skills.md Section 1 enforce —
1. block sys.path marker; rules/skills.md Section 3 multi-line format).

### Block 1 — Setup + Read drift-check Output

```python
import os
import sys
import json
from pathlib import Path
from datetime import datetime, timezone
from string import Template

sys.path.insert(0, os.getcwd())

# Entrypoint variables (orchestrator injects at runtime; plugin-agnostik —
# no project slug literals in skill body per F-16 invariant).
project_slug = os.environ.get("PSEO_PROJECT_ID")
if not project_slug:
    print("PSEO_PROJECT_ID env var required — orchestrator-injected at runtime")
    sys.exit(0)
week_start = os.environ.get("MONITORING_WEEK_START", "")
week_end = os.environ.get("MONITORING_WEEK_END", "")
workspace_root_env = os.environ.get("PSEO_WORKSPACE_ROOT")
if workspace_root_env:
    workspace_root = Path(workspace_root_env)
else:
    workspace_root = Path.home() / "Documents" / "platinum-seo-workspace"

REPO_ROOT = Path.cwd()

# Read drift-check consistency-report.json (DURUR #3 fallback if missing)
consistency_path = (
    workspace_root / "projects" / project_slug / "_state" / "cache" / "consistency-report.json"
)
if consistency_path.exists():
    drift_data = json.loads(consistency_path.read_text(encoding="utf-8"))
    red_count = drift_data.get("red_count", drift_data.get("fail_count", 0))
    amber_count = drift_data.get("amber_count", 0)
    green_count = drift_data.get("green_count", drift_data.get("pass_count", 0))
    drift_verdict = drift_data.get("verdict", "UNKNOWN")
    drift_available = True
else:
    # DURUR #3 — drift-check henüz çalıştırılmamış
    red_count = amber_count = green_count = 0
    drift_verdict = "AMBER (drift-check henüz çalıştırılmamış)"
    drift_available = False
```

### Block 2 — Read portfolio.json + Aggregate Per-Project Metrics

```python
# Read shared/portfolio.json — cross-project metrics aggregation
portfolio_path = workspace_root / "shared" / "portfolio.json"
if portfolio_path.exists():
    portfolio_data = json.loads(portfolio_path.read_text(encoding="utf-8"))
    projects = portfolio_data.get("projects", [])
    project_entry = next(
        (p for p in projects if p.get("slug") == project_slug),
        None,
    )
    if project_entry:
        completion_pct = float(project_entry.get("completion_percentage", 0.0))
        active_oq_count = int(project_entry.get("active_oq_count", 0))
        recent_events = int(project_entry.get("recent_events_count_7day", 0))
        portfolio_available = True
    else:
        completion_pct = 0.0
        active_oq_count = 0
        recent_events = 0
        portfolio_available = False
else:
    # DURUR fallback — portfolio.json yoksa health snapshot incomplete
    completion_pct = 0.0
    active_oq_count = 0
    recent_events = 0
    portfolio_available = False

# Compute overall severity from drift + portfolio signals
if red_count >= 5 or not drift_available:
    severity = "RED" if red_count >= 5 else "AMBER"
elif amber_count > 0:
    severity = "AMBER"
else:
    severity = "GREEN"
```

### Block 3 — Emit Audit Event + Render Markdown Report

```python
# Render markdown via templates/reports/monitoring-weekly.template.md
# DURUR #4 inline fallback if template missing.
template_path = REPO_ROOT / "templates" / "reports" / "monitoring-weekly.template.md"
INLINE_TEMPLATE = (
    "---\n"
    "project_id: $project_slug\n"
    "period_start: $week_start\n"
    "period_end: $week_end\n"
    "generated_at: $generated_at\n"
    "report_kind: monitoring_weekly\n"
    "severity: $severity\n"
    "---\n\n"
    "# Monitoring Weekly — $project_slug\n\n"
    "**Pencere:** $week_start – $week_end\n"
    "**Genel severity:** $severity\n\n"
    "## Exec Summary\n\n$exec_summary\n\n"
    "## Drift Section\n\n$drift_section\n\n"
    "## GSC Anomaly Section\n\n$gsc_anomaly_section\n\n"
    "## Budget Burn Section\n\n$budget_burn_section\n\n"
    "## Escalations\n\n$escalations\n"
)

if template_path.exists():
    template_str = template_path.read_text(encoding="utf-8")
else:
    template_str = INLINE_TEMPLATE  # DURUR #4

generated_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d")

# --- GSC weekly anomaly (GAP-M4 / R-141): median+MAD over the ledger ---
from scripts.reporting import weekly_anomaly, update_calendar
from datetime import date as _date

ledger_path = (
    workspace_root / "projects" / project_slug / "_state" / "metrics" / "gsc-weekly.jsonl"
)
ledger_records = []
if ledger_path.exists():
    for _line in ledger_path.read_text(encoding="utf-8").splitlines():
        _line = _line.strip()
        if not _line:
            continue
        try:
            ledger_records.append(json.loads(_line))
        except json.JSONDecodeError:
            continue

try:
    _iso = _date.fromisoformat(week_start[:10]).isocalendar()
    current_iso_week = f"{_iso[0]}-W{_iso[1]:02d}"
except (ValueError, TypeError):
    current_iso_week = ""

# Ranking-update calendar overlap (R-137): engine seed ∪ workspace overlay.
engine_cal = REPO_ROOT / "google-update-calendar.json"
overlay_cal = workspace_root / "shared" / "cache" / "google-update-calendar.json"
update_overlaps = None
if engine_cal.exists() and week_start and week_end:
    try:
        _cal = update_calendar.load_calendar(
            engine_cal, overlay_cal if overlay_cal.exists() else None
        )
        update_overlaps = update_calendar.overlaps(week_start, week_end, _cal)
    except (OSError, ValueError, KeyError):
        update_overlaps = None

anomaly = weekly_anomaly.detect(
    ledger_records, current_iso_week, update_overlaps=update_overlaps
)
anomaly_sev = anomaly.get("severity", "none")


def _sev_rank(s):
    return {"GREEN": 0, "none": 0, "INFO": 0, "AMBER": 2, "RED": 3}.get(s, 0)


# Severity rollup: max(drift/portfolio severity, GSC anomaly severity).
if _sev_rank(anomaly_sev) > _sev_rank(severity):
    severity = anomaly_sev

exec_summary = (
    f"Haftalık sağlık özeti: drift={red_count}R/{amber_count}A/{green_count}G "
    f"(verdict={drift_verdict}); completion={completion_pct:.1f}%; "
    f"active_oq={active_oq_count}; recent_events_7day={recent_events}; "
    f"gsc_anomaly={anomaly['status']}/{anomaly_sev}."
)
drift_section = (
    f"Drift counts (consistency-report.json): RED={red_count} "
    f"AMBER={amber_count} GREEN={green_count}. "
    f"Verdict: {drift_verdict}. Available: {drift_available}."
)
if anomaly["status"] == "insufficient_history":
    gsc_anomaly_section = (
        "GSC anomali tespiti (median+MAD modified-z, R-141): yetersiz geçmiş — "
        f"{anomaly['points_used']}/{anomaly['needed']} tam ISO hafta (≥6 gerekir). "
        "Ledger biriktikçe aktifleşir (cold-start: gsc-pull days_back=112)."
    )
elif anomaly["status"] == "current_week_missing":
    gsc_anomaly_section = (
        f"GSC anomali tespiti: {current_iso_week} ledger'da yok — gsc-pull bu "
        "hafta için weekly ledger satırı yazmadı."
    )
else:
    _cells = []
    for _m in ("clicks", "impressions", "ctr", "avg_position"):
        _md = anomaly["metrics"].get(_m)
        if not _md:
            continue
        _cells.append(
            f"{_m}={_md['value']} (medyan {_md['median']}, z={_md['modified_z']}, "
            f"bayrak={'EVET' if _md['flagged'] else 'hayır'}, yön={_md['direction']})"
        )
    _names = anomaly.get("anomalies") or "yok"
    gsc_anomaly_section = (
        f"GSC anomali (median+MAD, {anomaly['points_used']} hafta baz, R-141): "
        f"severity={anomaly_sev}, anomaliler={_names}. " + " | ".join(_cells)
    )
    if anomaly.get("attribution_caution"):
        gsc_anomaly_section += " ⚠ " + anomaly["attribution_caution"]
budget_burn_section = (
    f"Recent events 7-day count: {recent_events} (portfolio.json available={portfolio_available}). "
    "Budget burn rate calc Phase 14+ scope (events.jsonl cost.credits aggregation)."
)
_esc = []
if anomaly.get("severity") == "RED":
    _det = "; ".join(
        f"{_m} {anomaly['metrics'][_m]['direction']} "
        f"(modified_z={anomaly['metrics'][_m]['modified_z']})"
        for _m in anomaly.get("anomalies", [])
    )
    _esc.append(
        f"CRITICAL (DURUR #5): GSC anomali RED — {_det} — ikinci audit satırı yazıldı."
    )
if anomaly.get("attribution_caution"):
    _esc.append("Dikkat: " + anomaly["attribution_caution"])
if not _esc:
    _esc.append(f"Eskalasyon yok. Genel severity={severity}.")
escalations = " ".join(_esc)

variables = {
    "project_slug": project_slug,
    "week_start": week_start,
    "week_end": week_end,
    "generated_at": generated_at,
    "severity": severity,
    "exec_summary": exec_summary,
    "drift_section": drift_section,
    "gsc_anomaly_section": gsc_anomaly_section,
    "budget_burn_section": budget_burn_section,
    "escalations": escalations,
}

rendered = Template(template_str).safe_substitute(variables)

# Write markdown report
report_dir = workspace_root / "projects" / project_slug / "outputs" / "reports"
report_dir.mkdir(parents=True, exist_ok=True)
report_path = report_dir / f"{date_str}-monitoring-weekly.md"
report_path.write_text(rendered, encoding="utf-8")

# Emit audit event (event_kind=audit, no event_type per Section 6 disambiguation)
from scripts.state import events_writer

events_writer.append_audit(
    project_id=project_slug,
    audit_action="accessed",
    audit_target=f"reports:monitoring-weekly:{week_start}_{week_end}",
    actor="agent:monitoring-weekly",
    workspace_root=workspace_root,
    notes=(
        f"weekly_monitoring severity={severity} "
        f"red={red_count} amber={amber_count} green={green_count} "
        f"completion={completion_pct:.1f}% active_oq={active_oq_count} "
        f"drift_available={drift_available} portfolio_available={portfolio_available}"
    ),
)

# DURUR #5: a SECOND audit row when the GSC anomaly severity is RED (an overlap
# cap to AMBER deliberately suppresses this — overlapping movement is not a
# CRITICAL engine regression, R-137).
if anomaly.get("severity") == "RED":
    _anote = "; ".join(
        f"{_m}:{anomaly['metrics'][_m]['direction']}:"
        f"z={anomaly['metrics'][_m]['modified_z']}"
        for _m in anomaly.get("anomalies", [])
    )
    events_writer.append_audit(
        project_id=project_slug,
        audit_action="accessed",
        audit_target=f"reports:monitoring-weekly:{week_start}_{week_end}:anomaly",
        actor="agent:monitoring-weekly",
        workspace_root=workspace_root,
        notes=f"gsc_anomaly RED {_anote}",
    )

print(f"monitoring-weekly report written: {report_path}")
print(f"audit event appended: project={project_slug} severity={severity}")
```

### Schema-First Compliance

- `event_kind=audit` — `event_type` field YASAK (rules/events-writer.md
  Section 4c + Section 6).
- `audit_action="accessed"` — read-only access trail (drift-check
  doğum belgesi paterni reuse: Phase 5 governance audit kind).
- `audit_target` namespace `reports:monitoring-weekly:{week}` —
  cross-project audit query selectability.
- `events_writer.append_audit` convenience wrapper — envelope
  auto-populate (schema_version + event_id + timestamp + project_id);
  bare `append(event)` API YASAK (rules/events-writer.md Section 2 +
  Phase B Wave 1 fix).

## Foundational Principles Enforcement (3-Layer)

The 3 üst-prensip (Phase 10 `rules/content-quality.md#foundational-
principles`) gate this skill end-to-end. No alt-rule (R-01..R-148)
overrides them.

### Principle 1 — Truth-Verifiable Health Report

Every datum in the report is sourced from a tangible artifact. **Wave-1a
inline scope** — what the runtime actually reads today:
- `drift_section` ← `_state/cache/consistency-report.json`
  (drift-check output: red/amber/green counts + verdict).
- `exec_summary` ← computed roll-up of the drift counts +
  `shared/portfolio.json` health fields (completion %, active OQ
  count, recent-events count); no free-form fabrication.
- `gsc_anomaly_section` ← `_state/metrics/gsc-weekly.jsonl` (the
  gsc-pull Step 7b ledger) run through `weekly_anomaly.detect()`
  (median + MAD modified-z, R-141) + the Ranking-update calendar
  (`google-update-calendar.json`, R-137). With <6 complete weeks it
  renders the honest `insufficient_history` string — never a fabricated
  delta. The discredited standard-deviation-on-n≈8 placeholder is gone.
- `escalations` ← computed from the anomaly result (RED ⇒ a second
  audit row; calendar overlap ⇒ AMBER cap + attribution caution).

**ONLY still deferred to Phase 14+ (PLACEHOLDER string):**
- `budget_burn_section` ← *will* aggregate events.jsonl `cost.credits`
  (Phase 7+8 work events). Today it reports a recent-events count from
  `portfolio.json`.

The skill never invents values; the GSC section reports computed
robust statistics or honestly announces insufficient history. AI
suistimal yasağı: hayali delta fabrikasyonu YASAK — the GSC path reads
the ledger, never guesses.

### Principle 2 — Profile-Aware Severity

The severity thresholds adapt to `project-config.profile`:
- `ymyl` (Your Money Your Life): drift-check FAIL → CRITICAL
  escalation immediately (compliance burst alert; one FAIL is one
  too many for medical/legal/financial sites).
- `e-commerce`: GSC clicks delta < -20% week-over-week → AMBER
  (conversion-loss signal); drift-check FAIL → AMBER.
- `b2b-saas`: GSC impressions delta < -30% AND signup_velocity
  proxy (events.jsonl `event_type=content_new` count) drop → AMBER
  (top-of-funnel signal).
- `local-service` / `portfolio`: drift-check FAIL only →
  AMBER (lower volume, lower stakes).

The 5-value `profile` enum lives in
`schemas/project-config.schema.json` (Phase 11 W-A1 v1.2 cascade
authority): `e-commerce`, `ymyl`, `local-service`, `b2b-saas`,
`portfolio`.

### Principle 3 — Anti-Cheap-Content (no fabrication)

The skill renders ONLY counts + factual deltas. It does NOT generate
prose summaries via LLM, does NOT invent week-over-week percentages,
and does NOT guess at root causes. The `exec_summary` is a
rule-based 1-2 sentence Turkish rollup composed from the section
counts (template `string.Template` substitution).

## Inline Template Fallback (DURUR #4)

When `templates/reports/monitoring-weekly.template.md` is absent, the
transform falls back to an inline template string defined in **Block 3
of this SKILL.md** (the `INLINE_TEMPLATE` constant inside the inline
orchestration block; see line ~315). The fallback template carries the
same 5 sections + same `$var` slots as the on-disk template. The audit
event records `template_path=inline` in the `note` field. The test suite
asserts both render paths produce structurally equivalent output.

(Q-V1.2-MONITORING-WEEKLY-MISSING-SCRIPT-01 RESOLVED via option b —
no separate `scripts/reporting/monitoring_weekly.py` exists; the
constant lives inline alongside the orchestration blocks.)

## Cross-references

- Schemas: `schemas/events.schema.json` (audit event_kind required
  fields: audit_action + audit_target + actor),
  `schemas/master-excel.schema.json` (`gsc_performance` sheet
  read-only access), `schemas/skill-frontmatter.schema.json` (this
  frontmatter).
- Cross-modules (READ-only, IMPORT pattern):
  `scripts/reporting/render_template.py` (`string.Template` $var
  rendering reuse).
- Reference SKILL.md: `skills/reporting/weekly-summary/SKILL.md`
  (Phase 9 weekly aggregation paterni — local aggregator + READ-ONLY
  contract on master.xlsx) and
  `skills/reporting/portfolio-overview/SKILL.md` (Phase 9 8
  reporting skill no-write paterni — strict read-only aggregator).
- Reference governance skill: `skills/governance/drift-check/SKILL.md`
  (Phase 5 audit event_kind paterni: `audit_action=accessed +
  audit_target=invariants:25 + actor=drift-check`).
- Template: `templates/reports/monitoring-weekly.template.md`
  (DURUR #4 fallback to inline string if absent).
- Tests: `tests/skills/test_monitoring_weekly.py`.

## Discipline checklist

- [x] TODO/fallback YASAK — every DURUR raises a typed exception or
      writes a sentinel audit event; no silent swallow.
- [x] Schema-first — frontmatter validates against
      `schemas/skill-frontmatter.schema.json` (Draft 7); audit event
      validates against `schemas/events.schema.json` allOf if/then
      conditional for `event_kind=audit`.
- [x] Plugin-agnostik — no slug literals in transform; no hardcoded
      `.mcp.json` writes; F-16 MCP boundary intact.
- [x] ADR-013 — `Use when` / `Also use when` / `Do not use when`
      are STRING content inside `description`, not separate fields.
- [x] Append-only state — this skill writes ONLY to
      `outputs/reports/` + `_state/events.jsonl` (audit append).
      NEVER mutates `master.xlsx`.
- [x] Phase 9 no-write paterni — no `transaction.append`,
      `transaction.update`, `transaction.delete`, `wb.save`
      tokens in production paths (test enforces grep sentinel; the
      forbidden call-site is the workbook save method invocation
      with trailing paren — prose mentions without parens are
      allowed for documentation discussion).
- [x] Forbidden tokens (3): grep CLEAN of the per-call/per-url
      credit field tokens (Phase 7 schema-field uydurma marker) +
      hardcoded project slugs (test enforces).
- [x] natural_language sentinel: ≥30 char (test enforces).
- [x] .mcp.json byte unchanged (Phase 11 F-16 invariant — test
      enforces SHA-256 stability across the worker run).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.