From 09d44687e74120e08579c7e87dee3a682a47a6e0 Mon Sep 17 00:00:00 2001 From: Rachael Rose Renk <91027132+rachaelrenk@users.noreply.github.com> Date: Thu, 6 Aug 2026 12:46:38 -0600 Subject: [PATCH 1/3] docs(skills): reduce automation noise and consolidate drafting improvements Recurring docs agents were producing more PRs and Slack messages than the team could absorb. Three systemic causes, plus a batch of GitBook-era migration artifacts that left several skills unable to run as written. Shared conventions (skill-authoring-guidelines.md): - Add "One standing PR per automation": stable branch and title, look before creating, add to the existing PR rather than opening another. - Invert "Slack notifications" to actionable-only. The old rule required posting on every run and was the direct cause of the channel noise. Its silent-failure rationale is preserved by requiring a run log instead. - Rewrite "Log availability" so outer loops read the log branch rather than main, and never merge the standing log PR as a workflow step. Cron correctness: - `0 17 1-7 * 1` is not "first Monday". Cron ORs day-of-month with day-of-week, so it fired ~11 times a month and produced four conflicting PRs in six days. Replace with `0 17 * * 1` plus an in-skill first-week guard in improve-drafting-skills, improve-aeo-crosslink-skill, and improve-404-monitor-skill. PR reuse applied to: improve-drafting-skills, weekly-404-monitor, afdocs-fix, sync-error-docs, sync_terminology, sync-openapi-spec, improve-aeo-crosslink-skill, improve-404-monitor-skill. update-changelog keeps one PR per release (correct) but now detects stacked release PRs. Slack volume: aeo_crosslink_audit no longer posts on no-change runs; weekly-404-monitor gates on threshold and folds its Phase 2 results into a single message instead of two; afdocs-audit posts only on regression or a blocked audit, backed by a new run log for the baseline. Migration artifacts: a find-and-replace during the GitBook-to-Astro move substituted descriptions into file paths. sync-error-docs referenced `astro.config.mjs (sidebar config)` and `vercel.json (redirects)` as real paths, had an invalid grep, and still called the GitBook API - it could not have succeeded. Also corrected the sidebar location to src/sidebar.ts, dropped the GITBOOK_TOKEN dependency, and fixed dead `.warp/references/terminology.md` paths in four skills. Consolidates PRs #450, #454, #468, and #484, which all edited draft_docs/SKILL.md and conflicted with each other. Overlapping patterns were merged rather than stacked, and PR #468's frontmatter-description edits were dropped as already superseded on main. Co-Authored-By: Warp Agent --- .agents/logs/afdocs_audit_runs.md | 26 ++++ .../references/skill-authoring-guidelines.md | 57 ++++++++- .agents/skills/aeo_crosslink_audit/SKILL.md | 21 ++-- .agents/skills/afdocs-audit/SKILL.md | 74 +++++++++-- .agents/skills/afdocs-fix/SKILL.md | 27 +++- .agents/skills/answer_question/SKILL.md | 4 +- .../skills/check_for_broken_links/SKILL.md | 10 +- .agents/skills/create_pr/SKILL.md | 48 ++++--- .agents/skills/draft_docs/SKILL.md | 71 +++++++++-- .../skills/improve-404-monitor-skill/SKILL.md | 71 ++++++++--- .../improve-aeo-crosslink-skill/SKILL.md | 99 ++++++++------- .../skills/improve-drafting-skills/SKILL.md | 118 +++++++++++++++--- .agents/skills/review-docs-pr/SKILL.md | 2 +- .agents/skills/sync-error-docs/SKILL.md | 109 ++++++++++------ .../references/redirect-patterns.md | 92 +++++++------- .agents/skills/sync-openapi-spec/SKILL.md | 23 +++- .agents/skills/sync_terminology/SKILL.md | 27 ++-- .agents/skills/triage-issue-local/SKILL.md | 2 +- .agents/skills/update-changelog/SKILL.md | 26 +++- .agents/skills/weekly-404-monitor/SKILL.md | 59 ++++++--- .agents/templates/conceptual.md | 9 +- .agents/templates/feature-doc.md | 32 ++++- .agents/templates/reference.md | 4 +- 23 files changed, 737 insertions(+), 274 deletions(-) create mode 100644 .agents/logs/afdocs_audit_runs.md diff --git a/.agents/logs/afdocs_audit_runs.md b/.agents/logs/afdocs_audit_runs.md new file mode 100644 index 000000000..dfbc75354 --- /dev/null +++ b/.agents/logs/afdocs_audit_runs.md @@ -0,0 +1,26 @@ +# AFDocs audit run log + +Written by the `afdocs-audit` skill on every scheduled run — clean, regressed, or blocked. + +This log serves two purposes: + +1. **Regression baseline.** The skill compares each run's score and failing-check set against the most recent entry marked `valid`. Entries marked `blocked` are skipped for comparison: a Vercel Firewall challenge makes every check a false positive, so its score is an artifact rather than a measurement. +2. **Proof a quiet run happened.** The skill only posts to Slack on a regression or a blocked audit, so a clean run is silent. This log is what distinguishes "ran, nothing to report" from "did not run." + +Newest entries first. Prepend, do not append. + +Entry format: + +```markdown +## YYYY-MM-DD — [valid | blocked] +- **Score**: N/100 (grade) +- **Checks**: N total — N pass, N fail, N warn +- **Failing check ids**: comma-separated list, or "none" +- **Allowlisted**: N +- **Oz run**: [URL] +- **Notes**: [anything unusual] +``` + +For a `blocked` run, omit the score line rather than recording the meaningless value. + +--- diff --git a/.agents/references/skill-authoring-guidelines.md b/.agents/references/skill-authoring-guidelines.md index eab3fbcf5..d39e8f29b 100644 --- a/.agents/references/skill-authoring-guidelines.md +++ b/.agents/references/skill-authoring-guidelines.md @@ -37,6 +37,31 @@ Reviewers should merge the log PR periodically so entries reach `main` and becom **Keep the log branch separate from content PRs.** Never write log updates and skill/content edits in the same commit or branch. +### One standing PR per automation + +**The second biggest failure mode: PR stacking.** A skill that mints a new date-suffixed branch on every run accumulates one open PR per run. Because recurring skills tend to edit the same small set of files, those PRs conflict with each other and none of them can be merged cleanly. The `improve-drafting-skills` agent produced four mutually-conflicting open PRs in six days this way, every one of them editing `draft_docs/SKILL.md`. + +The log-branch pattern above already solves this for logs. Generalize it to content PRs: **an automation has at most one open PR at any time.** + +**Required pattern for all PR-opening skills:** + +1. **Use a stable branch name with no date suffix** — `docs/`, not `docs/-2026-08-06`. +2. **Use a stable PR title with no date.** The run date belongs in a dated section of the PR body, not the title. A date in the title defeats title-based lookup and guarantees a new PR on every run. +3. **Look before creating:** + ```bash + gh pr list --repo warpdotdev/docs --state open \ + --search ' in:title' --json number,headRefName + ``` +4. **If an open PR exists**, add to it rather than opening another: + - Check out its branch and rebase on the latest `origin/main`. + - Apply this run's edits, commit, and push. + - Append a new dated section to the PR body. Fetch the current body and make a minimal additive edit — never regenerate it wholesale (see "Outer loop PR body integrity"). + - Re-run `check_pr_body.py` after the edit. +5. **If no open PR exists**, create the stable branch from the latest `origin/main` and open a draft PR. +6. **Never leave two open PRs for the same automation.** If a stale or superseded one is found, close it with an explanatory comment before opening a replacement. + +This keeps a run's work reviewable without letting unreviewed work pile up, and it means a missed review cycle costs one stale PR rather than one per run. + ### Verifying log writes explicitly Agents often proceed past a failed file write without noticing. For any log update step, verify explicitly: @@ -107,7 +132,23 @@ Always use `SLACK_BOT_TOKEN` and other secrets from environment variables — ne ### Slack notifications -Post a Slack notification on every run, including no-action runs and stale-snapshot exits. A missing notification on a no-action run is indistinguishable from a run that silently failed. Use a simple text message (not Block Kit) that can be scanned in under 30 seconds. +**Post only when the run produced something a human needs to act on.** Recurring agents that post unconditionally train the channel to ignore them, which costs more than a missed notification does. + +Actionable means one of: + +- A PR was created or received new commits. +- A threshold was crossed (broken links found, significant 404 gaps, a score regression). +- The agent hit a failure that stopped it from completing — including stale-snapshot exits and blocked audits. These are failures, not no-ops, and they always post. + +Everything else is silent. A no-change or no-op run writes to the run output and its run log, and posts nothing. + +**Silence means "ran, nothing to do."** An earlier version of this guidance required posting on every run, reasoning that a silent no-action run is indistinguishable from a run that silently failed. That concern is real, but Slack is the wrong place to solve it: the run log records every run including no-ops, and Oz lifecycle events surface failed and errored runs directly. Between them, a quiet run is distinguishable from a broken one without spending a notification. + +The corollary is a hard requirement: **a skill may only adopt the quiet default if it also writes a run log on every run.** If it does not log, it has no other way to prove it ran, and it should post. + +**Never post twice for one run.** If a skill has multiple phases, fold the later phase's results into the single message rather than posting a follow-up. + +Use a simple text message (not Block Kit) that can be scanned in under 30 seconds. --- @@ -123,15 +164,19 @@ This minimum must be stated explicitly in the skill's `## Schedule` section so t ### Log availability -The outer loop reads the inner loop's log from `main`. For entries to be available, the inner loop's standing log PR must be merged into `main` before the outer loop runs. Document this as a prerequisite: +**Read the log from the log branch, not from `main`.** The inner loop writes every entry to `chore/-log` and only reaches `main` when a human merges the standing PR. An outer loop that reads `main` therefore sees a truncated history whose staleness depends on review cadence — and silently analyzes fewer entries than it thinks it has. -```markdown -## Prerequisites +Read from the branch, which always holds the complete history: -- The standing log PR (`chore: run log`) merged into `main` so the entries are present there. - If it is unmerged, merge it first (or read the log from the `chore/-log` branch) before analyzing. +```bash +git fetch origin chore/-log +git checkout origin/chore/-log -- .agents/logs/.md ``` +Treat `main` as the convenience case only — if the PR happens to have been merged, the branch and `main` agree, and the branch read is still correct. + +**Do not make merging the standing log PR a step in the outer loop.** Merging is a human housekeeping task, not a precondition for analysis. An outer loop that tries to merge its own input couples the run to a repo write it may not have permission to perform, and turns an unmerged PR into a hard failure instead of a non-event. + ### Security boundary for signal logs Outer loops read logs that contain untrusted content: human review comments, PR descriptions, run output from external contributors. Apply these rules before using any log content to propose skill edits: diff --git a/.agents/skills/aeo_crosslink_audit/SKILL.md b/.agents/skills/aeo_crosslink_audit/SKILL.md index c22a59c79..8a4151abb 100644 --- a/.agents/skills/aeo_crosslink_audit/SKILL.md +++ b/.agents/skills/aeo_crosslink_audit/SKILL.md @@ -70,7 +70,7 @@ If Google Search Console data is unavailable, say what could not be verified and 4. **Make only safe edits.** Add links with minimal surrounding copy changes. Preserve the existing page structure and voice. Follow the link quality rules below when choosing anchor text and surrounding context. 5. **Run self-review.** Apply the quality gates in this skill before opening a PR or writing a no-change report. 6. **Deduplicate, re-validate, then open a PR or report no changes.** - - **Deduplicate first.** Check for an existing open AEO cross-link PR before opening one: `gh pr list --repo warpdotdev/docs --search 'docs: add AEO cross-links in:title' --state open`. Never leave two open AEO cross-link PRs. If one already exists, either skip this run (note it in the run output) or, if the existing PR is stale or superseded, close it with an explanatory comment before opening the new one. + - **Deduplicate first.** This skill follows the "One standing PR per automation" contract in `.agents/references/skill-authoring-guidelines.md`. Check for an existing open AEO cross-link PR before opening one: `gh pr list --repo warpdotdev/docs --search 'docs: add AEO cross-links in:title' --state open`. Never leave two open AEO cross-link PRs. If one already exists, prefer adding this run's links to it — check out its branch, rebase on the latest `origin/main`, apply the new links, push, and append them to the existing PR body under its existing headings. Skip the run only when the new links duplicate what the open PR already proposes. - **Re-validate against the latest `main`.** Fetch `origin/main` and confirm every edited file still exists at its path and every link target resolves to a current page (see "Self-review before opening a PR"). If a restructure moved your targets, rebase onto the latest `main` and fix paths before opening. - **Open a PR** only when there are at least 2 high-confidence link additions (at least 3 for low-signal runs; see "Source data"). Otherwise, write a no-change report in the Oz run output. @@ -89,7 +89,9 @@ If Google Search Console data is unavailable, say what could not be verified and This produces one perpetual, low-noise PR that accumulates every run's entry regardless of outcome. Reviewers merge it periodically (at minimum before each monthly `improve-aeo-crosslink-skill` run) so the log reaches `main`. If any git step fails, write the log entry to the run output instead and continue to step 8. -8. **Post Slack notification.** After writing the log entry, post the formatted message to `#growth-docs` using the Python snippet below. Python is preferred over curl because it reads `SLACK_BOT_TOKEN` from the environment (keeping the token out of process argv) and JSON-encodes the payload correctly regardless of newlines or special characters. If either secret is unavailable, write the notification body to the run output instead. +8. **Post Slack notification — only if there is something to act on.** Follow the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`: post **only** when a cross-link PR was opened or updated this run, or when the run failed in a way that stopped it from completing. A no-change run is silent — the run log entry from step 7 is its record. + + When the run is actionable, post the formatted message to `#growth-docs` using the Python snippet below. Python is preferred over curl because it reads `SLACK_BOT_TOKEN` from the environment (keeping the token out of process argv) and JSON-encodes the payload correctly regardless of newlines or special characters. If either secret is unavailable, write the notification body to the run output instead. ```bash python3 - <<'SLACK_EOF' @@ -223,7 +225,7 @@ Use this format: - [One specific improvement for the next run.] ``` -No-change reports stay in the Oz run output. The Oz run link is posted automatically to `#growth-docs` as part of step 8. +No-change reports stay in the Oz run output and are recorded in the run log from step 7. They are **not** posted to Slack — see the notification rules in step 8 and the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`. ## Human review expectations @@ -255,27 +257,26 @@ Keep each entry to 7 fields and under 10 lines. Do not add narrative prose. Use a simple text message (not Block Kit). The message should be scannable in under 30 seconds. -**PR opened:** +**PR opened or updated:** ``` ✅ AEO crosslink audit · YYYY-MM-DD -PR opened: [PR URL] +PR [opened | updated]: [PR URL] Links added: [N links] across [N pages]: [page names] Signals: [Peec | GSC | Peec + GSC] Oz run: [run URL] ``` -**No change:** +**Run blocked by a failure:** ``` -ℹ️ AEO crosslink audit · YYYY-MM-DD — No changes -Checked: agents, cloud agents, and orchestration docs -No PR: [brief reason — e.g., "fewer than 2 high-confidence opportunities"] +⚠️ AEO crosslink audit · YYYY-MM-DD — run blocked +What failed: [brief reason — e.g., "docs repo checkout unavailable"] Oz run: [run URL] ``` Rules: -- Post on every run, including no-change runs. +- Post only when a PR was opened or updated, or when the run was blocked by a failure. A no-change run posts nothing — its record is the run log entry. - Never include raw secret values, personal access tokens, or credential file paths in the Slack message. - Build the `Oz run` link at runtime — never hard-code the Oz host (for example `app.warp.dev` or `oz.warp.dev`). This agent may run on staging or production, and a hard-coded host resolves to the wrong environment (or a generic Runs page). Resolve the environment-correct link from your current run with `oz-dev run get "" --output-format json | jq -r '.session_link'`, substituting the run ID this agent is executing as. - If the Oz run URL is unavailable, omit that line rather than posting a broken link. diff --git a/.agents/skills/afdocs-audit/SKILL.md b/.agents/skills/afdocs-audit/SKILL.md index 0f3baf01b..35b3dfccc 100644 --- a/.agents/skills/afdocs-audit/SKILL.md +++ b/.agents/skills/afdocs-audit/SKILL.md @@ -110,29 +110,87 @@ AFDocs audit complete: 23 checks run, score 82/100 (B). After reporting, ask the user which issues they want to address. -## Slack notification (optional) +## Run log + +Write a run log entry on **every** scheduled run — clean, regressed, or blocked. The log is what makes the regression comparison possible and what makes a silent run distinguishable from a broken one. + +Use the standing log-branch pattern from `.agents/references/skill-authoring-guidelines.md`: + +1. Fetch and check out `chore/afdocs-audit-log`. Create it from the latest `origin/main` if it does not exist. +2. Prepend the entry to `.agents/logs/afdocs_audit_runs.md`. +3. Verify the write with `head -10 .agents/logs/afdocs_audit_runs.md` before committing. +4. Stage only the log file and commit: + ```text + chore: log afdocs audit run YYYY-MM-DD + ``` +5. Push and verify with `git log --oneline -1 origin/chore/afdocs-audit-log`. +6. Ensure exactly one open PR exists from `chore/afdocs-audit-log` into `main`, titled `chore: afdocs audit run log`. + +If any git step fails, write the entry to the run output and continue. + +### Run log format + +```markdown +## YYYY-MM-DD — [valid | blocked] +- **Score**: N/100 (grade) +- **Checks**: N total — N pass, N fail, N warn +- **Failing check ids**: comma-separated list, or "none" +- **Allowlisted**: N +- **Oz run**: [URL] +- **Notes**: [anything unusual] +``` + +For a firewall-blocked run, record `blocked`, omit the score entirely rather than logging the meaningless one, and note the mitigation status. + +## Regression detection + +Compare this run against the most recent **valid** entry in the run log — never against a `blocked` entry, whose score is an artifact of the firewall challenge rather than a real measurement. If there is no prior valid entry, this run establishes the baseline: log it and post nothing. + +A run is a regression when either: +- The score dropped versus the last valid entry. +- A check id appears in this run's failing set that was not in the last valid entry's failing set. + +## Slack notification + +Post **only** when the run is actionable, per the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`: -If instructed to send a report to Slack, post a summary after the audit completes. +- **Regression detected** — post the summary below. +- **Audit blocked** by the Vercel Firewall challenge — post the blocked notice. This is a failure, so it always posts. Never post a score for a blocked run; every check is a false positive. +- **Clean run with no regression** — post nothing. The run log entry is the record. + +A first-ever run with no baseline posts nothing. 1. Check if `BUZZ_SLACK_TOKEN` environment variable exists. -2. If the token exists, send a summary to the channel the user specified (or the channel configured in the agent's instructions). +2. If the token exists, send the summary to the channel the user specified (or the channel configured in the agent's instructions). -**Format:** +**Format — regression:** ``` -*AFDocs Audit — * -Score: /100 () | checks | pass, fail, warn +*AFDocs Audit — * — regression +Score: /100 (), down from /100 on + checks | pass, fail, warn -*Failures ():* +*New failures since last valid run ():* • : -*Warnings ():* +*Pre-existing failures ():* • : *Allowlisted ():* • : ``` +Lead with what changed. Pre-existing failures are context, not news — keep that list short or omit it when long. + +**Format — audit blocked:** + +``` +*AFDocs Audit — * — audit blocked, no score +The crawler was blocked by the Vercel Firewall bot challenge, so no checks could run. +Fix: disable Attack Mode, switch Bot Protection to log mode, or add a WAF bypass for the runner. +Details: references/vercel-firewall-challenge.md +``` + Send using: ```bash diff --git a/.agents/skills/afdocs-fix/SKILL.md b/.agents/skills/afdocs-fix/SKILL.md index e7f72ea51..31be075fb 100644 --- a/.agents/skills/afdocs-fix/SKILL.md +++ b/.agents/skills/afdocs-fix/SKILL.md @@ -151,14 +151,29 @@ These checks require infrastructure or design changes that can't be automated: ## Applying fixes -1. Create a branch: `git checkout -b afdocs-fixes origin/main` -2. Apply the fixes for each failing check (skip allowlisted checks). -3. Validate: `npm run build` (the build must succeed). -4. Commit with the prefix: `AFDocs fixes: ` -5. Open a PR: `gh pr create` +This skill maintains **one** long-lived fixes PR rather than one per run — see "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md`. + +1. Look for an existing open PR before creating a branch: + ```bash + gh pr list --repo warpdotdev/docs --state open \ + --search 'AFDocs fixes in:title' --json number,headRefName + ``` +2. Check out the standing branch. If the PR exists, continue on its branch and rebase; otherwise create it from `main`: + ```bash + git fetch origin + git checkout afdocs-fixes 2>/dev/null || git checkout -b afdocs-fixes origin/main + git rebase origin/main + ``` +3. Apply the fixes for each failing check (skip allowlisted checks). If a fix on the existing branch already addresses a check that is still failing, do not duplicate it — the audit may have run before the PR merged. +4. Validate: `npm run build` (the build must succeed). +5. Commit with the prefix: `AFDocs fixes: ` +6. Push. If the PR already exists the push updates it; otherwise open one with `gh pr create`. + +Never leave two open AFDocs PRs. If you find more than one, consolidate onto `afdocs-fixes` and close the extras with a comment pointing at the survivor. ## PR conventions -- Title must be prefixed with `AFDocs fixes:` (e.g., `AFDocs fixes: add llms.txt directive and content negotiation middleware`) +- Title must be prefixed with `AFDocs fixes:` and must not contain a date — a dated title defeats the title search in step 1 and produces a new PR every run - Include the audit score (before/after if known) in the PR description +- When updating an existing PR, append the new run's score and fixes under the existing headings rather than adding duplicate headings, which `check_pr_body.py` rejects - Include the co-author line: `Co-Authored-By: Oz ` diff --git a/.agents/skills/answer_question/SKILL.md b/.agents/skills/answer_question/SKILL.md index f4c98bbf1..44b69e642 100644 --- a/.agents/skills/answer_question/SKILL.md +++ b/.agents/skills/answer_question/SKILL.md @@ -24,7 +24,7 @@ Search strategy: - Use `grep` when searching for exact feature names, settings, CLI commands, or specific terms. - Read matched files to gather authoritative content. Skim broadly first, then read key sections in detail. - If the question spans multiple topics (e.g. "How do skills work with cloud agents?"), search each topic independently and cross-reference. -- Check `astro.config.mjs (sidebar config)` files in the relevant section if you need to locate a page by name. +- Check `src/sidebar.ts` if you need to locate a page by name; it holds the full navigation tree for every section. ### 2. Search source code (if needed) @@ -49,7 +49,7 @@ Both repos are indexed for `codebase_semantic_search`. Use `grep` for exact symb - Be direct and matter-of-fact. Answer the question, don't summarize the docs. - Be comprehensive — cover what the user needs to fully understand the answer — but don't pad with tangential information. -- Use Warp's standard terminology from `AGENTS.md` and the full glossary in `.warp/references/terminology.md`. Key rules: capitalize feature names (Agent, Agent Mode, Warp Drive, Codebase Context), use "agent" (generic) or "Warp Agent" (built-in harness) not "Oz agent" or "Ozzie", use "credits" not "AI credits." +- Use Warp's standard terminology from `AGENTS.md` and the full glossary in `.agents/references/terminology.md`. Key rules: capitalize feature names (Agent, Agent Mode, Warp Drive, Codebase Context), use "agent" (generic) or "Warp Agent" (built-in harness) not "Oz agent" or "Ozzie", use "credits" not "AI credits." - If the docs do not cover the topic, say so honestly. Do not guess or fabricate information. ### 4. Generate doc links diff --git a/.agents/skills/check_for_broken_links/SKILL.md b/.agents/skills/check_for_broken_links/SKILL.md index d83a477a2..0a7e7890e 100644 --- a/.agents/skills/check_for_broken_links/SKILL.md +++ b/.agents/skills/check_for_broken_links/SKILL.md @@ -145,16 +145,18 @@ After running the script, fix each broken link based on the error type: ### Adding Redirects -If content moved, you can add a redirect in the appropriate `vercel.json (redirects)`: +If content moved, add a redirect to the `redirects` array in `vercel.json` at the repo root: ```json { - "redirects": [ - { "source": "/old/path", "destination": "/new/path" } - ] + "source": "/old/path", + "destination": "/new/path/", + "statusCode": 308 } ``` +Include the trailing slash on `destination` and the `statusCode`, matching the existing entries. + ## Creating a PR with Fixes 1. Create a branch: `git checkout -b fix/broken-links` diff --git a/.agents/skills/create_pr/SKILL.md b/.agents/skills/create_pr/SKILL.md index 158c078d0..7b988bfd7 100644 --- a/.agents/skills/create_pr/SKILL.md +++ b/.agents/skills/create_pr/SKILL.md @@ -55,10 +55,10 @@ Run the link checker to validate all internal and external links: ```bash # Quick internal-only check (fast, no HTTP requests) -python3 .warp/skills/check_for_broken_links/check_links.py --internal-only +python3 .agents/skills/check_for_broken_links/check_links.py --internal-only # Full check including external links -python3 .warp/skills/check_for_broken_links/check_links.py +python3 .agents/skills/check_for_broken_links/check_links.py ``` Fix any broken links before opening the PR. See the `check_for_broken_links` skill for detailed guidance on fixing different link types. @@ -83,31 +83,30 @@ This helps you: - Catch unintended changes before review - Write an accurate PR description -### 5. Verify astro.config.mjs (sidebar config) updates +### 5. Verify sidebar updates If you added, moved, or renamed any documentation pages: -- Update the sidebar config in `astro.config.mjs` at the repo root (see AGENTS.md "Navigation and redirects") -- Ensure the page title in the sidebar config matches the H1 title in the document -- Check that the file path is correct +- Update the sidebar in `src/sidebar.ts`. That file is the source of truth; `astro.config.mjs` only imports it via `starlightSidebarTopics(sidebarTopics)`. +- Ensure the label matches the H1 title in the document, or omit the label and let Starlight derive it. +- Check that the slug is correct: no leading slash and no `.md`/`.mdx` extension. ### 6. Add redirects for moved/renamed pages -If you renamed or moved a page that's already published: +If you renamed or moved a page that's already published, add a redirect to the `redirects` array in `vercel.json` at the repo root. Every redirect lives in that one file, including redirects between top-level sections — there is no per-section redirect file and no external redirect tool. -- Add a redirect entry to the appropriate `vercel.json (redirects)` file -- For cross-space redirects, use the `scripts/docs_redirects.py` tool -- Check existing redirects first to avoid duplicates +Check existing redirects first to avoid duplicates. ```json -// Example redirect in vercel.json { - "redirects": [ - { "source": "/old/path", "destination": "/new/path" } - ] + "source": "/old/path", + "destination": "/new/path/", + "statusCode": 308 } ``` +Include the trailing slash on `destination` and the `statusCode`, matching the existing entries. + ## PR Description Guidelines Structure your PR description with these sections: @@ -129,10 +128,29 @@ This PR updates the Terminal and Agent modes documentation for the Oz launch. - Updated keyboard shortcuts with comprehensive tables - Added fork functionality documentation -### src/content/docs/agent-platform/astro.config.mjs (sidebar config) +### src/sidebar.ts - Updated navigation entry title ``` +### Unverified claims (required on drafting PRs) + +Any PR that adds or updates page content must state which UI labels, Settings paths, CLI flags, permission defaults, plan eligibility, and platform-support claims could not be verified against `warp-internal`, `warp-server`, or a live build. See step 9.5 of the `draft_docs` skill. + +Include the section even when nothing is outstanding: + +```markdown +## Unverified claims +None — all UI labels, flags, defaults, and eligibility claims were verified against source. +``` + +When claims are outstanding, give the reviewer one bullet per claim with what would confirm it: + +```markdown +## Unverified claims +- `--auto-approve` flag name — `cloud-agents.mdx`, "Run an agent" — taken from the PRD; confirm against `TuiArgs` in `warp-internal`. +- **Settings** > **Agents** > **Permissions** path — `permissions.mdx`, "Defaults" — source repos were not available in this environment. +``` + ### Additional context (optional) - Link to related issues or discussions - Screenshots for visual changes diff --git a/.agents/skills/draft_docs/SKILL.md b/.agents/skills/draft_docs/SKILL.md index a5425a9e0..782d2f357 100644 --- a/.agents/skills/draft_docs/SKILL.md +++ b/.agents/skills/draft_docs/SKILL.md @@ -81,6 +81,15 @@ To find these repos, search for directories named `warp-internal` and `warp-serv Use source code to verify technical behavior, understand feature implementation, and find accurate terminology. +When the draft names UI labels, Settings paths, CLI flags, default permissions, plan eligibility, or platform support, treat source (or a live build) as required verification, not optional color. A PRD or spec is not verification: labels and flag names routinely change between spec and ship. + +If you cannot verify a claim (for example, the source repos are not available in this environment), do not guess and do not silently drop it. Choose one of these, and record the claim either way: + +1. **Omit the claim** - Write around it when the page still works without it. Describe the action without naming the exact flag, or link to the reference page that will carry the detail. +2. **Include it with an inline marker** - Keep the spec's wording and flag it in an MDX comment next to the claim: `{/* VERIFY: flag name from PRD, unconfirmed against warp-internal */}`. + +Keep a running list of every unverified claim as you draft. Reporting that list is required — see step 9.5. + ### 6.5. Critical formatting rules These rules are frequently violated by agents. Apply them carefully during drafting: @@ -88,12 +97,25 @@ These rules are frequently violated by agents. Apply them carefully during draft - **Product name variables** — For any product name in `src/data/vars.ts`, use the variable instead of the hardcoded string. Add `import { VARS } from '@data/vars';` immediately after the frontmatter closing `---`. Use `{VARS.KEY}` in MDX prose (e.g. `{VARS.WARP_AGENT_CLI}` not "Oz CLI"). Use `{{TOKEN}}` directly in frontmatter YAML values (e.g. `title: "{{WARP_AGENT_CLI}} reference"`). Key vars: `WARP_AGENT_CLI`, `WARP_AUTOMATION_PLATFORM`, `WEB_APP`, `WEB_APP_URL`, `DASHBOARD`, `AGENT_MODE`, `WARP_DRIVE`. See `src/data/vars.ts` for the full list. - **Sentence case for all headings (H1–H4)** — Capitalize only the first word and proper feature names. ✅ `## How it works` ❌ `## How It Works` -- **Descriptive, specific headings** — Beyond correct case, a heading should name the specific topic so readers and agents can scan the page and extract a self-contained answer. ✅ `## How key type affects billing and GitHub access` ❌ `## More details` -- **Bold + dash format for list items** — `* **Term** - Description`, not `* Term: Description` +- **Descriptive, specific headings** — Beyond correct case, a heading should name the specific topic so readers and agents can scan the page and extract a self-contained answer. Prefer the concrete object or outcome over a vague section label. ✅ `## How key type affects billing and GitHub access` / `## Configuring Workload Identity Federation` ❌ `## More details` / `## Overview` / `## Additional information` / `## Other` +- **Frontmatter `description` is a standalone search summary** — One to two sentences, roughly 50–160 characters, stating the user benefit and primary keywords. It must make sense out of context, in a search result or an AI citation. ✅ `description: Environments keep cloud agents on a consistent toolchain across every trigger.` ❌ `description: This page describes environments.` ❌ a description that only restates the title +- **Bold + dash format for list items** — `* **Term** - Description`, not `* Term: Description` and not `* **Term** — Description`. Use a hyphen with spaces around it as the separator after the bold term. +- **Unordered list marker is `*`** — Match the templates and existing docs. Reserve `-` for nested lists whose parent already uses `*`; use `1.` for numbered procedures. ✅ `* **Codebase Context** - Warp indexes your Git-tracked codebase` ❌ `- **Codebase Context** - ...` as the top-level marker on a new page - **Tables or parallel bullets for comparison and reference data** — When you present two or more parallel items (key types, plan tiers, environments) or structured reference data (API endpoints, parameters), use a Markdown table or tightly parallel bullets instead of one dense paragraph. ✅ a table with one row per API endpoint, or parallel `**Personal API keys**` / `**Agent API keys**` bullet groups ❌ a single paragraph mixing both key types and their billing rules - **Bold for UI elements** — Use `**Save**` not `` `Save` `` after action verbs like "click" - **Bold per-segment for Settings paths** — Use `**Settings** > **AI** > **Knowledge**` not `` `Settings > AI > Knowledge` `` -- **Screenshots for hard-to-describe UI** — When a page documents a visual surface (statusline chips, tab bars, settings panes, multi-control layouts), include a screenshot after the prose that introduces that surface. Do not rely on prose alone for chrome that reviewers cannot reconstruct from text. Prefer one well-placed figure over repeating the same surface. Always use descriptive alt text. ✅ a statusline screenshot after the paragraph that names the chips ❌ describing chip layout in a long paragraph with no image when humans keep asking "should we include a screenshot?" +- **Orient the reader before every Settings path, CLI command, or URL** — On first reference in the page, name the app or tool. ✅ `In the Warp app, go to **Settings** > **AI** > **Knowledge**.` ❌ `Go to **Settings** > **AI** > **Knowledge**.` +- **Verify labels, flags, and defaults against source** — Before documenting a button name, Settings path, CLI flag, permission default, or eligibility rule, confirm it in `warp-internal` / `warp-server` or the live UI. ✅ `warp --auto-approve` after checking `TuiArgs` ❌ inventing `--fast-forward` from memory or an old PR description +- **Document durable behavior, not ephemeral chrome** — Prefer workflows, shortcuts, and outcomes that stay true when styling shifts. Drop glyph colors, pixel-level layout narration, and other pure presentation detail unless the reader must recognize them to succeed. ✅ "Press `Ctrl+C` once to stop the in-progress response." ❌ a full inventory of pending/running/failed glyph colors +- **State availability honestly** — If a capability is preview-only, platform-limited, interactive-only, or not yet in cloud agents, say so next to the claim. Never describe limited-preview behavior as generally available. ✅ "Linux post-processing adds smart cut; macOS applies a uniform speedup." ❌ listing smart cut as a property of every recording +- **Cover team-wide and admin effects** — For integrations and team features, state who can install, whether every teammate gets access immediately, and any per-user auth or admin steps on the external system. ✅ "A Jira admin must install the app; each teammate links their own account for run attribution." ❌ setup steps that only describe the installer's happy path +- **Section order follows reader chronology** — Prerequisites and requirements before setup, setup before usage, usage before advanced options. ✅ `## Prerequisites` → `## Set up the integration` → `## Start a run` ❌ setup steps before the reader knows what they need +- **Keep error messages out of the main flow** — Do not weave full error strings through conceptual or procedural sections. Put them in a dedicated `## Troubleshooting` section near the end, formatted symptom → cause → fix. ✅ one Troubleshooting section with the exact error as a bold lead-in ❌ repeating the same error callout after every step +- **Use callouts sparingly** — Prefer body prose. At most one or two callouts per page unless the content type template requires more. ✅ a single `:::note` for a non-obvious prerequisite ❌ a `:::note` / `:::tip` after every subsection +- **Descriptive link text, and no dead-end pages** — Never use "here", "this page", or a bare URL as link text. End every new page with a `## Related pages` section (or the type-equivalent, such as `## Next steps` on a quickstart) containing at least one internal link whose anchor names the destination topic. ✅ `Learn more about [Codebase Context](/code/codebase-context/)` ❌ `Click [here](/code/codebase-context/)` ❌ ending a new feature page with no cross-links +- **Disambiguate conditional and multi-clause wording** — If a sentence has two plausible readings (especially with "when", "if", "can", or stacked clauses), rewrite it so only one meaning remains. Prefer one idea per sentence. ✅ `Cloud handoff keeps your conversation's model only when that model is available in the cloud.` ❌ `Cloud handoff keeps your conversation's model when it can run in the cloud.` (keeps the model when it can? or only when cloud supports the model?) +- **Lead instructional sentences with the action or goal** — In steps, keyboard shortcuts, and "how to" sentences, put the action or goal first, then the control or condition. Readers should not need prior context to know what values or targets you mean. ✅ `To open the searchable environment and model selectors, press Ctrl+E.` ❌ `To change either value, press Ctrl+E.` (which values?) +- **Screenshots for hard-to-describe UI** — When a page documents a visual surface (statusline chips, tab bars, settings panes, multi-control layouts), include a screenshot after the prose that introduces that surface. Prefer prose for straightforward clicks, and prefer one well-placed figure over repeating the same surface. Always use descriptive alt text, never "screenshot". Do not invent or request screenshots of internal-only, flagged, or unfinished UI. ✅ a statusline screenshot after the paragraph that names the chips ❌ describing chip layout in a long paragraph with no image when humans keep asking "should we include a screenshot?" - **`VideoEmbed` requires a specific `title`** — Every `` must include a `title` prop that names the integration, workflow, feature, or task shown. ✅ `` ❌ `` or a generic title like `"video"` / `"demo"` ### 7. Draft the doc @@ -117,28 +139,57 @@ Skip steps 1–3 in local/interactive sessions. ### 9. Review against checklist Before presenting the draft, verify against the quality checklist in `AGENTS.md`: -- [ ] Frontmatter includes clear description written as a standalone summary +- [ ] Frontmatter description is a standalone search summary (benefit + keywords; not "This page describes..." and not a restatement of the title) - [ ] Content follows the structure for its content type +- [ ] Section order follows reader chronology (requirements → setup → usage → advanced → troubleshooting) +- [ ] Error messages and failure modes live in Troubleshooting, not woven through the main flow +- [ ] Callouts are sparse (usually 0–2 per page) and not used as a substitute for body prose - [ ] Terminology matches the glossary (`.agents/references/terminology.md`) - [ ] Headers use sentence case (with proper feature name capitalization) -- [ ] Lists use bold term + dash + explanation format -- [ ] Cross-references to related features are included +- [ ] Headers name a specific topic (not bare Overview / More details / Other) +- [ ] Lists use `*` markers with bold term + hyphen + explanation format +- [ ] Cross-references are included, and every new page ends with `## Related pages` or a type-equivalent `## Next steps` +- [ ] Link text names the destination topic (not "here" / "this page" / raw URLs) +- [ ] The first Settings path, CLI command, or URL on the page names the app or tool - [ ] Instructions include expected outcomes +- [ ] Instructional sentences lead with the action or goal before the control, shortcut, or condition +- [ ] Conditional or multi-clause sentences have only one clear reading (no ambiguous "when/if/can" stacking) - [ ] Procedures are scannable: dense sections are split into numbered steps, short bullets, or concise subsections - [ ] UI surfaces and product terms use canonical names from `.agents/references/terminology.md` +- [ ] UI labels, CLI flags, permission defaults, and eligibility claims were verified against source or the live product — anything unverified is marked inline and reported per step 9.5 +- [ ] The draft emphasizes durable behavior over ephemeral UI chrome (glyphs, pure styling, layout minutiae) +- [ ] Preview-only, platform-limited, or interactive-only capabilities are labeled as such +- [ ] Integrations and team features state admin requirements and who gets access after install - [ ] Product names with a corresponding entry in `src/data/vars.ts` use the variable syntax (`{VARS.KEY}` in prose, `{{TOKEN}}` in frontmatter) — not hardcoded strings - [ ] If AEO-driven, the draft follows the AEO brief, uses source vocabulary naturally, and avoids duplicative or junk-drawer coverage -- [ ] Images have descriptive alt text +- [ ] Images have descriptive alt text and are used only where the UI is hard to describe in prose - [ ] Visual UI surfaces that are hard to reconstruct from prose include a screenshot (or an explicit note that no screenshot is available yet) - [ ] Every `VideoEmbed` includes a specific `title` prop describing the workflow or feature shown +### 9.5. Report unverified claims + +Inline `{/* VERIFY: ... */}` markers alone are skippable: a reviewer who skims the rendered page or the diff will miss them. Surface the full list where the human cannot miss it. + +- **Agent-authored PRs** - Add an `## Unverified claims` section to the PR description. Include one bullet per claim with the claim itself, the file and section where it appears, and what would confirm it (for example, "check `TuiArgs` in `warp-internal`"). Include the section even when the list is empty, with the single line `None — all UI labels, flags, defaults, and eligibility claims were verified against source.` Never drop the section. +- **Local or interactive sessions** - List the same claims in your response to the user, before they review the draft. + +A reviewer must be able to see every unconfirmed claim without opening the diff. + ### 10. Update navigation and redirects If this is a new page, remind the user to: - Add it to the relevant section in `src/sidebar.ts`. -If this page replaces, renames, or moves an existing page, remind the user to add a redirect: -- **Same-space redirect**: Add an entry to the space's `vercel.json (redirects)` file under `redirects:`. -- **Cross-space redirect**: Add the redirect through the Astro Starlight UI (cross-space redirects cannot be managed via `vercel.json (redirects)`). +If this page replaces, renames, or moves an existing page, remind the user to add a redirect to the `redirects` array in `vercel.json` at the repo root: + +```json +{ + "source": "/old/path", + "destination": "/new/path/", + "statusCode": 308 +} +``` + +All redirects live in that one file, including redirects between top-level sections — there is no separate per-section redirect file and no UI for managing them. Include the trailing slash on `destination` to match the existing entries. Always check the current list of redirects before adding a new one to avoid duplicates. diff --git a/.agents/skills/improve-404-monitor-skill/SKILL.md b/.agents/skills/improve-404-monitor-skill/SKILL.md index ccf2d305e..4a1068127 100644 --- a/.agents/skills/improve-404-monitor-skill/SKILL.md +++ b/.agents/skills/improve-404-monitor-skill/SKILL.md @@ -11,12 +11,32 @@ This skill is part of the self-improvement loop architecture. The `weekly-404-mo ## Schedule -Monthly, first Monday of each month, 9am PT (`0 17 1-7 * 1` in UTC). Run this agent starting in month 2 after `weekly-404-monitor` begins writing log entries, but only act on patterns if at least 6 entries exist. +Monthly, first Monday of each month, 9am PT. Run this agent starting in month 2 after `weekly-404-monitor` begins writing log entries, but only act on patterns if at least 6 entries exist. + +Cron: `0 17 * * 1` (UTC) — every Monday — combined with the first-week guard in step 0 below. + +:::caution +Do **not** use `0 17 1-7 * 1`. That expression looks like "first Monday" but is not: when a cron expression restricts **both** day-of-month and day-of-week, the two fields are **ORed**, so it fires on every day of the 1st through 7th **and** every Monday — roughly 11 times a month. The `improve-drafting-skills` agent shipped with this exact expression and opened four conflicting PRs in six days before it was caught. Standard cron cannot express "first Monday," so the day-of-month guard is required. +::: + +## Step 0: First-week guard + +Run this before anything else. The schedule fires every Monday, so a run outside the first week of the month must exit immediately without reading logs, editing files, opening a PR, or posting to Slack. + +```bash +DAY_OF_MONTH=$(date -u +%d) +if [ "$DAY_OF_MONTH" -gt 7 ]; then + echo "Skipping: today is day $DAY_OF_MONTH, not the first Monday of the month. This agent runs monthly." + exit 0 +fi +``` + +A skipped run is a no-op, not a failure. Write the skip line to the run output and post nothing. ## Prerequisites - Docs repo checked out at `main` -- `.agents/logs/weekly_404_monitor_runs.md` present on `main` (or on `chore/404-monitor-log` if the standing PR has not been merged yet) +- The `chore/404-monitor-log` branch reachable, since the run log is read from there (see "Signal source") - At least 6 entries in the run log - `gh` CLI authenticated with write access to `warpdotdev/docs` - `SLACK_BOT_TOKEN` — for posting a summary to `#growth-docs` @@ -24,7 +44,14 @@ Monthly, first Monday of each month, 9am PT (`0 17 1-7 * 1` in UTC). Run this ag ## Signal source -Read `.agents/logs/weekly_404_monitor_runs.md`. If the standing log PR (`chore: 404 monitor run log`) has not been merged into `main`, read from the `chore/404-monitor-log` branch instead. +Read the run log from the `chore/404-monitor-log` branch, which always holds the complete history: + +```bash +git fetch origin chore/404-monitor-log +git checkout origin/chore/404-monitor-log -- .agents/logs/weekly_404_monitor_runs.md +``` + +Do not read it from `main`. `main` only has entries up to the last time a human merged the standing log PR, so it can silently under-count entries — which matters here because the 6-entry minimum and the 3+ occurrence thresholds below are both counts. Do not attempt to merge the standing log PR; merging is human housekeeping, not a precondition for this analysis. See "Log availability" in `.agents/references/skill-authoring-guidelines.md`. Each entry captures: date, outcome (PR opened / No PR / No data), total 404 volume (this week vs last week), trend direction, significant gap count, redirect candidates processed, HIGH-confidence redirect count, PR URL, Oz run URL, and notes. @@ -118,38 +145,52 @@ Before opening a PR, verify: python3 -c "import sys; content = open(sys.argv[1]).read(); parts = content.split('---', 2); assert len(parts) >= 3" .agents/skills/weekly-404-monitor/SKILL.md ``` -### 6. Open a draft PR +### 6. Create or update the standing improvement PR -PR title: +This agent maintains **one** long-lived improvement PR, never one per run — see "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md`. + +Stable branch: `docs/improve-404-monitor-skill` +Stable title (no date — the date goes in the body): ```text -docs(skills): improve weekly-404-monitor skill from run log analysis YYYY-MM-DD +docs(skills): improve weekly-404-monitor skill from run log analysis ``` -PR body must include: +Look for an existing open PR first: +```bash +gh pr list --repo warpdotdev/docs --state open \ + --search 'improve weekly-404-monitor skill from run log analysis in:title' \ + --json number,headRefName +``` +If one exists, check out its branch, rebase on the latest `origin/main`, apply this run's edits, push, and append dated bullets under the existing headings. If none exists, create the branch from the latest `origin/main` and open a draft PR. + +PR body carries these headings, each appearing exactly once (`check_pr_body.py` rejects duplicates, so do not add a per-run copy): - **Entries analyzed**: N run log entries, date range - **Patterns identified**: each pattern, evidence (entry count and dates), and proposed fix - **GitHub PR quality check**: summary of how many redirect PRs were accepted, corrected, or closed - **Patterns reviewed but not acted on**: observed patterns below threshold or already addressed - **Open questions for human review**: anything requiring editorial judgment +Prefix each appended bullet with its run date so the reviewer can tell runs apart. + Cap the diff at `weekly-404-monitor/SKILL.md` only. Do not rewrite unrelated sections. -### 7. Post Slack notification +### 7. Notify only if there is something to act on + +Post to `#growth-docs` **only** when the standing PR was created or received new commits, or when the run was blocked by a failure. Follow the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`. A run that finds no actionable patterns — or that skips via the step 0 guard, or exits because fewer than 6 entries exist — posts nothing and is recorded in the run output only. -**PR opened:** +**PR opened or updated:** ``` ✅ 404 monitor skill improvement · YYYY-MM-DD -PR: [PR URL] +PR [created | updated]: [PR URL] Patterns addressed: N Evidence base: N run log entries (last N weeks) Oz run: [run URL] ``` -**No action (too few patterns or entries):** +**Run blocked by a failure:** ``` -ℹ️ 404 monitor skill review · YYYY-MM-DD — No changes -Entries analyzed: N -No actionable patterns found: [brief reason] +⚠️ 404 monitor skill review · YYYY-MM-DD — run blocked +What failed: [brief reason — e.g., "could not fetch the log branch"] Oz run: [run URL] ``` @@ -168,7 +209,7 @@ To deploy: 2. Verify the Oz environment has `SLACK_BOT_TOKEN` and `GROWTH_DOCS_SLACK_CHANNEL_ID` set. 3. In the Oz web app, create a new scheduled agent: - **Skill**: `improve-404-monitor-skill` from `warpdotdev/docs` - - **Schedule**: `0 17 1-7 * 1` (UTC) = first Monday of each month at 9am PT + - **Schedule**: `0 17 * * 1` (UTC) = every Monday at 9am PT. The step 0 first-week guard narrows this to the first Monday only. See the caution in `## Schedule` for why the day-of-month field must stay `*`. - **Environment**: the same environment used for `weekly-404-monitor` (already has `warpdotdev/docs` checked out and secrets set) - **Branch**: `main` 4. Start this agent after at least 6 weekly-404-monitor run log entries exist on `main` (approximately 6 weeks after run log writing is deployed). diff --git a/.agents/skills/improve-aeo-crosslink-skill/SKILL.md b/.agents/skills/improve-aeo-crosslink-skill/SKILL.md index 546f82d0a..0377fa929 100644 --- a/.agents/skills/improve-aeo-crosslink-skill/SKILL.md +++ b/.agents/skills/improve-aeo-crosslink-skill/SKILL.md @@ -13,7 +13,25 @@ This skill is part of the self-improvement loop architecture. The `aeo_crosslink Monthly, first Monday of each month, 9am PT. Start this agent on month 3 after `aeo_crosslink_audit` is running regularly (requires at least 8 run log entries for meaningful pattern analysis). -Suggested cron: `0 17 1-7 * 1` (UTC) = first Monday of each month at 9am PT. +Cron: `0 17 * * 1` (UTC) — every Monday — combined with the first-week guard in step 0 below. + +:::caution +Do **not** use `0 17 1-7 * 1`. That expression looks like "first Monday" but is not: when a cron expression restricts **both** day-of-month and day-of-week, the two fields are **ORed**, so it fires on every day of the 1st through 7th **and** every Monday — roughly 11 times a month. The `improve-drafting-skills` agent shipped with this exact expression and opened four conflicting PRs in six days before it was caught. Standard cron cannot express "first Monday," so the day-of-month guard is required. +::: + +## Step 0: First-week guard + +Run this before anything else. The schedule fires every Monday, so a run outside the first week of the month must exit immediately without reading logs, editing files, opening a PR, or posting to Slack. + +```bash +DAY_OF_MONTH=$(date -u +%d) +if [ "$DAY_OF_MONTH" -gt 7 ]; then + echo "Skipping: today is day $DAY_OF_MONTH, not the first Monday of the month. This agent runs monthly." + exit 0 +fi +``` + +A skipped run is a no-op, not a failure. Write the skip line to the run output and post nothing. ## Prerequisites @@ -30,47 +48,20 @@ Do not act if fewer than 8 entries exist. Write a "too early to analyze" notice ## Workflow -### 0. Merge the standing log PR - -Before reading the run log, ensure all accumulated entries are on `main` by merging the standing log PR. This is the PR from `chore/aeo-crosslink-audit-log` that the `aeo_crosslink_audit` agent continuously appends to. - -```bash -# Find the open log PR (there should be at most one) -OPEN_LOG_PR=$(gh pr list --repo warpdotdev/docs \ - --head chore/aeo-crosslink-audit-log \ - --state open \ - --json number \ - --jq '.[0].number' 2>/dev/null) - -if [[ -n "$OPEN_LOG_PR" ]]; then - # Safety check: only merge if the PR touches exactly the expected log file. - CHANGED_FILES=$(gh pr view "$OPEN_LOG_PR" --repo warpdotdev/docs --json files --jq '[.files[].path]') - ONLY_LOG=$(echo "$CHANGED_FILES" | python3 -c " -import json, sys -files = json.load(sys.stdin) -print('yes' if all(f == '.agents/logs/aeo_crosslink_audit_runs.md' for f in files) else 'no') -") - if [[ "$ONLY_LOG" == 'yes' ]]; then - gh pr merge "$OPEN_LOG_PR" --repo warpdotdev/docs --merge - # Non-destructive fast-forward: fails loudly if worktree is dirty or not fast-forwardable. - git fetch origin main - git merge --ff-only origin/main - else - echo "Log PR contains unexpected files — skipping merge, reading log from branch instead." - git fetch origin chore/aeo-crosslink-audit-log - git checkout origin/chore/aeo-crosslink-audit-log -- .agents/logs/aeo_crosslink_audit_runs.md - fi -fi -``` +### 0. Read the run log from its branch -If the merge fails (conflict, permissions, or the branch is ahead of main in an unexpected way), log the failure to run output and read the log from the `chore/aeo-crosslink-audit-log` branch instead: +Read the log directly from `chore/aeo-crosslink-audit-log`, the branch the `aeo_crosslink_audit` agent appends to after every run: ```bash git fetch origin chore/aeo-crosslink-audit-log git checkout origin/chore/aeo-crosslink-audit-log -- .agents/logs/aeo_crosslink_audit_runs.md ``` -Do not abort the skill run because the log PR could not be merged. Proceed with whatever log entries are available. +The branch always holds the complete history. `main` only has entries up to the last time a human merged the standing log PR, so reading `main` would silently analyze a truncated set and skew every pattern threshold below. + +**Do not merge the standing log PR.** An earlier version of this skill attempted the merge as its first step. That coupled the analysis to a repo write the agent may not have permission to perform, and turned an unmerged PR into a hard failure rather than a non-event. Merging is human housekeeping; see "Log availability" in `.agents/references/skill-authoring-guidelines.md`. + +If the branch does not exist or the fetch fails, fall back to reading `.agents/logs/aeo_crosslink_audit_runs.md` from the current checkout and note in the run output that the history may be incomplete. Do not abort the run. ### 1. Parse the run log @@ -131,37 +122,49 @@ Before opening a PR, verify: - Verify the YAML frontmatter of any changed `.md` file is parseable: `python3 -c "import sys; content = open(sys.argv[1]).read(); parts = content.split('---', 2); assert len(parts) >= 3" .agents/skills/aeo_crosslink_audit/SKILL.md` - Note: `style_lint.py --changed` only scans `src/content/docs/` and does not cover `.agents/skills/`; do not rely on it to validate skill file edits -### 5. Open a draft PR +### 5. Create or update the standing improvement PR -Open a draft PR with title: +This agent maintains **one** long-lived improvement PR, never one per run — see "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md`. + +Stable branch: `docs/improve-aeo-crosslink-skill` +Stable title (no date — the date goes in the body): ```text -docs(skills): improve aeo_crosslink_audit skill from run log analysis YYYY-MM-DD +docs(skills): improve aeo_crosslink_audit skill from run log analysis ``` -PR body must include: +Look for an existing open PR first: +```bash +gh pr list --repo warpdotdev/docs --state open \ + --search 'improve aeo_crosslink_audit skill from run log analysis in:title' \ + --json number,headRefName +``` +If one exists, check out its branch, rebase on the latest `origin/main`, apply this run's edits, push, and append dated bullets under the existing headings. If none exists, create the branch from the latest `origin/main` and open a draft PR. + +PR body carries these headings, each appearing exactly once (`check_pr_body.py` rejects duplicates, so do not add a per-run copy): - **Entries analyzed**: N run log entries, date range - **Patterns identified**: each pattern, evidence (entry count and dates), and proposed fix - **Patterns reviewed but not acted on**: patterns observed but below threshold or already addressed - **Open questions for human review**: anything that requires editorial judgment before the change is applied -### 6. Post Slack notification +Prefix each appended bullet with its run date so the reviewer can tell runs apart. + +### 6. Notify only if there is something to act on -Post to `#growth-docs`: +Post to `#growth-docs` **only** when the standing PR was created or received new commits, or when the run was blocked by a failure. Follow the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`. A run that finds no actionable patterns — or that skips via the step 0 guard, or exits because fewer than 8 entries exist — posts nothing and is recorded in the run output only. -**PR opened:** +**PR opened or updated:** ``` ✅ AEO crosslink audit skill improvement · YYYY-MM-DD -PR: [PR URL] +PR [created | updated]: [PR URL] Patterns addressed: N Evidence base: N run log entries (last N weeks) Oz run: [run URL] ``` -**No action (too few patterns or too few entries):** +**Run blocked by a failure:** ``` -ℹ️ AEO crosslink audit skill review · YYYY-MM-DD — No changes -Entries analyzed: N -No actionable patterns found: [brief reason] +⚠️ AEO crosslink audit skill review · YYYY-MM-DD — run blocked +What failed: [brief reason — e.g., "could not fetch the log branch"] Oz run: [run URL] ``` In both messages, build the `Oz run` link at runtime — never hard-code the Oz host (for example `app.warp.dev` or `oz.warp.dev`). This agent may run on staging or production, and a hard-coded host resolves to the wrong environment (or a generic Runs page). Resolve the environment-correct link from your current run, substituting the run ID this agent is executing as: @@ -179,6 +182,6 @@ To deploy: 2. Verify the Oz environment has `SLACK_BOT_TOKEN` and `GROWTH_DOCS_SLACK_CHANNEL_ID` set. 3. In the Oz web app, create a new scheduled agent: - **Skill**: `improve-aeo-crosslink-skill` from `warpdotdev/docs` - - **Schedule**: `0 17 1-7 * 1` (UTC) = first Monday of each month at 9am PT + - **Schedule**: `0 17 * * 1` (UTC) = every Monday at 9am PT. The step 0 first-week guard narrows this to the first Monday only. See the caution in `## Schedule` for why the day-of-month field must stay `*`. - **Environment**: the same environment used for `aeo_crosslink_audit` (has `warpdotdev/docs` and buzz workspace checked out) - **Branch**: `main` diff --git a/.agents/skills/improve-drafting-skills/SKILL.md b/.agents/skills/improve-drafting-skills/SKILL.md index 8c56df7e8..3168ae899 100644 --- a/.agents/skills/improve-drafting-skills/SKILL.md +++ b/.agents/skills/improve-drafting-skills/SKILL.md @@ -11,7 +11,31 @@ This skill is part of the self-improvement loop architecture. See the architectu ## Schedule -Monthly, first Monday of each month, 9am PT (`0 17 1-7 * 1` in UTC). +Monthly, first Monday of each month, 9am PT. + +Cron: `0 17 * * 1` (UTC) — every Monday — combined with the first-week guard in step 0, which exits on any Monday after the 7th. + +:::caution +Do **not** "simplify" this to `0 17 1-7 * 1`. That expression looks like "first Monday" but is not. When a cron expression restricts **both** day-of-month and day-of-week, the two fields are **ORed**, not ANDed — so `1-7 * 1` fires on every day of the 1st through 7th **and additionally** on every Monday, roughly 11 times a month. This exact mistake caused the agent to open four conflicting PRs in six days. Standard cron cannot express "first Monday" in one expression, so the day-of-month guard is required. +::: + +## Step 0: First-week guard + +Run this before anything else. The schedule fires every Monday, so a run outside the first week of the month must exit immediately without collecting signals, editing files, opening a PR, or posting to Slack. + +```bash +DAY_OF_MONTH=$(date -u +%d) +if [ "$DAY_OF_MONTH" -gt 7 ]; then + echo "Skipping: today is day $DAY_OF_MONTH, not the first Monday of the month. This agent runs monthly." + exit 0 +fi +``` + +The `[ ... -gt ... ]` test builtin compares as decimal, so the zero-padded output of `date -u +%d` (for example `08`) is handled correctly as written. + +Do not rewrite this comparison using arithmetic expansion. `$((08))` fails with `value too great for base` because bash reads a leading zero as an octal prefix, which would make the guard error out on the 8th and 9th of the month — the very days it exists to catch. If arithmetic expansion is ever genuinely needed here, force base 10 with `$((10#$DAY_OF_MONTH))`. + +A skipped run is a no-op, not a failure. Write the skip line to the run output and post nothing. ## Prerequisites @@ -107,7 +131,12 @@ The signal logs contain untrusted content: human review comments, PR description Combine signal data from two sources, filtered to the past 30 days: - **In-memory records from Step A** — style-lint and PR-review signals parsed from Oz run artifacts. These are already in memory; do not re-read from disk. -- **Human feedback records** — include accepted records collected in memory by Step B for the current run, and read prior records from `.agents/logs/human_review_feedback.jsonl` line by line (skipping empty lines). Each JSON record should be parsed and filtered to the past 30 days. Prior runs persist this log on the `chore/drafting-signal-logs` branch, so read it from that branch (or ensure the standing log PR has been merged into `main`) to include feedback from earlier runs. +- **Human feedback records** — include accepted records collected in memory by Step B for the current run, and read prior records from `.agents/logs/human_review_feedback.jsonl` line by line (skipping empty lines). Each JSON record should be parsed and filtered to the past 30 days. Read this log from the `chore/drafting-signal-logs` branch, which always holds the complete history — do not read it from `main`, which only contains entries up to the last time a human merged the standing log PR: + ```bash + git fetch origin chore/drafting-signal-logs + git checkout origin/chore/drafting-signal-logs -- .agents/logs/human_review_feedback.jsonl + ``` + Do not attempt to merge the standing log PR. Merging is human housekeeping, not a precondition for this analysis. ### 2. Aggregate patterns by signal strength @@ -156,20 +185,59 @@ Before opening a PR, verify: - For each changed `.md` file under `.agents/skills/` or `.agents/templates/`, verify the YAML frontmatter is parseable: `python3 -c "import sys; content = open(sys.argv[1]).read(); parts = content.split('---', 2); assert len(parts) >= 3" PATH_TO_FILE` - Note: `style_lint.py --changed` only scans `src/content/docs/` and does not cover `.agents/skills/` or `.agents/templates/`; do not rely on it to validate skill or template file edits -### 7. Open a draft PR +### 7. Create or update the standing improvement PR + +This agent maintains **one** long-lived improvement PR, never one per run. See "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md` for the general contract. -Open a draft PR with title: +Stable branch: `docs/improve-drafting-skills` +Stable title (no date — the date goes in the body): ```text -docs(skills): improve drafting skills from signal log patterns YYYY-MM-DD +docs(skills): improve drafting skills from signal log patterns +``` + +**First, look for an existing open PR:** +```bash +gh pr list --repo warpdotdev/docs --state open \ + --search 'improve drafting skills from signal log patterns in:title' \ + --json number,headRefName ``` -PR body must include: -- **Patterns addressed** — list each pattern, its signal source (which log, which check/tag), and the occurrence count -- **Improvement targets** — which files were edited and why -- **Patterns reviewed but not acted on** — any patterns that met the threshold but were already covered or had insufficient signal -- **Open questions for human review** — any judgment calls about whether a proposed rule change is correct +**If one exists**, add this run's work to it: +1. Check out `docs/improve-drafting-skills` and rebase on the latest `origin/main`. +2. Apply this run's edits and commit. +3. Push. +4. Append this run's dated bullets under the PR body's existing headings (see "PR body" below). Fetch the current body first and make a minimal additive edit — do not regenerate it, and do not add new copies of the headings. + +**If none exists**, create `docs/improve-drafting-skills` from the latest `origin/main` and open a draft PR. + +Never leave two open improvement PRs. If you find more than one, consolidate onto the stable branch and close the extras with a comment pointing at the survivor. + +#### PR body -Write the body to a file and verify it before opening the PR — this catches repetition-loop corruption that has reached PR descriptions before (see the `create_pr` skill for details): +The body carries a **fixed set of headings that appear exactly once**, no matter how many runs have contributed. Each run appends dated bullets under the existing headings rather than adding its own run section. + +This structure is required, not stylistic. `check_pr_body.py` flags any duplicate heading and asserts each required heading appears exactly once, so a body with per-run copies of `## Patterns addressed` fails the check and blocks the update. + +```markdown +## Run history +- YYYY-MM-DD — N patterns addressed, M files touched + +## Patterns addressed +- `YYYY-MM-DD` **pattern_category** — signal source (which log, which check/tag), occurrence count, and the edit made + +## Improvement targets +- `YYYY-MM-DD` `path/to/file.md` — what changed and which pattern it addresses + +## Patterns reviewed but not acted on +- `YYYY-MM-DD` **pattern_category** — why not acted on (already covered, below threshold) + +## Open questions for human review +- `YYYY-MM-DD` — judgment call needing a reviewer's opinion +``` + +Before appending, re-read the existing body and check whether the pattern you are about to add is already listed. Consecutive runs draw from an overlapping 30-day signal window, so the same pattern will often resurface. Do not add a duplicate bullet — append the new date to the existing bullet instead, so the reviewer can see the pattern recurred without the list growing. + +Write the body to a file and verify it before creating or editing the PR — this catches repetition-loop corruption that has reached PR descriptions before (see the `create_pr` skill for details): ```bash python3 .agents/skills/create_pr/check_pr_body.py /tmp/pr-body.md \ --require-heading "## Patterns addressed" \ @@ -177,23 +245,33 @@ python3 .agents/skills/create_pr/check_pr_body.py /tmp/pr-body.md \ --require-heading "## Patterns reviewed but not acted on" \ --require-heading "## Open questions for human review" ``` -Run `gh pr create --draft --body-file /tmp/pr-body.md` only if the check passes. If you later edit this PR's body (for example, to record a human-review follow-up), fetch the current body first and apply a minimal, additive edit rather than regenerating it, then re-run the check — see the `create_pr` skill's "Update an existing PR" section. +Run `gh pr create --draft --body-file /tmp/pr-body.md` (or `gh pr edit`) only if the check passes. See the `create_pr` skill's "Update an existing PR" section for the update workflow. + +### 8. Notify only if there is something to act on + +Post to `#growth-docs` **only** when the standing PR was created or received new commits this run. Follow the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`. -Post a Slack summary to `#growth-docs`: ``` ✅ Drafting skills improvement · YYYY-MM-DD -PR: [PR URL] -Patterns addressed: N (human feedback: N, agent review: N, style lint: N) +PR: [PR URL] ([created | updated]) +Patterns addressed this run: N (human feedback: N, agent review: N, style lint: N) Top patterns: [pattern 1], [pattern 2], [pattern 3] Oz run: [run URL] ``` + Build the `Oz run` link at runtime — never hard-code the Oz host (for example `app.warp.dev` or `oz.warp.dev`). This agent may run on staging or production, and a hard-coded host resolves to the wrong environment (or a generic Runs page). Resolve the environment-correct link from your current run, substituting the run ID this agent is executing as: ```bash oz-dev run get "" --output-format json | jq -r '.session_link' ``` If the command fails or returns an empty value, omit the `Oz run` line rather than posting a hard-coded or broken URL. -If fewer than 2 actionable patterns are found, do not open a PR. Write a no-change report to the run output instead: +**Do not post** when: +- The first-week guard skipped the run (step 0). +- Fewer than 2 actionable patterns were found and no PR was created or updated. + +**Do post** when the run fails in a way that prevents it from completing — for example the signal collection step errors out, or the log branch cannot be fetched. A blocked run is actionable; a quiet run is not. + +If fewer than 2 actionable patterns are found, do not open or update a PR. Write a no-change report to the run output and stop: ```text ## Drafting skills improvement — no-change report @@ -205,11 +283,11 @@ If fewer than 2 actionable patterns are found, do not open a PR. Write a no-chan **Suggested adjustment**: [one specific suggestion for the next run, e.g., lower a threshold or check a different log] ``` -Post the no-change report link to Slack. - ## Run log -This skill does not have its own run log. Its durable outputs are the improvement PR (or no-change report), the Slack message, and the standing `chore: drafting signal logs` PR that accumulates the signal logs it collects. +This skill does not keep a separate run-log file. Its durable record is the standing `chore: drafting signal logs` PR, which accumulates a signal-log entry on every run — including no-change runs and guard-skipped runs. That per-run entry is what makes the actionable-only Slack policy safe: a silent run is still recorded, so silence means "ran, nothing to do" rather than "possibly broken." + +Its other durable outputs are the standing improvement PR and, when warranted, the Slack message. ## Deployment @@ -220,6 +298,6 @@ To deploy: 2. Verify the Oz environment has `SLACK_BOT_TOKEN` and `GROWTH_DOCS_SLACK_CHANNEL_ID` set. 3. In the Oz web app, create a new scheduled agent: - **Skill**: `improve-drafting-skills` from `warpdotdev/docs` - - **Schedule**: `0 17 1-7 * 1` (UTC) = first Monday of each month at 9am PT + - **Schedule**: `0 17 * * 1` (UTC) = every Monday at 9am PT. The step 0 first-week guard narrows this to the first Monday only. See the caution in `## Schedule` for why the day-of-month field must stay `*`. - **Environment**: the same environment used for `weekly-404-monitor` (already has `warpdotdev/docs` checked out) - **Branch**: `main` diff --git a/.agents/skills/review-docs-pr/SKILL.md b/.agents/skills/review-docs-pr/SKILL.md index a4d823fd7..cb54d4144 100644 --- a/.agents/skills/review-docs-pr/SKILL.md +++ b/.agents/skills/review-docs-pr/SKILL.md @@ -25,7 +25,7 @@ Focus on: 2. **Style guide compliance**: Reference `AGENTS.md` for documentation standards (voice, formatting, terminology). 3. **Content quality**: Check for clarity, accuracy, proper frontmatter, and appropriate use of headers/lists. 4. **Code snippets**: Verify that any code examples, commands, or configuration snippets are correct and will work as documented. If you're unsure about technical details, use the `answer_question` skill to verify against the docs or search the source code. -5. **Astro Starlight structure**: Verify `src/sidebar.ts` updates if files were added, moved, or renamed, and that redirects are added to vercel.json (redirects) when needed. +5. **Astro Starlight structure**: Verify `src/sidebar.ts` updates if files were added, moved, or renamed, and that redirects are added to the `redirects` array in `vercel.json` when needed. 6. **Product name variables**: Check whether any product names with a corresponding entry in `src/data/vars.ts` are hardcoded as literal strings instead of using `{VARS.KEY}` (prose) or `{{TOKEN}}` (frontmatter). Key strings to watch for: "Oz CLI", "Oz web app", "oz.warp.dev", "Oz dashboard", "Oz run". Flag as `⚠️ [IMPORTANT]` if a new file adds these without using the variable system. For existing files, flag as `💡 [SUGGESTION]`. 7. **AEO/source-data fit**: diff --git a/.agents/skills/sync-error-docs/SKILL.md b/.agents/skills/sync-error-docs/SKILL.md index 89279fda0..52eb0109f 100644 --- a/.agents/skills/sync-error-docs/SKILL.md +++ b/.agents/skills/sync-error-docs/SKILL.md @@ -2,13 +2,13 @@ name: sync-error-docs description: >- Detect new platform error codes in warp-server that are missing documentation - pages in the docs repo. Creates doc pages, astro.config.mjs (sidebar config) entries, and - redirects for any gaps. Use on a weekly schedule or when error codes change. + pages in the docs repo. Creates doc pages, sidebar entries, and redirects for + any gaps. Use on a weekly schedule or when error codes change. --- # Sync Error Docs -Ensure every `ErrorCode` in `platformerrors.go` has a corresponding documentation page, astro.config.mjs (sidebar config) entry, and redirects. +Ensure every `ErrorCode` in `platformerrors.go` has a corresponding documentation page, sidebar entry, and redirect. ## Repos @@ -17,6 +17,8 @@ This skill requires two repos in the agent's environment: - `warpdotdev/warp-server` — source of truth for error codes - `warpdotdev/docs` — documentation pages +All paths below are relative to the **docs repo root**, with `warp-server` checked out as a sibling directory (`../warp-server`), matching the convention used by `sync-openapi-spec`. + ## Workflow ### Step 1: Extract error codes from warp-server @@ -24,7 +26,7 @@ This skill requires two repos in the agent's environment: Grep the `ErrorCode` constants from `platformerrors.go`: ```bash -grep 'ErrorCode = "' warp-server/logic/ai/ambient_agents/platformerrors/platformerrors.go +grep 'ErrorCode = "' ../warp-server/logic/ai/ambient_agents/platformerrors/platformerrors.go ``` Each match yields a line like `InsufficientCredits ErrorCode = "insufficient_credits"`. Extract the quoted string — that is the canonical error code (underscore format). @@ -34,7 +36,7 @@ Each match yields a line like `InsufficientCredits ErrorCode = "insufficient_cre List the markdown files in the errors directory: ```bash -ls docs/src/content/docs/reference/api-and-sdk/troubleshooting/errors/*.mdx +ls src/content/docs/reference/api-and-sdk/troubleshooting/errors/*.mdx ``` Each file is named `{hyphen-code}.mdx` (e.g., `insufficient-credits.mdx`). Ignore `index.mdx`. @@ -59,68 +61,95 @@ To fill in the template accurately: Place the new file at: ``` -docs/src/content/docs/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}.mdx +src/content/docs/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}.mdx ``` -### Step 5: Add to astro.config.mjs (sidebar config) +### Step 5: Add to the sidebar -Add the new page to `docs/src/content/docs/reference/astro.config.mjs (sidebar config)` under the Errors section. +The sidebar lives in `src/sidebar.ts`. (`astro.config.mjs` only imports it via `starlightSidebarTopics(sidebarTopics)` — do not edit the sidebar there.) -- User errors: insert before `authentication_required` (which begins the platform errors group) -- Platform errors: append after `internal_error` (currently the last entry in the list) -- Note: the list has no explicit section labels. The platform errors group starts at `authentication_required` and currently contains `authentication_required`, `resource_unavailable`, and `internal_error`. -- Use the format: ` * [{underscore\_code}](api-and-sdk/troubleshooting/errors/{hyphen-code}.md)` -- Note: underscores in the display name must be escaped as `\_` for Astro Starlight +Find the `Errors` group inside the `API Troubleshooting` group, under the `Reference` topic. Its `items` array begins with the index entry: -### Step 6: Add vercel.json (redirects) redirect +```ts +{ slug: 'reference/api-and-sdk/troubleshooting/errors', label: 'Errors' }, +'reference/api-and-sdk/troubleshooting/errors/insufficient-credits', +'reference/api-and-sdk/troubleshooting/errors/feature-not-available', +``` -Add a redirect entry in `docs/src/content/docs/reference/vercel.json (redirects)` that maps the underscore path to the hyphen path. This handles visitors who type the underscore form directly: +Add the new page as a bare slug string using the **hyphenated** code: -```yaml -api-and-sdk/troubleshooting/errors/{underscore_code}: api-and-sdk/troubleshooting/errors/{hyphen-code}.md +```ts +'reference/api-and-sdk/troubleshooting/errors/{hyphen-code}', ``` -Before adding, check if the entry already exists to avoid duplicates on re-runs: +Rules: +- Use a plain slug string. Do not use Markdown link syntax — `* [name](path.md)` is GitBook-era format and will not build. +- No `.md`/`.mdx` extension and no leading slash. +- Only add an explicit `{ slug, label }` object if the auto-derived title is wrong; the existing error entries all rely on the derived title. +- Append after the last existing error entry unless a grouping order is obvious from the surrounding entries. The list is not alphabetized and has no section labels. + +### Step 6: Add the underscore-to-hyphen redirect + +Error codes are underscored (`insufficient_credits`) but page slugs are hyphenated (`insufficient-credits`). Add a redirect to `vercel.json` (at the repo root) so the underscore form resolves. + +Check for an existing entry first, to stay idempotent across re-runs: ```bash -grep 'api-and-sdk/troubleshooting/errors/{underscore_code}:' docs/src/content/docs/reference/vercel.json (redirects) +grep -F '/reference/api-and-sdk/troubleshooting/errors/{underscore_code}"' vercel.json ``` -If it returns a match, skip this step. Otherwise, add the entry under the existing `redirects:` block. +If it matches, skip. Otherwise add an entry to the `redirects` array, alongside the other error-code redirects: -### Step 7: Create site-level redirect +```json +{ + "source": "/reference/api-and-sdk/troubleshooting/errors/{underscore_code}", + "destination": "/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}/", + "statusCode": 308 +} +``` -The API's `type` URI uses the format `https://docs.warp.dev/errors/{underscore_code}`. This needs a site-level redirect to reach the actual doc page. +The trailing slash on `destination` is required — every existing error redirect uses one. - -Use the existing `docs_redirects.py` script (requires `GITBOOK_TOKEN` env var and `requests` Python package). +### Step 7: Confirm the site-level `/errors/` route (usually no action) -First, check if the redirect already exists to avoid duplicates on re-runs: +The API's `type` URI uses `https://docs.warp.dev/errors/{underscore_code}`. A **catch-all redirect already covers every code**, so no per-code work is normally needed: -```bash -python3 docs/scripts/docs_redirects.py get-by-source \ - --source "/errors/{underscore_code}" +```json +{ + "source": "/errors/:code", + "destination": "/reference/api-and-sdk/troubleshooting/errors/:code/", + "statusCode": 308 +} ``` -If a redirect is returned, skip the `create` step. Otherwise, create it: +That rule forwards the code unchanged, so `/errors/insufficient_credits` lands on the underscored path and is then picked up by the step 6 redirect. Adding step 6 is therefore sufficient. + +Verify the catch-all is still present: ```bash -python3 docs/scripts/docs_redirects.py create \ - --source "/errors/{underscore_code}" \ - --destination-json '{"kind": "url", "url": "https://docs.warp.dev/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}"}' +grep -F '"/errors/:code"' vercel.json ``` -Read `references/redirect-patterns.md` in this skill directory for more details on the redirect setup. +If it is missing, restore it rather than adding per-code entries. Read `references/redirect-patterns.md` for background. -If `GITBOOK_TOKEN` is not set, skip this step and note it in the report. +This step no longer uses the GitBook API. The former `docs_redirects.py` / `GITBOOK_TOKEN` flow was left over from the GitBook era and does not apply to the Astro Starlight site. ### Step 8: Commit and open PR -If any pages were created: +If any pages were created, follow the "One standing PR per automation" contract in `.agents/references/skill-authoring-guidelines.md` — new error codes trickle in over time and each dated PR would edit the same `src/sidebar.ts` and `vercel.json`, so they would conflict. + +1. Look for an existing open PR, then check out the stable branch: + ```bash + gh pr list --repo warpdotdev/docs --state open \ + --search 'add error code pages for new platform errors in:title' \ + --json number,headRefName -1. Create a branch in the docs repo (e.g., `sync-error-docs/{date}`) -2. Commit all changes with a descriptive message -3. Push and open a PR targeting `main`. Write the body to a file: + git fetch origin + git checkout sync-error-docs 2>/dev/null || git checkout -b sync-error-docs origin/main + git rebase origin/main + ``` +2. Commit all changes with a descriptive message. +3. Push. If a PR already exists the push updates it — append the new codes under the existing `## New error code pages` heading rather than adding a duplicate heading, which `check_pr_body.py` rejects. If none exists, open one. Write the body to a file: ```bash cat > /tmp/sync-error-docs-pr-body.md << 'EOF' ## New error code pages @@ -142,9 +171,11 @@ Summarize what was found: - Total error codes in `platformerrors.go` - Number of existing doc pages - New codes that were missing pages (list them) -- Pages created, astro.config.mjs (sidebar config) entries added, redirects configured +- Pages created, `src/sidebar.ts` entries added, redirects configured - Or confirm everything is already in sync +Follow the actionable-only Slack rule in `.agents/references/skill-authoring-guidelines.md`: a run that finds everything already in sync writes this report to the run output and posts nothing. + ## References - `references/error-page-template.md` — template for new error doc pages diff --git a/.agents/skills/sync-error-docs/references/redirect-patterns.md b/.agents/skills/sync-error-docs/references/redirect-patterns.md index 0e53da69a..d4a66a958 100644 --- a/.agents/skills/sync-error-docs/references/redirect-patterns.md +++ b/.agents/skills/sync-error-docs/references/redirect-patterns.md @@ -1,6 +1,6 @@ # Redirect Patterns -Two types of redirects are needed for each error code to ensure the API's `type` URI resolves to the correct documentation page. +How an API error's `type` URI resolves to its documentation page, and what (if anything) a new error code needs. ## Background @@ -10,75 +10,77 @@ The `platformerrors` package defines `ProblemTypeBaseURI = "https://docs.warp.de https://docs.warp.dev/errors/insufficient_credits ``` -But the actual documentation page lives at: +The documentation page lives at: ``` https://docs.warp.dev/reference/api-and-sdk/troubleshooting/errors/insufficient-credits ``` -Two redirects bridge this gap: -1. A **site-level redirect** from `/errors/{underscore_code}` to the full doc page URL -2. A **Astro Starlight space redirect** from the underscore filename to the hyphen filename (within the reference space) +Two gaps separate them: -## 1. Site-level redirect (Astro Starlight API) +1. **Path prefix** — `/errors/{code}` versus the full `/reference/api-and-sdk/troubleshooting/errors/{code}` path. +2. **Separator** — error codes are underscored (`insufficient_credits`); page slugs are hyphenated (`insufficient-credits`). - -This redirect is created via the GitBook API using `scripts/docs_redirects.py`. It requires the `GITBOOK_TOKEN` environment variable. +Both are handled by entries in `vercel.json` at the repo root. All redirects for the site live in that one file. -### Create a redirect +## 1. Prefix redirect (already generic — no per-code work) -```bash -python3 docs/scripts/docs_redirects.py create \ - --source "/errors/{underscore_code}" \ - --destination-json '{"kind": "url", "url": "https://docs.warp.dev/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}"}' -``` +A catch-all already covers every error code, current and future: -### Check if a redirect already exists - -```bash -python3 docs/scripts/docs_redirects.py get-by-source \ - --source "/errors/{underscore_code}" +```json +{ + "source": "/errors/:code", + "destination": "/reference/api-and-sdk/troubleshooting/errors/:code/", + "statusCode": 308 +} ``` -### List existing error redirects +`:code` is forwarded unchanged, so `/errors/insufficient_credits` lands on the underscored path, which the separator redirect below then resolves. + +Because this rule is generic, **adding a new error code requires no change here.** Just confirm it still exists: ```bash -python3 docs/scripts/docs_redirects.py list --search "/errors/" +grep -F '"/errors/:code"' vercel.json ``` -### Notes - -- The script uses hardcoded org ID (`-MbqIZLCtzerswjFm7mh`) and site ID (`site_FKhQ8`) which are the Warp docs defaults -- If `GITBOOK_TOKEN` is not set, skip this step and report it — the redirect can be created manually later -- The destination `kind` is `"url"` (external URL redirect), not `"site-page"` +If it is missing, restore this single rule rather than adding one entry per code. -## 2. Astro Starlight space redirect (vercel.json (redirects)) +There is also a bare `/errors` redirect pointing at the errors index, which likewise needs no per-code maintenance. -This redirect lives in `docs/src/content/docs/reference/vercel.json (redirects)` and handles in-space navigation where someone might visit the underscore form of the path. +## 2. Separator redirect (one entry per code) -### Format +This is the only redirect a new error code needs. It maps the underscored form to the hyphenated page slug: -Add an entry under the `redirects:` key: +```json +{ + "source": "/reference/api-and-sdk/troubleshooting/errors/{underscore_code}", + "destination": "/reference/api-and-sdk/troubleshooting/errors/{hyphen-code}/", + "statusCode": 308 +} +``` -```yaml -redirects: - # ... existing redirects ... +Example for `insufficient_credits`: - # Error code underscore→hyphen redirects - api-and-sdk/troubleshooting/errors/{underscore_code}: api-and-sdk/troubleshooting/errors/{hyphen-code}.md +```json +{ + "source": "/reference/api-and-sdk/troubleshooting/errors/insufficient_credits", + "destination": "/reference/api-and-sdk/troubleshooting/errors/insufficient-credits/", + "statusCode": 308 +} ``` -### Example +Rules: -For `insufficient_credits`: +- `source` has a **leading slash**, no trailing slash, and no file extension. +- `destination` has a **trailing slash**. Every existing error redirect uses one. +- Always set `"statusCode": 308`. +- Add the entry near the other `/reference/api-and-sdk/troubleshooting/errors/` redirects so they stay grouped. +- Check for an existing entry before adding, so re-runs stay idempotent: -```yaml - api-and-sdk/troubleshooting/errors/insufficient_credits: api-and-sdk/troubleshooting/errors/insufficient-credits.md -``` + ```bash + grep -F '/reference/api-and-sdk/troubleshooting/errors/{underscore_code}"' vercel.json + ``` -### Notes +## Note on the former GitBook flow -- Paths are relative to the space root (defined by `root: ./` in the yaml) -- The source path has NO leading slash and NO `.md` extension -- The destination path includes the `.md` extension -- Group error redirect entries together with a comment for clarity +Earlier versions of this reference created the prefix redirect through the GitBook API using `scripts/docs_redirects.py` and a `GITBOOK_TOKEN` secret. That approach no longer applies: the docs moved from GitBook to Astro Starlight on Vercel, redirects are plain JSON in `vercel.json`, and the prefix case is now covered by the generic `/errors/:code` rule. Neither the script nor the token is needed. diff --git a/.agents/skills/sync-openapi-spec/SKILL.md b/.agents/skills/sync-openapi-spec/SKILL.md index 3084789e8..a90df6954 100644 --- a/.agents/skills/sync-openapi-spec/SKILL.md +++ b/.agents/skills/sync-openapi-spec/SKILL.md @@ -94,16 +94,33 @@ If `npm run build` fails, the most common cause is a malformed path or missing ` ### Step 6: Commit and open a PR +This skill maintains **one** long-lived sync PR rather than one per run — see "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md`. A dated branch per run would produce multiple open PRs that all rewrite the same generated YAML file and conflict with each other. + +```bash +# Is there already an open OpenAPI sync PR? +gh pr list --repo warpdotdev/docs --state open \ + --search 'sync agent-api-openapi.yaml from warp-server in:title' \ + --json number,headRefName + +git fetch origin +# If the PR exists, continue on its branch and rebase; otherwise create it from main. +git checkout sync-openapi-spec 2>/dev/null || git checkout -b sync-openapi-spec origin/main +git rebase origin/main +``` + +Re-run `--mode apply` after the rebase so the regenerated subset reflects the latest `main`, then commit: + ```bash -git checkout -b sync-openapi-spec/YYYY-MM-DD git add developers/agent-api-openapi.yaml git commit -m "docs: sync agent-api-openapi.yaml from warp-server Co-Authored-By: Oz " -git push origin sync-openapi-spec/YYYY-MM-DD +git push origin sync-openapi-spec ``` -Open a draft PR. Write the body to a file before creating the PR — the diff output from Step 2 can be long and is prone to repetition-loop degeneration when passed inline: +If a PR already exists for this branch, the push updates it — do not open a second one. Replace the diff summary in the existing body with the current run's output (this spec is regenerated wholesale each run, so the latest diff supersedes rather than accumulates) and note the date of the refresh. Re-run `check_pr_body.py` after editing. + +If no PR exists, open a draft one. Write the body to a file before creating the PR — the diff output from Step 2 can be long and is prone to repetition-loop degeneration when passed inline: ```bash cat > /tmp/sync-openapi-pr-body.md << 'EOF' diff --git a/.agents/skills/sync_terminology/SKILL.md b/.agents/skills/sync_terminology/SKILL.md index 58fc91152..029c4c250 100644 --- a/.agents/skills/sync_terminology/SKILL.md +++ b/.agents/skills/sync_terminology/SKILL.md @@ -2,14 +2,14 @@ name: sync_terminology description: >- Sync the Warp terminology glossary from the Notion Dictionary to the repo. - Fetches the Notion Dictionary page, compares with .warp/references/terminology.md, + Fetches the Notion Dictionary page, compares with .agents/references/terminology.md, and opens a PR for any additions or changes. Flags repo-only terms that are missing from Notion. Use on a weekly schedule or manually when terminology changes. --- # Sync Terminology from Notion -Keep `.warp/references/terminology.md` in sync with the canonical Notion Dictionary. +Keep `.agents/references/terminology.md` in sync with the canonical Notion Dictionary. **Direction:** Notion → repo. Notion is the source of truth. If the repo has terms not in Notion, flag them for addition to Notion rather than removing them from the repo. @@ -42,7 +42,7 @@ Parse both sections. Extract each term with its: ### Step 2: Read the current terminology.md -Read `.warp/references/terminology.md` from the repo. Parse each entry, extracting: +Read `.agents/references/terminology.md` from the repo. Parse each entry, extracting: - **Name** (the bolded term) - **Definition** (the text after the em dash) - **Usage note** (the italic `*Usage note:*` line, if present) @@ -69,10 +69,19 @@ If both lists are empty, report "Terminology is in sync" and stop. Do not create If there are new or changed terms from Notion: -1. Create a new branch: +1. Check out the standing branch. This skill maintains **one** long-lived sync PR rather than one per run — see "One standing PR per automation" in `.agents/references/skill-authoring-guidelines.md`. ```bash - git checkout -b sync-terminology/YYYY-MM-DD + # Is there already an open terminology sync PR? + gh pr list --repo warpdotdev/docs --state open \ + --search 'sync terminology from Notion Dictionary in:title' \ + --json number,headRefName + + git fetch origin + # If the PR exists, continue on its branch and rebase; otherwise create it from main. + git checkout sync-terminology 2>/dev/null || git checkout -b sync-terminology origin/main + git rebase origin/main ``` + Do not create a date-suffixed branch. Terminology drift accumulates across weeks, and a dated branch per run produces a pile of PRs that all edit the same two files and conflict with each other. 2. For each **new term**, add it to the appropriate category section in `terminology.md`: - Match the category from Notion to the existing `##` sections in the file @@ -95,14 +104,16 @@ If there are new or changed terms from Notion: ### Step 6: Commit and open a PR ```bash -git add .warp/references/terminology.md AGENTS.md +git add .agents/references/terminology.md AGENTS.md git commit -m "docs: sync terminology from Notion Dictionary Co-Authored-By: Oz " -git push origin sync-terminology/YYYY-MM-DD +git push origin sync-terminology ``` -Open a PR. Write the body to a file before creating the PR — lists of changed terms can be long and are prone to repetition-loop degeneration when passed inline: +If a PR already exists for this branch, the push updates it — do not open a second one. Append this run's terms to the existing body under its existing headings rather than adding a new dated section: `check_pr_body.py` rejects duplicate headings, so per-run copies of `## Terms added` would fail the check and block the update. Fetch the current body first and make a minimal additive edit. + +If no PR exists, open one. Write the body to a file before creating the PR — lists of changed terms can be long and are prone to repetition-loop degeneration when passed inline: ```bash # Write body to a temp file first diff --git a/.agents/skills/triage-issue-local/SKILL.md b/.agents/skills/triage-issue-local/SKILL.md index a96aeac53..3c8ad0d30 100644 --- a/.agents/skills/triage-issue-local/SKILL.md +++ b/.agents/skills/triage-issue-local/SKILL.md @@ -17,7 +17,7 @@ marks as overridable. - Distinguish between **site bugs** (the docs platform is broken — search, navigation, rendering, styling, build errors) and **content issues** (documentation is incorrect, outdated, missing, unclear, has typos, or has formatting problems). Most issues will be content issues. - When the reporter provides a `docs.warp.dev` URL, map it to the source file: `docs.warp.dev/agent-platform/capabilities/skills` → `src/content/docs/agent-platform/capabilities/skills.mdx`. - When an issue claims documentation is wrong about a feature's behavior, verify against the source repos (`warp-internal` for client/Rust, `warp-server` for server/Go) before concluding the docs are incorrect. Docs are the primary source of truth for user-facing content, but source code is essential for validating accuracy when disputed. -- Check the docs style guide (`AGENTS.md`) and terminology glossary (`.warp/references/terminology.md`) to validate that issue reports reference features by their correct names and that any proposed fixes would align with current terminology. +- Check the docs style guide (`AGENTS.md`) and terminology glossary (`.agents/references/terminology.md`) to validate that issue reports reference features by their correct names and that any proposed fixes would align with current terminology. - If the report is a support question (e.g., "How do I do X?") rather than an issue with the docs themselves, direct the reporter to the [Warp community Slack](https://go.warp.dev/join-preview) and the [docs site](https://docs.warp.dev). ## Follow-up question limit diff --git a/.agents/skills/update-changelog/SKILL.md b/.agents/skills/update-changelog/SKILL.md index 419a9ad17..2b1db3541 100644 --- a/.agents/skills/update-changelog/SKILL.md +++ b/.agents/skills/update-changelog/SKILL.md @@ -214,10 +214,32 @@ Edit `src/content/docs/changelog/{year}.mdx` (the year file determined in Step 2 ### Step 7: Create branch, commit, and open PR +Unlike the other recurring docs automations, this skill correctly opens **one PR per release** rather than one standing PR — each changelog entry describes a distinct release and should merge on its own. The "One standing PR per automation" contract in `.agents/references/skill-authoring-guidelines.md` does not apply here. + +It does still have a stacking hazard: every changelog PR inserts at the top of the same `src/content/docs/changelog/{year}.mdx` file, so two unmerged release PRs will conflict, and merging them out of order puts the entries in the wrong sequence. + +**Check for an unmerged prior changelog PR before branching:** + ```bash -# Create a new branch -git checkout -b changelog/{base_version} +gh pr list --repo warpdotdev/docs --state open \ + --search 'docs: changelog in:title' --json number,title,headRefName +``` + +If one exists, branch from it rather than `main` so the entries chain in release order instead of colliding: +```bash +git fetch origin +# No prior open changelog PR: +git checkout -b changelog/{base_version} origin/main +# Prior open changelog PR on branch changelog/{earlier_version}: +git checkout -b changelog/{base_version} origin/changelog/{earlier_version} +``` + +When you branch from a prior changelog PR, say so in the new PR body and note that the earlier PR must merge first. If more than two changelog PRs are open at once, that is a review backlog worth flagging in the PR body rather than chaining further. + +Then commit and open the PR: + +```bash # Stage and commit git add src/content/docs/changelog/ git commit -m "docs: add changelog entry for {base_version} diff --git a/.agents/skills/weekly-404-monitor/SKILL.md b/.agents/skills/weekly-404-monitor/SKILL.md index 13a7f2cb6..be4edcd32 100644 --- a/.agents/skills/weekly-404-monitor/SKILL.md +++ b/.agents/skills/weekly-404-monitor/SKILL.md @@ -57,11 +57,22 @@ Compare this week's uncovered gaps against last week's uncovered gaps (from step - **Significant gaps** = uncovered URLs with `hits_this_week >= REPORT_MIN_HITS`. These are worth a redirect and belong in the headline. - **Long-tail noise** = uncovered URLs below the threshold. Because the monitor is only weeks old (low sample), most broken URLs are hit once by bots, crawlers, or stale bookmarks, so the raw uncovered and "new gap" counts churn heavily week-over-week and overstate the problem. Roll these up into a single count — never list them individually or put them in the headline. -### 5. Post Slack summary +### 5. Determine whether the run is actionable -Post a Slack message using the Block Kit format defined in the "Slack message format" section below. +This agent posts **at most one message per run**, and only when the run is actionable. Follow the actionable-only rule in `.agents/references/skill-authoring-guidelines.md`. -If `BUZZ_SLACK_TOKEN` is unavailable, write the full Slack message body to the run output instead and note that Slack posting was skipped. +Decide here; send later. The order for the rest of the run is: write the CSV (step 6), run Phase 2, then send one combined message if this step marked the run actionable **or** Phase 2 produced redirect results. + +The run is actionable when any of these is true: +- `significant_uncovered_count` is 1 or more (at least one gap at or above `REPORT_MIN_HITS`). +- Phase 2 found at least one HIGH-confidence redirect, or produced MEDIUM-confidence suggestions needing human review. +- The run was blocked by a failure (Metabase error, missing `docs_404` data, truncated `vercel.json`). + +Stay silent when the only findings are long-tail URLs below the threshold. That is the normal steady state once redirect coverage is healthy, and posting it weekly is what trains the channel to ignore this report. The run log entry and the CSV artifact remain the record of every run. + +**Do not send the message from this step.** Because Phase 2 can add HIGH-confidence redirects and MEDIUM-confidence suggestions to the same report, defer the send until Phase 2 completes, then send one combined message. Two messages per run for a single report is exactly the noise this removes. + +If `BUZZ_SLACK_TOKEN` is unavailable, write the full message body to the run output instead and note that Slack posting was skipped. ### 6. Write CSV artifact @@ -92,10 +103,16 @@ Use Slack Block Kit. The message should be scannable in under 30 seconds. _+{long_tail_count} other uncovered URLs under {report_min_hits} hits each (mostly bots/old links) — see CSV._ *{resolved_count} resolved since last week* (redirect added or traffic stopped) +🔀 *Redirect drafter:* {N} HIGH-confidence redirects → {PR URL, or "none found this week"} +{MEDIUM-confidence suggestions needing review, if any:} +{path} → {suggested destination} [{reason}] + → Add redirects for the gaps above: `vercel.json` › `redirects` array (PR against `main`) → Full breakdown: {oz_run_url} ``` +The redirect-drafter line is part of this single message, not a separate post. Omit the line entirely when Phase 2 found nothing and the message is being sent because of significant gaps alone. + Build `{oz_run_url}` at runtime — never hard-code the Oz host (for example `app.warp.dev` or `oz.warp.dev`). This agent may run on staging or production, and a hard-coded host resolves to the wrong environment (or a generic Runs page). Resolve the environment-correct link from your current run, substituting the run ID this agent is executing as: ```bash oz-dev run get "" --output-format json | jq -r '.session_link' @@ -112,7 +129,9 @@ Rules: ## Phase 2: Redirect drafter -After the Slack summary is posted and the CSV artifact is written, continue with Phase 2. Phase 2 proposes redirect entries for high-confidence uncovered 404 gaps, reducing the manual work required from the docs team. +After the CSV artifact is written, continue with Phase 2. Phase 2 proposes redirect entries for high-confidence uncovered 404 gaps, reducing the manual work required from the docs team. + +Phase 2 runs **before** the Slack message is sent, so its results can be folded into that single message (see step 5). ### Threshold and confidence scoring @@ -135,11 +154,21 @@ For each qualifying uncovered URL, attempt to find a redirect target using these Open a **draft** PR only when at least 1 HIGH-confidence redirect is found. Always pass `--draft` to `gh pr create`. -PR title: +This skill follows the "One standing PR per automation" contract in `.agents/references/skill-authoring-guidelines.md`. Every redirect PR edits the same `redirects` array in `vercel.json`, so a dated PR per week would guarantee conflicts. + +Use the stable branch `docs/404-redirects` and a title with no date: ```text -docs: add redirects for top uncovered 404 paths — YYYY-MM-DD +docs: add redirects for top uncovered 404 paths ``` +Look for an existing open PR before creating one: +```bash +gh pr list --repo warpdotdev/docs --state open \ + --search 'add redirects for top uncovered 404 paths in:title' \ + --json number,headRefName +``` +If one exists, check out `docs/404-redirects`, rebase on the latest `origin/main`, add this week's redirects, push, and append them to the existing PR body under its existing headings. Do not add a duplicate heading per week — `check_pr_body.py` rejects those. If none exists, create the branch from the latest `origin/main`. + For each proposed redirect, add an entry to the `redirects` array in `vercel.json`: ```json {"source": "/old/path", "destination": "/new/path", "statusCode": 308} @@ -152,21 +181,13 @@ PR body must include: Run `python3 .agents/skills/check_for_broken_links/check_links.py --internal-only` after editing `vercel.json` to catch any malformed destinations. -### Slack update +### Handing results to the Slack message -Append to the existing Slack message (or post a follow-up in the same thread): -``` -🔀 *Redirect drafter results* -HIGH-confidence PRs: N redirects → [PR URL] -MEDIUM-confidence suggestions: N paths (listed below for human review) -{path} → {suggested destination} [{reason}] -... -``` +Do not post a separate redirect-drafter message. Pass these values into the single Phase 1 message described in "Slack message format": +- The count of HIGH-confidence redirects and the PR URL, if a PR was opened or updated. +- Any MEDIUM-confidence suggestions, each with its proposed target and confidence reason, for human review. -If no gaps meet the threshold or no HIGH-confidence matches are found, post: -``` -🔀 *Redirect drafter*: No high-confidence redirects found this week. -``` +If no gaps meet the threshold and no HIGH-confidence matches are found, contribute nothing to the message and omit the redirect-drafter line. If that leaves the run with no significant gaps either, the run is a no-op: post nothing at all and let the run log record it. ### Threshold calibration note diff --git a/.agents/templates/conceptual.md b/.agents/templates/conceptual.md index de2bf40c5..592af528d 100644 --- a/.agents/templates/conceptual.md +++ b/.agents/templates/conceptual.md @@ -15,10 +15,10 @@ See AGENTS.md → Content variables for the full variable list and usage rules.] [Opening paragraph: What this feature/concept is and its primary benefit. 1-3 sentences. Lead with what the user gains from understanding this.] -## [Key concepts or components — sentence case. Rename to match the subject] +## [Key concepts or components — sentence case, specific to the subject. Not "Overview", "More details", or "Other"] [Explain the main ideas, components, or building blocks the reader needs -to understand. Use bulleted lists with bold term + dash + description.] +to understand. Use `*` bulleted lists with bold term + hyphen + description.] * **Concept A** - What it is and why it matters. * **Concept B** - What it is and why it matters. @@ -39,8 +39,9 @@ Help the reader decide if this is the right tool for their situation.] ## Related pages -[Cross-references to related features, procedural guides, and deeper references. -Use descriptive link text.] +[Required on new conceptual pages so the page does not dead-end. +Cross-reference related features, procedural guides, and deeper references. +Use descriptive link text that names the destination — not "here" or "this page".] * [Related feature](path/to/page.md) * [How to configure X](path/to/procedural-page.md) diff --git a/.agents/templates/feature-doc.md b/.agents/templates/feature-doc.md index a7c0ed4d0..11d1e8a23 100644 --- a/.agents/templates/feature-doc.md +++ b/.agents/templates/feature-doc.md @@ -35,22 +35,32 @@ Focus on what each capability means for the user.] Explain "what" and "why" before "how." Define new terms when they first appear. IMPORTANT: Do NOT include step-by-step procedures in this section. -Keep the conceptual and procedural sections clearly separated.] +Keep the conceptual and procedural sections clearly separated. +State platform, plan, preview, or interactive-only limits next to the behavior they constrain. +Do not invent internal tool names or implementation details the reader cannot act on. +Do NOT embed full error messages here — put failures in Troubleshooting at the end.] [SCREENSHOTS: If this feature has a distinctive visual surface (statusline, tab bar, side pane, multi-control layout), place a screenshot immediately after the paragraph that introduces that surface. Use descriptive alt text. Skip screenshots for purely textual CLI behavior.] ## [Usage/configuration section — sentence case. Rename to match the feature, e.g., "Creating environments", "Configuring integrations"] [PROCEDURAL section: step-by-step instructions. +Order sections for the reader: Prerequisites → setup/config → day-to-day usage → advanced options. Apply all procedural rules from AGENTS.md: - Motivate steps before giving instructions - Include expected outcomes after key steps -- Group related actions when they share the same UI context] +- Group related actions when they share the same UI context +- Name the app before the first Settings path or CLI command on this page +- Verify every UI label, Settings path, and CLI flag against source or the live product before publishing. If you cannot verify one, omit it or mark it with an inline `{/* VERIFY: ... */}` comment and report it per step 9.5 of the draft_docs skill +- Prefer durable actions and outcomes over ephemeral chrome (glyph colors, pure layout narration) +- Lead instructional sentences with the action or goal, then the control (✅ "To open the selector, press `Ctrl+E`." ❌ "To change either value, press `Ctrl+E`.") +- Disambiguate conditionals and multi-clause sentences so only one reading remains] ### Prerequisites [Bulleted list with inline context for each prerequisite. -Include: what the thing is, where to get it, link to full reference.] +Include: what the thing is, where to get it, link to full reference. +For integrations and team features, include admin requirements, who gains access after install, and any per-user auth steps.] ### [Task name — sentence case. e.g., "Create an environment with the CLI"] @@ -61,12 +71,22 @@ Include: what the thing is, where to get it, link to full reference.] ## [Additional sections as needed — sentence case. e.g., "Managing X", "Advanced usage"] [Repeat the conceptual or procedural pattern as appropriate. -Keep sections clearly delineated by type.] +Keep sections clearly delineated by type. +Avoid stacking multiple callouts; prefer short prose unless a caveat is easy to miss.] + +## Troubleshooting + +[Optional but recommended when the feature has common failures, permission errors, or exact platform error strings. +Place this section near the end of the page, before Related pages. +Format each item as: bold symptom or exact error message, then cause, then fix. +Do not scatter the same error callouts through earlier sections.] ## Related pages -[Cross-references to related features, next steps, deeper references. -Use descriptive link text.] +[Required on new feature and integration pages so the page does not dead-end. +Cross-reference related features, sibling integrations, next steps, and deeper references. +Use descriptive link text that names the destination — not "here" or "this page". +Include at least one sibling or overview link and one next-step workflow link.] * [Related feature](path/to/page.md) * [Deeper guide](path/to/page.md) diff --git a/.agents/templates/reference.md b/.agents/templates/reference.md index c9f707ca2..b1dc4a3f0 100644 --- a/.agents/templates/reference.md +++ b/.agents/templates/reference.md @@ -10,12 +10,12 @@ description: >- `import { VARS } from '@data/vars';` See AGENTS.md → Content variables for the full variable list and usage rules.] -# [Title — sentence case. Title convention: noun describing contents, e.g., "CLI commands", "Keyboard shortcuts"] +# [Title — sentence case. Title convention: noun describing contents, e.g., "CLI commands", "Keyboard shortcuts". Not a bare "Overview" or "Reference".] [Brief intro: what this reference covers and how to use it. 1-2 sentences. This is for lookup, not learning.] -## [Section name — sentence case. e.g., "Installing the CLI", "Authentication"] +## [Section name — sentence case and specific. e.g., "Installing the CLI", "Authentication". Not "More details".] [Introductory sentence or conceptual context for this section.] From 3cce11eb42b5842223be91bfcf579ed7570399f2 Mon Sep 17 00:00:00 2001 From: Rachael Rose Renk <91027132+rachaelrenk@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:33:12 -0600 Subject: [PATCH 2/3] docs(skills): document the deployed monthly cron for improve-drafting-skills The schedule was deployed as `0 15 1 * *` (the 1st of each month) rather than the `0 17 * * 1` + first-week-guard combination the skill documented. Both are correct and both fire exactly once a month, but the docs and the deployed schedule disagreed. Documented the deployed expression. Restricting only day-of-month is unambiguous because day-of-week stays `*`, so there is no ORing hazard. The tradeoff is noted: the 1st can land on a weekend, delaying review. Kept the first-week guard as a safety net and explained why, since it no longer trips on its own: it is what would narrow a day-of-week expression back to the first Monday, and it contains the blast radius if the day-of-month/day-of-week ORing mistake is ever reintroduced. Reworded the guard's skip message, which still referenced 'first Monday'. Co-Authored-By: Warp Agent --- .agents/skills/improve-drafting-skills/SKILL.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.agents/skills/improve-drafting-skills/SKILL.md b/.agents/skills/improve-drafting-skills/SKILL.md index d5cdacd8e..0d26a824a 100644 --- a/.agents/skills/improve-drafting-skills/SKILL.md +++ b/.agents/skills/improve-drafting-skills/SKILL.md @@ -11,22 +11,26 @@ This skill is part of the self-improvement loop architecture. See the architectu ## Schedule -Monthly, first Monday of each month, 9am PT. +Monthly, on the 1st of each month. -Cron: `0 17 * * 1` (UTC) — every Monday — combined with the first-week guard in step 0, which exits on any Monday after the 7th. +Cron: `0 15 1 * *` (UTC) — 15:00 UTC is 8am PT during daylight saving, 7am PT otherwise. + +Restricting only day-of-month is unambiguous: because the day-of-week field is `*`, this fires exactly once a month and nothing else. The tradeoff is that the 1st can land on a weekend, so a PR opened then may wait until Monday for a reviewer. :::caution -Do **not** "simplify" this to `0 17 1-7 * 1`. That expression looks like "first Monday" but is not. When a cron expression restricts **both** day-of-month and day-of-week, the two fields are **ORed**, not ANDed — so `1-7 * 1` fires on every day of the 1st through 7th **and additionally** on every Monday, roughly 11 times a month. This exact mistake caused the agent to open four conflicting PRs in six days. Standard cron cannot express "first Monday" in one expression, so the day-of-month guard is required. +Do **not** "simplify" this to `0 15 1-7 * 1` or any other expression that restricts **both** day-of-month and day-of-week. Cron **ORs** those two fields rather than ANDing them, so `1-7 * 1` fires on every day of the 1st through 7th **and additionally** on every Monday — roughly 11 times a month. That exact mistake caused this agent to open four conflicting PRs in six days. Standard cron cannot express "first Monday" in a single expression: either restrict day-of-month alone (as here) or restrict day-of-week alone and gate the day-of-month in the skill, as step 0 does. ::: ## Step 0: First-week guard -Run this before anything else. The schedule fires every Monday, so a run outside the first week of the month must exit immediately without collecting signals, editing files, opening a PR, or posting to Slack. +Run this before anything else. It exits immediately — without collecting signals, editing files, opening a PR, or posting to Slack — on any run outside the first week of the month. + +Under the current `0 15 1 * *` schedule the guard never actually trips, since the day is always the 1st. Keep it anyway: it is the safety net that makes a cron mistake harmless. If someone later switches the schedule to a day-of-week expression such as `0 15 * * 1` (every Monday, to guarantee a weekday), this guard is what narrows it back to the first Monday. It also contains the blast radius if the ORing mistake above is ever reintroduced. ```bash DAY_OF_MONTH=$(date -u +%d) if [ "$DAY_OF_MONTH" -gt 7 ]; then - echo "Skipping: today is day $DAY_OF_MONTH, not the first Monday of the month. This agent runs monthly." + echo "Skipping: today is day $DAY_OF_MONTH, outside the first week of the month. This agent runs monthly." exit 0 fi ``` @@ -303,6 +307,6 @@ To deploy: 2. Verify the Oz environment has `BUZZ_SLACK_TOKEN` and `GROWTH_DOCS_SLACK_CHANNEL_ID` set. 3. In the Oz web app, create a new scheduled agent: - **Skill**: `improve-drafting-skills` from `warpdotdev/docs` - - **Schedule**: `0 17 * * 1` (UTC) = every Monday at 9am PT. The step 0 first-week guard narrows this to the first Monday only. See the caution in `## Schedule` for why the day-of-month field must stay `*`. + - **Schedule**: `0 15 1 * *` (UTC) = the 1st of each month. The step 0 first-week guard is retained as a safety net. See the caution in `## Schedule` before changing this — never restrict day-of-month and day-of-week in the same expression. - **Environment**: the same environment used for `weekly-404-monitor` (already has `warpdotdev/docs` checked out) - **Branch**: `main` From deb365998d637c483aabb124b0e8beb3ba0612e5 Mon Sep 17 00:00:00 2001 From: Rachael Rose Renk <91027132+rachaelrenk@users.noreply.github.com> Date: Fri, 7 Aug 2026 08:10:22 -0600 Subject: [PATCH 3/3] docs(skills): treat a log-branch fetch failure as blocked, not a stale fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review catch on improve-aeo-crosslink-skill: its step 0 said to fall back to the log copy in the current checkout when the branch fetch fails. That copy comes from `main` — precisely the truncated history the branch read exists to avoid — so the fallback reintroduced the problem this PR set out to fix. The file also contradicted itself: its Slack section already listed 'could not fetch the log branch' as a blocked-run example while step 0 said not to abort. The failure mode is quiet, which is what makes it worth fixing. A short log still parses; only the counts change. The run then either drops below the 8-entry minimum and reports 'too early to analyze', or clears the minimum on stale entries and proposes skill edits from an incomplete picture. Both look like ordinary outcomes, so nobody investigates. Both outer loops that read a log branch now stop before analysis on a fetch failure and post the blocked-run message. improve-drafting-skills already had this behavior documented and is unchanged. Also generalized the rule in the authoring guidelines, since this is a class of bug rather than a one-off: do not adopt a fallback that is quieter but less correct than failing. The test is whether the fallback can change the answer ratherratherratherratherratherratherratherratherratherratherratherratverage transparently — proceeding on one source signal and recording the gap — remain fine, because the reader can see what was missing. Co-Authored-By: Warp Agent --- .agents/references/skill-authoring-guidelines.md | 16 ++++++++++++++++ .../skills/improve-404-monitor-skill/SKILL.md | 2 ++ .../skills/improve-aeo-crosslink-skill/SKILL.md | 4 +++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.agents/references/skill-authoring-guidelines.md b/.agents/references/skill-authoring-guidelines.md index 18226510f..ad9698fc5 100644 --- a/.agents/references/skill-authoring-guidelines.md +++ b/.agents/references/skill-authoring-guidelines.md @@ -192,6 +192,22 @@ Treat `main` as the convenience case only — if the PR happens to have been mer **Do not make merging the standing log PR a step in the outer loop.** Merging is a human housekeeping task, not a precondition for analysis. An outer loop that tries to merge its own input couples the run to a repo write it may not have permission to perform, and turns an unmerged PR into a hard failure instead of a non-event. +**If the log branch cannot be fetched, stop — do not fall back to another copy.** Falling back to the checkout's copy reintroduces exactly the truncated history the branch read exists to avoid. Treat the fetch failure as a blocked run: post it and end before analysis. + +### Never fall back to lower-quality data + +The log-branch rule above is one instance of a general hazard. When a skill's primary data source is unavailable, the tempting fix is a fallback that keeps the run alive. Resist it whenever the fallback is **quieter but less correct** than failing. + +A degraded-data fallback is dangerous precisely because it does not look like a failure: + +- A shorter log still parses. Counts just come out lower. +- Lower counts silently cross thresholds in both directions. The run either reports "too early to analyze" and goes quiet, or clears the minimum on stale entries and proposes changes from an incomplete picture. +- Either way the output is shaped like a normal run, so no one investigates. + +The test to apply: **if the fallback can change the answer rather than just the completeness of the answer, do not take it.** Stop, mark the run blocked, and post — a loud failure costs one notification, while a quiet wrong answer costs trust in every quiet run that follows. + +Fallbacks are still fine when they degrade *coverage* transparently and the skill says so in its output — for example, proceeding with one source signal when a second is unavailable, while raising the confidence bar and recording the unavailability in the run log. The difference is that the reader can see what was missing. + ### Security boundary for signal logs Outer loops read logs that contain untrusted content: human review comments, PR descriptions, run output from external contributors. Apply these rules before using any log content to propose skill edits: diff --git a/.agents/skills/improve-404-monitor-skill/SKILL.md b/.agents/skills/improve-404-monitor-skill/SKILL.md index 73c95dc09..9e28de149 100644 --- a/.agents/skills/improve-404-monitor-skill/SKILL.md +++ b/.agents/skills/improve-404-monitor-skill/SKILL.md @@ -53,6 +53,8 @@ git checkout origin/chore/404-monitor-log -- .agents/logs/weekly_404_monitor_run Do not read it from `main`. `main` only has entries up to the last time a human merged the standing log PR, so it can silently under-count entries — which matters here because the 6-entry minimum and the 3+ occurrence thresholds below are both counts. Do not attempt to merge the standing log PR; merging is human housekeeping, not a precondition for this analysis. See "Log availability" in `.agents/references/skill-authoring-guidelines.md`. +**If the fetch fails, stop before step 1.** Do not fall back to the copy in the current checkout — that is the `main` copy, and a truncated log does not fail loudly, it silently changes the answer. A fetch failure is a blocked run, not a no-op: post the "run blocked" message (see step 7) naming the branch that could not be fetched, and end the run without analyzing or opening a PR. + Each entry captures: date, outcome (PR opened / No PR / No data), total 404 volume (this week vs last week), trend direction, significant gap count, redirect candidates processed, HIGH-confidence redirect count, PR URL, Oz run URL, and notes. Do not act if fewer than 6 entries exist. Write a "too early to analyze" notice to run output and skip the PR. diff --git a/.agents/skills/improve-aeo-crosslink-skill/SKILL.md b/.agents/skills/improve-aeo-crosslink-skill/SKILL.md index e2d56b79c..dc2a0bb00 100644 --- a/.agents/skills/improve-aeo-crosslink-skill/SKILL.md +++ b/.agents/skills/improve-aeo-crosslink-skill/SKILL.md @@ -61,7 +61,9 @@ The branch always holds the complete history. `main` only has entries up to the **Do not merge the standing log PR.** An earlier version of this skill attempted the merge as its first step. That coupled the analysis to a repo write the agent may not have permission to perform, and turned an unmerged PR into a hard failure rather than a non-event. Merging is human housekeeping; see "Log availability" in `.agents/references/skill-authoring-guidelines.md`. -If the branch does not exist or the fetch fails, fall back to reading `.agents/logs/aeo_crosslink_audit_runs.md` from the current checkout and note in the run output that the history may be incomplete. Do not abort the run. +**If the branch does not exist or the fetch fails, stop before step 1.** Do not fall back to the copy in the current checkout. That copy comes from `main`, which is exactly the truncated history this step exists to avoid — and a truncated log does not fail loudly, it silently changes the answer. With fewer entries the run either drops below the 8-entry minimum and reports "too early to analyze," or clears it with stale entries and proposes skill edits from an incomplete picture. Both look like normal outcomes. + +A fetch failure is a blocked run, not a no-op: post the "run blocked" message (see step 6) naming the branch that could not be fetched, and end the run without analyzing or opening a PR. ### 1. Parse the run log