# Copilot Instructions For AI coding agents working on this repository. > **Quick Reference — most frequently missed rules:** > 1. Every session ends with a Mini-Retro in `progress/YYYY-MM-DD-{slug}.md` — not optional. > 2. Expand ALL acronyms on first use in research items: `Full Name (ABBR)`. This is the #1 cause of review failures. > 3. Never edit `.github/skills/` — it is a read-only submodule. All skill changes go to `davidamitchell/Skills`. > 4. Never assume credentials or capabilities exist — STOP and ask if not listed in the credentials table. > 5. When something goes wrong twice: name the class, fix the root cause, update the Known Recurring Patterns table. > 6. Never edit `docs/` directly — it is auto-generated by the `build_site.yml` workflow. Edit `scripts/build_site.py` instead. > 7. **MUST run `make check` (ruff lint + format over the full repo) and `python -m pytest tests/ -q` and confirm both pass before claiming any coding task is finished.** `make check` runs `ruff check .` and `ruff format --check .` — the same scope as CI. Do not push or declare done without a clean CI run. If tests fail with `ModuleNotFoundError`, run `pip install -e ".[dev]"` first. > 8. **Never commit `docs/` changes on a feature branch.** Run `python scripts/build_site.py` locally to verify the build, but do not stage or commit `docs/`. The workflow regenerates the full site on merge to `main`. Committing `docs/` in a PR creates 300+ file diffs that cannot be reviewed. > 9. **Every source in a research item's `## Sources` section must include a URL** — `[Display Name](https://url)` or bare `https://url`. Sources without URLs cannot be verified or linked on the published site. A completed item with URL-free sources is not done. > 10. **For any non-trivial development work, follow the mandatory development loop: `swe` (design) → `tdd` (implement) → `code-review` (verify).** All three skills must be applied in sequence. Trivial = a single-line config change or typo fix with no logic. When in doubt, use the loop. > 11. **When assigned to a new research request issue: apply the `research-question` skill, add a backlog item, open a PR — stop. Do NOT conduct the research. That is the research-loop workflow's job.** > 12. **Pace deliberately: keep going, take your time, do not do too much at once, make todos, use subagents, refocus on the objective often, capture all work to be done, review often, go slow.** --- ## Project Overview A research tracking and tooling repository. It has two distinct purposes: 1. **Research tracking** — `Research/` holds individual research items in four states (`backlog/`, `in-progress/`, `completed/`). Items in `reviewing` status stay in `in-progress/` — the status field in frontmatter is the state machine; directory moves happen only on `start` (backlog → in-progress) and `complete` (in-progress → completed). This is a file-based Kanban board for research. 2. **Research tooling** — `src/` contains Python code for fetching, processing, and indexing research content (YouTube transcripts, papers, web pages, etc.). These two concerns are intentionally separate. Research items in `Research/` are not code; they are structured Markdown notes. The code in `src/` is the machinery that helps produce and process those notes. --- ## Non-Negotiable Constraints - **`docs/` is auto-generated — never edit it directly, and never commit it on a feature branch.** The `build_site.yml` workflow runs `scripts/build_site.py` on every push to `main` that touches `Research/completed/` or `scripts/`, then commits the output back. Any manual edits to `docs/` will be overwritten on the next build. To change the site, edit `scripts/build_site.py` instead. To verify a build locally, run `python scripts/build_site.py` and inspect the output — but do **not** `git add docs/`. Committing generated HTML in a PR creates 300+ file diffs that are unreadable and serve no review purpose. - **Never commit secrets.** API keys and credentials live in environment variables / GitHub Secrets. The `.env` file is gitignored. - **Every source in `## Sources` must have a URL.** Format: `[Display Name](https://url)` or bare `https://url`. Sources listed as plain text — author names, titles, or domain hints without a link — are incomplete. The published site cannot render them as links and the claim cannot be independently verified. Do not mark a research item as completed if any source lacks a URL. - **Keep research backlog (`Research/backlog/`) separate from repo improvement backlog (`BACKLOG.md`).** One is about *what to research*; the other is about *improvements to this repo's code and structure*. - **No breaking changes to research item format** without updating `Research/_template.md` and the ADR that documents the format choice. - **Editing completed research items requires a `versions:` entry.** Any change to a completed item's findings, claims, or conclusions must be accompanied by: (1) a new entry in the `versions:` frontmatter array added before committing, (2) a `progress/YYYY-MM-DD-{slug}.md` session log, and (3) the `sha` field populated after the commit. Tag additions and `## Related Items` changes are exempt. See ADR-0013. - **Every code slice must be end-to-end runnable** before being marked complete in `BACKLOG.md`. - **Log every meaningful session** by creating a file in `progress/YYYY-MM-DD-{slug}.md` where `{slug}` is a short hyphenated identifier for the session (e.g. `fix-wiki-links`, `w-0032-mcp-cleanup`, `ai-strategy`). Do not edit `PROGRESS.md` — it is now static by design. - **DO NOT ASSUME OR GUESS facts about the environment.** If you do not know whether a credential exists, whether a service is available, or whether a tool is capable of something — **STOP. Ask the owner before proceeding.** Guessing and being wrong wastes cycles and breaks trust. The cost of asking is zero. The cost of guessing wrong is not. - **DO NOT introduce new external services or credentials without explicit owner approval.** If your design requires something not already listed in the "Available credentials and services" table below, that is a hard stop — surface the gap and ask, do not proceed. - **Treat undocumented capabilities as unknown.** If a credential, service, or tool is in the approved table but its Notes column does not explicitly state it can do what your design requires, apply the same hard stop as for an unlisted item — do not assume, do not proceed, ask first. - **`.github/skills/` is a read-only submodule.** Never edit files inside `.github/skills/`. It is overwritten on every sync. All skill changes go to `davidamitchell/Skills` (open a PR there). Then advance the submodule pointer in this repo after the Skills PR merges. - **New *research* backlog items must be created in `Research/backlog/`**, never in `Research/in-progress/` or the repo root. Use the filename format `YYYY-MM-DD-.md`. Repo improvement tasks (coding, tooling, process changes) belong in `BACKLOG.md` — not in `Research/backlog/`. - **`Research/backlog/`, `Research/in-progress/`, `Research/completed/`, and `Knowledge/` each contain a `.gitkeep` file. Never delete it.** Git drops empty directories; the `.gitkeep` keeps each directory tracked so workflow `find` commands do not fail. - **Synthesis items belong in `Knowledge/`, not in `Research/`.** The autonomous research loop only processes items in `Research/backlog/`. Placing a synthesis item in `Research/backlog/` would cause the research loop to pick it up incorrectly. Synthesis items are always produced by the `synthesis-loop.yml` workflow and stored in `Knowledge/`. --- ## Working Environment These constraints are fixed. Every agent working on this repository **must** respect them. - **The owner interacts exclusively via the GitHub website or iOS GitHub app.** There is no local IDE, no `git clone`, and no terminal. - **All coding is done by the agent.** The owner does not write or edit code directly. - **Codespaces is not in use.** Do not rely on Codespaces features, devcontainers, or `$CODESPACE_*` environment variables. - **GitHub Copilot Spaces and GitHub Projects are fine to use if helpful**, but are not a requirement. Suggest them only when they add clear value. - **Agent interactions happen via PR comments, issue comments, or by starting a new agent task/session.** The owner may also trigger operations by clicking buttons on the GitHub website (e.g., the Actions tab "Run workflow" button). ### Available credentials and services The following table is the ground truth. Do not guess what exists outside this table. | Credential / Service | Available | Notes | |---|---|---| | `GITHUB_TOKEN` | ✅ Yes | Auto-provided by GitHub Actions; scoped to the current repo and its wiki (requires `permissions: contents: write`); cannot push to other repos | | `COPILOT_GITHUB_TOKEN` | ✅ Yes (add once) | GitHub PAT; required for Copilot CLI and direct `main` pushes | | `YOUTUBE_DATA_API` | ✅ Yes | YouTube video metadata | | `TAVILY_API_KEY` | ✅ Yes | Web search via Tavily API and `tavily-mcp` MCP server | | `GEMINI_API_KEY` | ✅ Yes | Google Generative AI API key; used by `scripts/enrich_items.py` and `enrich-items.yml` to classify research items with `ai_themes` via the `google-genai` SDK | | Any other credential | ❓ Unknown | **STOP. Ask the owner before designing anything that requires it.** | If a workflow you are designing requires a credential not in this table, **ask before building**. Do not proceed on the assumption it exists or can be easily added. ### Consequences for tooling design - **Prefer fully automated pipelines** (scheduled or event-triggered workflows) over requiring the owner to manually trigger anything. If a manual trigger is unavoidable, `workflow_dispatch` is the acceptable fallback — it surfaces as a "Run workflow" button on the Actions tab, accessible from the website and iOS app. - Do not require Codespaces secrets for production workflows — use repository secrets (Settings → Secrets and variables → Actions) instead. - Any manual step in a process should be achievable entirely through the GitHub website: creating files, triggering workflows, commenting on PRs. --- ## Coding Standards ### Language & Runtime - Python 3.11+ - Type hints on all public functions and class methods - `pyproject.toml` is the source of truth for dependencies and tool config ### Style - `ruff` for linting and formatting (line length 100) - Run `make check` before committing — this runs `ruff check .` + `ruff format --check .` over the full repo, matching CI scope exactly - No unused imports; no bare `except:` clauses ### Logging - Use the project logger (`src/logger.py`) — never `print()` in production code - Log levels: `DEBUG` for per-item detail, `INFO` for pipeline stages, `WARNING` for skipped/degraded paths, `ERROR` for failures ### Error Handling - Fetcher failures for a single source must not abort the entire run — log and continue - Network errors must be retried with exponential backoff (max 3 attempts) ### Testing - Tests live in `tests/`; use `pytest` - Mock all network calls - Unit tests on all business logic - **Bug fixes must start with a failing test.** Write the test first, confirm it fails, then fix and confirm it passes. - **Apply the testing pyramid to external service configuration.** Configuration that wires up an external service (MCP servers, API clients, credentials) is production code and must be proven to work at each relevant layer: - **Unit** — verify config file well-formedness (JSON validity, required fields, correct env var names). These tests prove the *file is correct*; they do NOT prove the *service works*. - **Integration** — call the actual service with the actual credential. This is the only test that proves the configuration works end-to-end. Mark with `pytest.mark.skipif(not os.getenv("KEY"), reason="KEY not set")` so they skip when credentials are absent, and expose the secret in `ci.yml` so they run in CI. - A config change that adds or modifies an external service entry is **not done** until the integration test exists and passes in CI. The absence of a credential is a blocker on shipping the change, not a reason to fall back to unit tests alone. --- ## Repository Layout ``` Research/ ├── README.md # Research workflow documentation ├── _template.md # Template for a new research item ├── backlog/ # Items not yet started (always contains .gitkeep) ├── in-progress/ # Items actively being researched (always contains .gitkeep) ├── completed/ # Finished research with findings (always contains .gitkeep) └── transcripts/ # Plain-text transcripts committed by fetch-transcript workflow Knowledge/ # Cross-item synthesis items (always contains .gitkeep) └── _template.md # Template for a new synthesis item src/ ├── main.py # CLI entry point ├── fetchers/ │ ├── __init__.py # Fetcher protocol and FetchedItem dataclass │ └── youtube.py # YouTube transcript fetcher ├── pipeline/ │ └── _gemini.py # Gemini client, adaptive rate limiter, model cascade ├── research/ │ ├── __init__.py │ └── item.py # ResearchItem dataclass and file I/O ├── logger.py # Logging setup └── config.py # Load and validate config scripts/ ├── build_site.py # Site generator — EDIT THIS to change the site ├── canonicalise_tags.py # Rewrite tags to canonical form (use docs/tag-vocabulary.md) ├── enrich_items.py # Add ai_themes to research items via Gemini └── extract_metadata.py # Pre-build metadata extraction config/ └── sources.yaml # Source configuration docs/ # ⚠️ AUTO-GENERATED — do not edit directly # Built by build_site.yml on every push to main that # touches Research/completed/, Knowledge/, or scripts/. # Edit scripts/build_site.py to change site output. docs-adr/ # Architecture Decision Records ├── README.md # ADR index └── NNNN-title.md .github/ ├── copilot-instructions.md # Agent instructions (this file) ├── mcp.json # MCP servers for GitHub Copilot Agent ├── skills/ # Agent skills (submodule: davidamitchell/Skills) └── workflows/ ├── ci.yml ├── build_site.yml # Builds docs/ and commits output back to main ├── synthesis-loop.yml # Manual synthesis workflow (workflow_dispatch) └── sync-skills.yml state/ └── index.json # URL deduplication state (written by StateStore; gitignored content) progress/ # Per-session logs (one file per session; no conflicts) └── YYYY-MM-DD-{slug}.md tests/ ``` --- ## Handling a New Research Request Issue When you are assigned to a GitHub issue that requests a new research topic — any issue whose purpose is to add something to investigate later — your job is to formulate a well-scoped research question and add it to the backlog. **Stop there.** Do not conduct the research. Do not move the item to `in-progress` or `completed`. Do not run the `research` skill. The `research-loop.yml` workflow handles research, review, and completion — with `research-review.yml` quality controls that a single SWE session bypasses. ### Steps **1. Apply the `research-question` skill** Open `.github/skills/research-question/SKILL.md` and run it against the issue title and body as the candidate topic statement. The skill's interaction protocol asks three questions before starting. **Do not ask these interactively** — extract the answers from the issue content: - *What decision or problem does the answer need to inform?* → infer from the issue body - *Are there known constraints?* → extract any mentioned limits, time horizons, or scope hints - *What output type is expected?* → default to `knowledge` unless the issue says otherwise Then: - Run the five-test quality check (Specific, Answerable, Scoped, Motivated, Decomposable) - Rewrite the question if it fails any test - Decompose into sub-questions - Produce: validated question, scope (in/out/constraints), context, approach, readiness verdict If the verdict is still **NEEDS REVISION** after two iterations, use the best available formulation, note the unresolved gaps in the Scope's Constraints field, and proceed. **2. Create the backlog item** Copy `Research/_template.md` to `Research/backlog/YYYY-MM-DD-.md`. Populate from the `research-question` skill output: | Template field | Source | |---|---| | `title` | Validated question (shortened to a noun phrase if needed) | | `added` | Today's date | | `status` | `backlog` | | `priority` | Infer from issue content (`high` / `medium` / `low`) | | `blocks` | List slugs of backlog items this must precede; otherwise `[]` | | `tags` | Extract from topic area | | `output` | Leave as `[]` — populated when the item completes | | `## Research Question` | Validated question verbatim | | `## Scope` | In scope / Out of scope / Constraints from skill output | | `## Context` | One-sentence context from skill output | | `## Approach` | Decomposed sub-questions from skill output | | `## Sources` | Any URLs or references mentioned in the issue | Leave `## Research Skill Output` and `## Findings` as empty template placeholders. **3. Create the session log** Create `progress/YYYY-MM-DD-.md` using the Mini-Retro format (mandatory — see Quick Reference item 1): ```markdown # YYYY-MM-DD -- Add backlog item () **Completed:** - `Research/backlog/YYYY-MM-DD-.md` — added from issue #NNN; ## Mini-Retro 1. **Did the process work?** 2. **What slowed down or went wrong?** 3. **What single change would prevent this next time?** 4. **Is this a pattern?** ``` **4. Commit and open a PR targeting `main`** ```bash git add Research/backlog/YYYY-MM-DD-.md progress/YYYY-MM-DD-.md git commit -m "research: add backlog item - " git push origin ``` Then open a PR targeting `main` via the GitHub website or `gh pr create --base main`. **Stop here. Do not proceed to Starting Research or Conducting Research.** --- ## Research Item Workflow ### Which backlog? Before creating any backlog item, decide where it belongs: | Is the item… | Goes in… | |---|---| | A topic to investigate (external question, technology, concept) | `Research/backlog/YYYY-MM-DD-.md` | | A coding, tooling, or process improvement to this repo | `BACKLOG.md` (W-XXXX entry) | The autonomous research loop picks up **only** items in `Research/backlog/`. Placing a repo improvement task there causes the loop to treat it as a research topic. When in doubt: if the output type would be `tool`, `agent`, or improvements to the repo's own infrastructure, it belongs in `BACKLOG.md`. ### Adding a New Research Item **If you are handling a GitHub issue that requests new research**, follow the full process in **[Handling a New Research Request Issue](#handling-a-new-research-request-issue)** above — it uses the `research-question` skill and is the authoritative guide. **If you are creating a backlog item for another reason** (e.g. from an Open Questions entry in a completed item): 1. Copy `Research/_template.md` to `Research/backlog/YYYY-MM-DD-short-title.md` 2. Fill in: title, added date, priority, blocks, tags, question, scope, context, approach, and any known sources 3. Create `progress/YYYY-MM-DD-{slug}.md` with Mini-Retro — note the new backlog item and its origin 4. Commit with message: `research: add backlog item - ` ### Starting Research 1. Run the CLI command to move the item and stamp the `started` date automatically: ```bash python -m src.main research start ``` This moves the file from `Research/backlog/` to `Research/in-progress/`, updates `status` and `started`, and **stages the move in the git index automatically**. 2. Add an entry to `progress/YYYY-MM-DD-{slug}.md` — note the item has moved to in-progress 3. Commit with message: `research: start - ` (No `git add Research/` needed — the file move is already staged.) ### Conducting Research Once the item is in `Research/in-progress/`, run the **`research` skill** in full and use available **MCP tools**. **Invoke the research skill:** - **GitHub Copilot Agent:** Open `.github/skills/research/SKILL.md` and follow its process step by step as the agent - **Fallback (submodule not initialised):** Follow Steps 3–7 of `research-prompt.md`, which mirrors the skill process in full **Write the full skill output into `## Research Skill Output` as you work through each section:** - **§0** — restate the question, confirm scope and constraints - **§1** — decompose Approach sub-questions into atomic questions - **§2** — gather evidence iteratively; label each claim **[fact]**, **[inference]**, or **[assumption]** with source - **§3** — separate facts from inferences and assumptions explicitly - **§4** — identify and resolve internal contradictions - **§5** — re-examine findings through relevant lenses (technical, regulatory, economic, historical, behavioural) - **§6** — write the synthesis (executive summary, key findings, evidence map, assumptions, analysis, risks, open questions) - **§7** — validate the full output; confirm every claim is sourced or labelled The `## Research Skill Output` section is **retained verbatim** in the completed item. **Use MCP tools throughout the investigation** (full reference: [Using MCP in research tasks](#using-mcp-in-research-tasks)): - `tavily` — discover sources, verify claims, and find current information - `fetch` — retrieve full page content from each source URL - `arxiv` — locate and fetch academic papers referenced in Sources - `sequential_thinking` — plan the synthesis structure before writing Findings - `time` — get today's date for `started` and `completed` timestamps - `filesystem` — read the item file and write Research Skill Output and Findings directly - `memory` — persist state if the investigation spans multiple sessions - `github` — read issue or PR context when the item was spawned from one **Seed `## Findings` from `## Research Skill Output §6`:** Once §6 Synthesis is written, copy and expand it into the structured Findings subsections (Executive Summary, Key Findings, Evidence Map, Assumptions, Analysis, Risks/Gaps, Open Questions, Output). No new claims may appear in Findings that are not already in the Research Skill Output. ### Completing Research 1. Run the CLI command to mark the item as ready for automated review: ```bash python -m src.main research draft ``` This updates `status: reviewing` in frontmatter but **does not move the file** — it stays in `Research/in-progress/`. 2. Commit and trigger the automated review workflow: ```bash git add Research/in-progress/ git commit -m "research: draft - " git push origin main gh workflow run research-review.yml --field item_path=Research/in-progress/ ``` 3. If the review fails (a GitHub issue labelled `research-review` is opened), address the violations and loop back to Conducting Research, then re-run `research draft`. 4. Once the review passes, close the review issue if one was opened, then move the item to completed: ```bash python -m src.main research complete ``` This moves the file to `Research/completed/`, updates `status` and `completed` date, and **stages the move in the git index automatically**. 5. Check `learnings.md` — if any key finding from this item adds signal to an existing cross-cutting thread, update that thread now. If the item establishes a genuinely new cross-cutting theme, add a new numbered thread entry. 6. Create `progress/YYYY-MM-DD-{slug}.md` — record findings summary and any outputs produced 7. Commit with message: `research: complete - ` (No `git add Research/` needed — the file move is already staged.) ### Output Types Research can produce one or more of the following outputs (record in the `output` field of the template): - **skill** — a new skill for the `davidamitchell/Skills` repo - **tool** — a new or updated tool in `src/` - **agent** — a new agent configuration - **knowledge** — a structured note or ADR - **backlog-item** — spawns one or more new repo improvement items in `BACKLOG.md` --- ## Adding a New Source Type (Code) 1. Create `src/fetchers/.py` implementing the `Fetcher` protocol 2. Add config schema to `config/sources.yaml` 3. Register in `src/main.py` 4. Write unit tests in `tests/test_fetchers_.py` 5. Write an ADR in `docs-adr/` if the approach involves a significant design decision 6. Update `BACKLOG.md` (mark slice done) and create `progress/YYYY-MM-DD-{slug}.md` --- ## Adding an ADR **Use the `adr` skill** — `.github/skills/adr/SKILL.md` — whenever writing or updating an ADR. The skill provides the exact format, section structure, coded-bullet identifiers, and quality checklist. ADRs are stored in `docs-adr/`. File naming: `NNNN-short-title.md` (zero-padded 4 digits). Update `docs-adr/README.md` after adding. Status values: `proposed` → `accepted` → `superseded` / `deprecated` ### When to write an ADR (required) An ADR **must** be written any time a change involves one or more of the following: | Trigger | Examples | |---|---| | New external dependency or third-party API | Adding a new pip/npm package to production code; integrating a new cloud API | | New GitHub Actions workflow with autonomous side-effects | Workflows that commit to `main`, modify issues/PRs, or call external APIs | | Delivery or publication channel for research content | How completed items are published (wiki, Pages, email, MCP); index format choices | | Storage or indexing approach | How processed-item state is persisted; choice of DB vs flat file vs YAML index | | Research item format or schema change | Adding or renaming YAML front-matter fields; changing the directory layout | | Reversal cost > 1 hour | Any architectural decision where undoing the choice requires significant rework | ### When NOT to write an ADR Do not write an ADR for routine work: - Bug fixes that don't change the architecture - Adding a new Python module or test file within an existing pattern - Updating a workflow that already exists (e.g., changing a schedule or dropdown value) - Completing or moving a research item through the `backlog → in-progress → reviewing → completed` lifecycle - Updating documentation, comments, or session logs in `progress/` ### Checklist before closing a slice If your change touches any "when to write" trigger above, the slice is not done until the ADR exists, is `accepted`, and is linked in `docs-adr/README.md`. --- ## Agent Skills `.github/skills/` is a git submodule tracking [`davidamitchell/Skills`](https://github.com/davidamitchell/Skills). A weekly workflow (`.github/workflows/sync-skills.yml`) advances the submodule pointer to the latest commit. > **Setup required:** After a standard `git clone`, the skills directory is empty. Run `git submodule update --init` from the repository root to populate it before using any skill. | Skill | When it applies | GitHub Copilot | |---|---|---| | `adr` | Creating or updating Architecture Decision Records | read `SKILL.md` and apply manually | | `backlog-manager` | Adding, prioritising, or reviewing backlog items | read `SKILL.md` and apply manually | | `citation-discipline` | Ensuring claims are sourced and referenced | read `SKILL.md` and apply manually | | `code-review` | Reviewing code after implementation — correctness, security, performance, maintainability, style | **mandatory** after every non-trivial implementation; read `SKILL.md` and apply | | `remove-ai-slop` | Reviewing output for hollow filler language | read `SKILL.md` and apply manually | | `research` | Conducting structured research on a topic | read `SKILL.md` and apply manually | | `research-question` | Formulating and scoping a new research question before adding it to the backlog | read `SKILL.md` and apply manually | | `speculation-control` | Flagging uncertain claims vs established facts | read `SKILL.md` and apply manually | | `strategic-persuasion` | Building audience-targeted persuasive content | read `SKILL.md` and apply manually | | `strategy-author` | Producing or reviewing strategy documents | read `SKILL.md` and apply manually | | `swe` | Designing or implementing software — applies SOLID principles, design patterns, REST constraints | **mandatory** before writing any non-trivial implementation; read `SKILL.md` and apply | | `tdd` | Implementing any feature, bug fix, or behaviour change using Red-Green-Refactor | **mandatory** for all non-trivial code changes; read `SKILL.md` and apply | ### Invoking skills **GitHub Copilot:** Skills in `.github/skills/` are readable as context files. Read the relevant `SKILL.md` directly (e.g., open `.github/skills/research/SKILL.md` in your context window) and follow its process step by step as the agent — no user action required. **Fallback (any agent, submodule not initialised):** Note the gap in `BACKLOG.md`. Proceed without synthesising a substitute skill. Do not halt work. To add a new skill: add it to the Skills repo first; it will be picked up on the next sync. --- ## MCP Configuration MCP server configs are defined in: - `.github/mcp.json` — GitHub Copilot Agent (requires `type: "stdio"` on each entry) - `.mcp.json` — Claude Code and other agents Both files must stay in sync. The following 9 servers are configured in both files. ### Server Reference | Server | Runtime | Purpose | Secrets required | |---|---|---|---| | `fetch` | `python -m mcp_server_fetch` | Fetch web pages and URLs for research sourcing | none | | `sequential_thinking` | `npx @modelcontextprotocol/server-sequential-thinking` | Step-by-step reasoning for complex research synthesis | none | | `time` | `python -m mcp_server_time` | Current date/time for timestamping research items | none | | `memory` | `npx @modelcontextprotocol/server-memory` | Persistent knowledge graph across sessions | none | | `git` | `python -m mcp_server_git` | Read git history, diffs, and commit context | none | | `filesystem` | `npx @modelcontextprotocol/server-filesystem` | Read/write research files in the current working directory (repo root) | none | | `arxiv` | `python -m arxiv_mcp_server` | Search and fetch arXiv papers for academic sourcing | none | | `github` | `npx @modelcontextprotocol/server-github` | Read GitHub issues, PRs, and repo data | `GITHUB_PERSONAL_ACCESS_TOKEN` (repository secret or `.env`) | | `tavily` | `npx tavily-mcp@latest` | Real-time web search, extraction, mapping, and crawl via Tavily API | `TAVILY_API_KEY` (repository secret or `.env`) | ### Session-start MCP availability check At the start of each session, note which configured MCP servers are actually running. If a configured server is unavailable, log it and fall back to built-in equivalents rather than attempting tool calls that will fail silently. | Environment | Typically available | Typically unavailable | |---|---|---| | GitHub Copilot agent (cloud runner) | `fetch`, `git`, `github` (built-in) | `arxiv`, `filesystem`, `memory`, `sequential_thinking`, `tavily` | | Local dev / other agent runtime | all 9 servers (if pip/npm packages installed and secrets set) | servers with missing secrets (`github`, `tavily`) | Substitutions when MCP servers are unavailable (use whatever equivalent capability your runtime provides): - `tavily` → built-in web search (e.g., `web_search` tool in Copilot/Claude) - `arxiv` → fetch arxiv.org URLs directly using available HTTP/fetch tools - `filesystem` → built-in file read/write tools (bash, view, edit, create tools) - `memory` → session notes; record key facts in a `progress/` session log - `sequential_thinking` → inline chain-of-thought reasoning ### Python MCP servers — installation `fetch`, `time`, `git`, and `arxiv` are Python packages (the npm packages were removed from the registry). Install them in the project virtualenv or Codespaces environment: ```bash pip install mcp-server-fetch mcp-server-time mcp-server-git arxiv-mcp-server ``` ### npx MCP servers — Node.js requirement `sequential_thinking`, `memory`, `filesystem`, `github`, and `tavily` use `npx` to run. **Node.js (v18 or higher) must be installed.** It is pre-installed on: - GitHub Actions runners (all standard runner images) - The devcontainer base image (`mcr.microsoft.com/devcontainers/python:3.11`) For local dev outside these environments, install Node.js from [nodejs.org](https://nodejs.org/) if `npx --version` returns an error. The `-y` flag in each config entry (`npx -y `) auto-accepts the one-time package download — no separate `npm install` step is needed. ### Using MCP in research tasks When executing the `research` skill or conducting a research item end-to-end: - Use **`fetch`** to retrieve raw web page content from a known URL (replaces manual `web_fetch` calls when the MCP server is running). - Use **`tavily`** for web discovery and structured content extraction (requires `TAVILY_API_KEY`). Use `tavily-search` to find relevant sources and links; use `tavily-extract` to get clean, structured text from a URL (higher fidelity than `fetch` for content-heavy pages). - Use **`arxiv`** to locate and fetch academic papers referenced by a research item. - Use **`sequential_thinking`** to plan multi-step research synthesis before writing findings. - Use **`memory`** to persist cross-session state about ongoing research threads. - Use **`filesystem`** to read/write research Markdown files directly (serves the current working directory). - Use **`git`** to inspect commit history when reviewing what has already been processed. - Use **`time`** to stamp `added`, `started`, and `completed` dates correctly. - Use **`github`** to read issue/PR context when a research item was spawned from an issue. --- ## Gemini API ### Free-tier quota pools (per model, independent) Each model has its own daily quota. The cascade in `src/pipeline/_gemini.py` walks through models newest-first and advances when a model's daily quota is exhausted. Per-model RPM is set in `_MODEL_RATES`; never guess or use a single global default. Cascade priority order (highest capability → highest throughput): | Priority | Model | RPM | RPD | Notes | |---|---|---|---|---| | 1 | `gemini-3.1-pro` | — | — | Check aistudio.google.com | | 2 | `gemini-2.5-pro` | — | — | Check aistudio.google.com | | 3 | `gemini-3-flash` | — | — | Check aistudio.google.com | | 4 | `gemini-2.5-flash` | 5 | 20 | Confirmed from aistudio dashboard (2026-05-14); thinking model | | 5 | `gemini-3.1-flash-lite` | — | — | Check aistudio.google.com | | 6 | `gemini-2.5-flash-lite` | 10 | 1 500 | Confirmed free tier (2026-05-12) | Cascade advances on **daily quota exhaustion only**. RPM backoff is handled by the adaptive rate limiter — it reads `x-ratelimit-*` headers and waits for the reset window without advancing the cascade. Model IDs are discovered at runtime via `client.models.list()` — never hard-coded or guessed. Starting model is configured in `config/sources.yaml → gemini.gemini_model`. --- ## GitHub Actions / Codespaces - CI: `.github/workflows/ci.yml` — lint + test on every push/PR - Skills sync: `.github/workflows/sync-skills.yml` — weekly Monday 06:00 UTC - **Research loop: `.github/workflows/research-loop.yml`** — autonomous research backlog worker; feeds `research-prompt.md` to the GitHub Copilot CLI in a loop, one fresh session per item, commits directly to `main`. Runs automatically on weekday mornings (3 items/day) and on demand via `workflow_dispatch`. Requires `COPILOT_GITHUB_TOKEN` repository secret (GitHub PAT with Copilot access). See ADR-0004 for safety controls. - **Transcript fetch: `.github/workflows/fetch-transcript.yml`** — manually triggered (`workflow_dispatch`); fetches YouTube auto-generated captions via `yt-dlp` and commits a plain-text file to `Research/transcripts/.txt`. If YouTube blocks the request (cloud IP restriction), the workflow commits step-by-step instructions for adding the transcript manually via the GitHub website. ### Research loop — setup and usage (no IDE required) **One-time setup:** Add a GitHub PAT as a repository secret: 1. Create a Personal Access Token at [github.com/settings/tokens](https://github.com/settings/tokens) with `repo` scope and Copilot access enabled 2. Settings → Secrets and variables → Actions → New repository secret → name: `COPILOT_GITHUB_TOKEN`, value: your PAT **How the loop works:** - Each run processes one or more backlog items. - Each item gets a **fresh Copilot session** (new context window) — matching the Ralph Wiggum pattern. - Copilot reads `research-prompt.md`, picks the highest-priority backlog item, researches it, marks it as a draft, triggers the quality review workflow (and waits for it), then completes the item and commits it + a new `progress/` session log directly to `main`. - The outer `while` loop restarts Copilot for the next item until `max_items` is reached or the backlog is empty. **Automatic schedule:** Runs weekdays at 07:00 UTC, processes 4 items per day. **Manual trigger:** 1. Go to the repository on GitHub 2. Click the **Actions** tab 3. Click **"Research Loop"** in the left sidebar 4. Click **"Run workflow"** → select `max_items` from the dropdown (default `1`) → click **"Run workflow"** **Safety controls:** The loop has multiple runaway-loop guards — see ADR-0004 for full details. Conservative defaults: max 1 item per manual run, max 4 per scheduled run, 150-minute job timeout, 10-iteration hard ceiling, 30-second inter-iteration sleep, abort after 2 consecutive Copilot failures. **Tuning:** Edit `research-prompt.md` to adjust what Copilot looks for, how findings are structured, or which items to prioritise. The prompt is the only lever — no code changes needed. ### How to trigger the transcript workflow (no IDE required) 1. Go to the repository on GitHub 2. Click the **Actions** tab 3. Click **"Fetch YouTube Transcript"** in the left sidebar 4. Click **"Run workflow"** → select the branch → paste the video URL → click **"Run workflow"** 5. If `yt-dlp` succeeds, the transcript is committed automatically 6. If it fails (YouTube blocks the cloud IP), open `Research/transcripts/-README.md` for manual instructions --- ## Slice Completion Checklist Before marking a backlog slice as done: - [ ] Code merged to the development branch - [ ] `make check` passes (ruff lint + format) — **run this locally before every push** - [ ] `make test` passes — **run this locally before every push** - [ ] Both checks confirmed clean — do not declare finished until you have seen passing output - [ ] **`docs/` NOT staged or committed** — run `git status` and confirm no `docs/` changes are staged - [ ] For non-trivial work: `swe` skill applied before implementation (design documented or stated) - [ ] For non-trivial work: `tdd` Red-Green-Refactor cycle followed — every new behaviour had a failing test first - [ ] For non-trivial work: `code-review` skill applied — all `Critical` and `High` findings resolved - [ ] Session log created in `progress/YYYY-MM-DD-{slug}.md` - [ ] Architectural decisions recorded as ADRs using the `adr` skill — index updated in `docs-adr/README.md` - [ ] README and user-facing documentation updated if behaviour or interface changed - [ ] `.github/copilot-instructions.md` updated if a new convention, constraint, or lesson emerged --- ## Working Methodology ### Pacing and Scope Work deliberately. These rules apply to every session: - **Keep going** — finish what you started before starting something new. - **Take your time** — correctness over speed. A slow, correct step is worth more than a fast, wrong one. - **Do not do too much at once** — one slice at a time. Complete it, commit it, then move to the next. - **Make todos** — if you spot work that is out of scope for the current slice, write it down as a backlog item and return to the current task. - **Use subagents** — delegate parallel or specialised work to subagent sessions rather than bundling everything into one pass. - **Refocus on the objective often** — before every commit, re-read the original task statement. Confirm you are still solving the stated problem. - **Capture all work to be done** — if you discover scope mid-session, add it to the backlog before continuing. Do not carry undocumented intent. - **Review often** — read back what you have written before moving to the next step. Catch errors early. - **Go slow** — when uncertain, stop and think. A paused session that asks a question is better than a fast session that ships the wrong thing. ### Root cause before action When something is broken or unclear, spend time on *why* before reaching for a fix. Most problems fall into one of three categories: **Context gap** — the information needed to do the right thing was never provided. Surface the missing information; don't guess. **Model error** — the mental model of how the system works is wrong. Update the model first, then re-derive the solution. **Prompt/specification error** — the task was stated in a way that made the wrong solution look right. Re-examine framing before retrying. ### Before writing code - State what you understand the problem to be. If the statement is fuzzy, stop and sharpen it. - Identify what you don't know. Missing information is better surfaced early. - Note any assumptions explicitly. ### Development Loop — mandatory for non-trivial work Any change that involves logic, behaviour, or structure beyond a single-line config fix or typo correction is **non-trivial** and requires the full development loop. When in doubt, treat it as non-trivial. The three skills are applied in strict sequence: **Step 1 — Design with `swe`** Open `.github/skills/swe/SKILL.md` and apply it before writing any code. The `swe` skill applies SOLID (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) principles, design patterns, and REST constraints to produce a coherent design with explicit trade-offs. Do not write production code until the design is clear. **Step 2 — Implement with `tdd`** Open `.github/skills/tdd/SKILL.md` and follow its Red-Green-Refactor cycle for every unit of new behaviour. The Iron Law applies: no production code without a failing test first. Delete any production code written before its test — do not adapt or keep it as reference. **Step 3 — Verify with `code-review`** Open `.github/skills/code-review/SKILL.md` and apply a full multi-dimensional review (correctness, security, performance, maintainability, style) to the completed implementation. Resolve all `Critical` and `High` findings before marking the work done. **Step 4 — Document and record decisions** Before marking the work done, ask: - Does the README or any user-facing documentation need updating to reflect this change? - Was a significant architectural or design decision made? If yes, use the `adr` skill (`.github/skills/adr/SKILL.md`) to write an Architecture Decision Record (ADR) and index it in `docs-adr/README.md`. - Do these changes warrant updating `.github/copilot-instructions.md` — for example, a new convention, constraint, or process that future sessions should follow? Do not defer documentation. Update it now, in this session, before the context degrades. **Composability note:** The three skills are designed to compose. `swe` answers *how should this be structured?* `tdd` answers *does it do what it should?* `code-review` answers *is there anything wrong with what was built?* Running all three closes the design-implement-verify loop. ### When an attempt fails - Do not retry the same thing. Understand why it failed first. - "It didn't work" is not a diagnosis. "It didn't work because X was Y when I expected Z" is. ### Progress and documentation Update documentation before context degrades, not after. - After each meaningful unit of work: commit, update status, note what changed and why. - `progress/` is the handoff document. A new session reading the latest file there should know exactly where to pick up. --- ## Continuous Improvement & Learning > Complete the work. Improve the system. If something was hard, slow, or confusing — fix it, document it, or raise it. ### Identity as Architect You are the **Architect** of this repository, not just a user. Your role is to complete work *and* to improve the system doing the work. If something was hard, slow, or confusing — fix it, document it, or raise it. Always ask: *"Is this the best version of this system, or just a working one?"* These standards are self-applied. The owner should not need to request a mini-retro, an ADR, a CHANGELOG entry, or a progress log. If you are waiting to be asked — the process has failed. ### Every Session Ends with a Mini-Retro Before closing any session or completing any PR, add a **Mini-Retro** to the session log in `progress/YYYY-MM-DD-{slug}.md`. It is **not optional**. It is how the system learns. Answer these six questions — briefly, honestly: 1. **Did the process work?** Was the approach sound? Did the plan hold? 2. **What slowed down or went wrong?** No blame — just facts. 3. **What single change would prevent this next time?** If nothing: say so. 4. **Is this a pattern?** Have you seen this friction before? If yes, it deserves a fix, not just a note. 5. **Does any documentation need updating?** README, user-facing docs, or inline comments — if this change affects how someone uses or understands the system, update the docs now. If a significant architectural decision was made, write an ADR using the `adr` skill. 6. **Do the default instructions need updating?** If a new convention, constraint, or hard-won lesson emerged from this session, add it to `.github/copilot-instructions.md` now, before the context is gone. > Do not just answer — make the change. If the answer is "document it", document it now. If it is "add a backlog item", add it now. ### Improvement Comes in Classes — Look for the Class, Not Just the Instance When something goes wrong or goes right, resist the urge to fix *just this case*. Ask: **what class of problem is this?** | Signal | Class to consider | |---|---| | You had to look something up that should be documented | → Add it to the agent instructions or a skill | | A step was manual that could be automated | → Raise a backlog item or add a workflow | | A decision was unclear or had to be re-made | → Write an ADR | | A note or file was out of date | → Mark it `superseded_by`, don't delete it | | The same friction appears in two retros | → It's a pattern. Prioritise fixing the root cause | | Missing skill | Add to backlog; do not synthesise a substitute | ### Known Recurring Failure Patterns The following patterns have appeared **three or more times** across sessions. If you see one occurring again, treat it as a class problem — not a one-off — and fix it at the root. | Pattern | Impact | Root fix in place | |---|---|---| | Acronym not expanded on first use (LLM, CLI, SDK, PAT, MCP, RAG, etc.) | Every automated research review fails citation-discipline for this reason | Inline acronym audit added to `research-prompt.md` Step 6 | | Editing `.github/skills/` files directly | Submodule content is overwritten on every `sync-skills.yml` run; edits are silently lost | "Read-only submodule" rule added to Non-Negotiable Constraints | | `web search synthesis` used as a citation | Not a verifiable source; fails citation-discipline pre-output check | Explicitly prohibited in `research-prompt.md` Step 6 companion skill checks | | Surface evaluation (self-inspection only, no external benchmarking) | Misses factual errors, architectural flaws, and gaps that are only visible when compared to published best practices or observable system behaviour | Evaluation protocol: always (1) search external best practices, (2) audit facts against actual files, (3) use open issues as evidence | | Committing `git add ` without staging the corresponding file deletion | Leaves the old file tracked in git but absent from disk (unstaged deletion); the next `git pull --rebase` fails with "You have unstaged changes" | `cmd_start` and `cmd_complete` now call `_git_add(root.parent, src, dest)` after each file move — see ADR-0011 | | Running `ruff check`/`make check` with narrower scope than CI | CI runs `ruff check .` (full repo); if `make check` only covered `src/ tests/`, format violations in `scripts/` were invisible locally but failed CI | `make check` now uses `.` to match CI exactly; `PreToolUse` hook blocks `git commit` if `make check` fails | | Test imports a package not in `pyproject.toml` dev deps | Test collection fails in CI with `ModuleNotFoundError`; passes locally only if the package happens to be globally installed | Before committing any test file that imports a third-party package, verify the package is listed under `[project.optional-dependencies] dev` in `pyproject.toml` and that `pip install -e ".[dev]"` + full pytest passes clean | | Committing `docs/` on a feature branch after running `build_site.py` locally | Creates 300+ unreadable file diffs in the PR; CI regenerates `docs/` on merge anyway so the commit is pure noise | Run `build_site.py` locally only to verify output; never `git add docs/`. Run `git status` before every commit and confirm `docs/` is absent. The workflow is the sole write path for `docs/`. | | W-number collision in `BACKLOG.md` — two branches both append the same next sequential number | Merge conflict in `BACKLOG.md`; the higher-numbered item must be renumbered and the conflict resolved manually | Before appending a new W-XXXX item: run `git fetch origin main` and inspect the last item number on `main` to avoid collisions. The research loop commits directly to `main`, so any open PR that adds a W-item is at risk if the loop runs between branch creation and merge. **Long-term fix tracked in W-0055: split `BACKLOG.md` into per-item files in `backlog/` to make all appends naturally conflict-free.** | | Key Finding full-sentence bold in `## Findings` | Bolding entire multi-clause claim sentences triggers the `remove-ai-slop` "inline-header list" violation — the same as bullet lists where every item opens with a bolded header | Only use bold for a key term being defined, a UI label, or a technical identifier that a reader scans for; never bold a full sentence. Run `grep -E "^\d+\. \*\*" ` before committing to detect violations. | | Missing epistemic labels in Key Findings parentheticals | Parentheticals of the form `(high confidence; source: URL)` without a preceding `[fact]`, `[inference]`, or `[assumption]` tag fail citation-discipline; the review correctly flags every KF without a label | Every KF parenthetical must open with the label: `([inference]; high confidence; source: URL)`. Run `grep -E "^\d+\." \| grep -v "inference\|fact\|assumption"` to find unlabeled KFs before committing. | | Fabricated specific quantitative estimates in `[inference]` claims | A claim labels a specific number, range, or percentage as `[inference]` and attributes it to a cited source, but the cited source contains no such data. Triggers `speculation-control` even when the epistemic label is present, because the specific number carries the appearance of measured fact. | After drafting §2 Investigation, grep for numeric claims (digits followed by %, "person-month", "range from", "order of magnitude") and verify each maps directly to a cited source. Convert uncited estimates to `[assumption]` with an explicit "not empirically measured" note, or remove the specific numbers entirely. | | Wikipedia as sole source for named historical cases | A named historical project or event (e.g., a named ontology, a named AI project) is cited as evidence using only a Wikipedia article. Wikipedia is tertiary; `citation-discipline` fails any substantive claim with Wikipedia as the only source. | Either (a) add a primary or peer-reviewed secondary source for the named case, or (b) generalise the claim from a named case to a class of projects (e.g., "large-scale commonsense ontology projects") backed by a secondary survey source. Run `grep -n "wikipedia" ` and verify every sole-Wikipedia line has a co-citation. | | Acronym expanded only inside a code block or metadata fence | Expanding an acronym inside a fenced code block, YAML metadata fragment, or `§4` consistency-check block does not satisfy `citation-discipline`'s first-use requirement. The first **narrative prose** occurrence is what the review audits. | Before triggering review, run `grep -n "\b[A-Z]{2,}\b" ` over all prose sections (§0–§7, Findings) and confirm each acronym's expansion appears in narrative text, not only in code/metadata fences. The §7 self-audit note must correctly identify the first **prose** use, not the first code-block use. | | Temporal notation abbreviations (BCE, CE, AD, BC) not expanded on first narrative prose use | BCE (Before Common Era) and CE (Common Era) are initialisms; `citation-discipline` treats them the same as LLM, API, etc. The first occurrence in Sources metadata or code fences does not satisfy the requirement; the first **narrative prose** use must carry the expansion. | Before triggering review, run `grep -n "\bBCE\b\|\bCE\b\|\bAD\b\|\bBC\b" ` and confirm each appears with its expansion (e.g., "Before Common Era (BCE)", "Common Era (CE)") at the first narrative prose use. Update the §7 acronym audit to list BCE and CE explicitly. | | `[fact]` claim with only a tertiary source (encyclopedias such as Britannica or Wikipedia) | `citation-discipline` prohibits labelling a claim `[fact]` when the sole citation is an encyclopedia or other tertiary source. Tertiary sources may be cited for orientation or as secondary support, but not as the primary evidence for a `[fact]` claim. | Change the label to `[inference]` if the claim is derived from the secondary/tertiary source, or locate a primary source to support the `[fact]` label. Run `grep -n "\[fact.*britannica\|\[fact.*wikipedia" ` before triggering review. | | `[fact]` on a historical origin claim citing only a secondary practitioner source (e.g., Google SRE Workbook, ITIL Foundation Guide, DORA State of DevOps report) | Historical origin claims — "X was invented in YYYY", "X originated at organisation Y", "ICS was developed in 1968 in response to Z" — require a primary source (original paper, contemporaneous record, first-person account, or official standards body history). Secondary practitioner handbooks are insufficient evidence to establish historical fact. `citation-discipline` treats this the same as a tertiary-source `[fact]` label. | Change the label to `[inference]` unless a primary source can be cited. Run `grep -n "\[fact\].*originated\|\[fact\].*developed in\|\[fact\].*invented\|\[fact\].*introduced in" ` before triggering review. Google SRE Workbook, ITIL guides, DORA reports, and Team Topologies are authoritative for current practice claims but not for claims about historical origins. | | Unlabeled evaluative judgments in Analysis ("This objection is valid", "neither refutes", "stronger evidence", "most decision-useful") | Evaluative phrases that assess the persuasive force of an argument or compare positions are `[inference]` claims, not factual assertions. `speculation-control` flags any such phrase without an epistemic label in the Findings section. | Before triggering review, run `grep -n "is valid\|does not refute\|is stronger\|is weaker\|better evidence\|more persuasive" Findings/Analysis sections` and add `[inference; source: URL]` labels to every match. | | Alternative-explanations gap in synthesis items — cited sources discuss multiple drivers but only one is argued for | When a synthesis item cites sources (DORA, ToC, academic literature) that discuss multiple drivers of an outcome, the Findings Analysis section must address each prominent driver as either a rival, a complementary, or an excluded explanation. Omitting a well-known competing hypothesis that the cited sources directly discuss triggers the `peer-reviewer / alternative-explanations` FAIL. | Before triggering the first review, list every major driver or constraint type discussed in the cited sources. For each one not addressed in Analysis, either add a paragraph explaining why it is excluded or complementary, or explicitly note the scope limitation in Risks/Gaps. | | Unlabeled closing/summary sentences and §6 Synthesis / Findings Executive Summary opening sentences | The final sentence of an Analysis paragraph and the opening sentence of `§6 Synthesis` and Findings `### Executive Summary` are written as restatements and inherit no label from surrounding sentences. Each must carry its own `[inference; source: URL]` label. This is the single most common cause of `citation-discipline` and `speculation-control` failures in the pass 5–10 range. Cross-framework synthesis claims (e.g., "ITIL 4 is consistent with BCBS 328") require `[inference]` not `[fact]` because the cited source supports only one side. **Also applies to §3 Reasoning numbered causal chain lists**: every numbered item in the §3 causal chain must carry its own label and URL-backed source; a trailing label on item 5 does not extend backward to items 1-4. | Before triggering any review: (1) manually check the first sentence of `§6 Synthesis` and `### Executive Summary` for a trailing `[inference; source: URL]`; (2) `grep -n "\bmost\b\|\bglobally\b\|\ball \b\|\balways\b"` and confirm each has a label and source; (3) scan every paragraph-closing sentence in Analysis for a label; (4) scan every numbered item in §3 causal chain lists — each must carry its own label and source. | | Executive Summary multi-sentence paragraph with a single trailing citation block | A multi-sentence Executive Summary that ends with one `[inference; source: URL1; URL2]` block is non-conforming: each sentence that makes a factual or inferential claim needs its own per-sentence inline citation. This is distinct from the single-sentence labelling requirement and is missed even when the closing-sentence rule is applied. **This applies to every multi-sentence paragraph in Findings** (Analysis, Risks, etc.), not only the Executive Summary. | Before triggering review, split every multi-sentence paragraph in Findings (Analysis, Risks, Open Questions, Output) into individual sentences and check that each ends with its own inline label and source — not only the paragraph's final sentence. A single trailing block covering multiple sentences in Analysis has caused the same failure as in Executive Summary. Use `python -c "import re; ..."` or manual review to confirm per-sentence citation coverage throughout. | | Citing a source that predates the standard or framework being described | Using a paper or report published before a standard's formal release date as the primary citation for a claim about that standard's specified behaviour is flagged as a citation-discipline violation. Example: Egyed & Grünbacher (2002) cannot be the primary source for a SysML v1 static-link behaviour claim because SysML v1 was standardised in 2006. The paper may be valid as motivating background or historical context, but the primary source for the standard's specified behaviour must be the specification itself or a document post-dating the standard. | Before citing a paper for a framework-specific behaviour claim, check the paper's publication year against the framework's standardisation date. If the paper predates the standard, change the citation to the official specification URL (e.g., `https://www.omg.org/spec/SysML/1.6/PDF`) and demote the paper to corroborating context or remove it from the claim binding entirely. | | Mid-paragraph evaluative/comparative claims between two labeled sentences | A sentence that uses comparative or evaluative language ("safer", "more usable", "most severe", "only rational", "only defensible", "more suitable") and appears between two already-labeled sentences is still flagged as unlabeled by the review. The review treats every sentence independently regardless of surrounding labels. This is a distinct failure class from the closing-sentence and §3-causal-chain patterns already in this table. | Before triggering review, run `grep -n "safer\|more usable\|most.*\b\|only rational\|only defensible\|more suitable\|harder to\|easier to" ` over all Findings and §6 Synthesis prose and confirm each match has a per-sentence `[inference; source: URL]` trailing label. Fixing only the sentences named in a review report is insufficient; fix all sentences of this class in the same pass. | | `[fact]` label on a derived structural claim (absence-of-mechanism inferred from framework description) | If a claim is derived from a framework's described structure rather than directly asserted by the source — for example, "i-star applies no formal completeness condition" inferred from a description of how i-star works — the label must be `[inference]`, not `[fact]`. The source may be a seminal primary paper directly about that framework, but if the claim requires the reader to infer an absence or a boundary condition from that description, the direct-assertion requirement for `[fact]` is not met. Review will reject `[fact]` if the equivalent claim in §2 Investigation is labeled `[inference]` from the same sources. | After drafting §2 Investigation, grep for `[fact]` labels where the corresponding §2 claim was labeled `[inference]`. For any absence-of-mechanism claim ("applies no formal X", "does not specify Y", "has no Z mechanism"), default to `[inference]` unless the source directly asserts "framework F has no X" rather than merely describing F without mentioning X. Run `grep -n "\[fact\]" ` and audit every instance against whether the source text directly states the claim or whether the claim is derived from a described property. | | §5 Depth and Breadth Expansion — multi-sentence paragraphs where only the opening sentence is labeled | Every paragraph in §5 typically opens with a labeled sentence followed by 2–3 sentences that continue the same argument but carry no labels. Review treats each sentence independently: a label on sentence 1 does not extend to sentences 2 or 3. This pattern has caused citation-discipline and speculation-control failures on multiple items. | Before triggering any review, scan every paragraph in `§5 Depth and Breadth Expansion` and confirm that every sentence after the first labeled one also carries its own `[inference; source: URL]` trailing label. Do not assume label inheritance. Run `grep -n "^\*\*.*lens" ` to identify all §5 paragraphs, then manually read each one sentence by sentence. | | Full-sentence bold in Findings Assumptions | Findings Assumptions bullet points that open with a bolded full assertion sentence — e.g., `**The shadow IT literature's findings apply to AI tooling.**` — trigger the "inline-header list" violation. Bold in Findings should apply only to key terms being defined, UI labels, or technical identifiers, not to entire claim sentences. | Before triggering review, run `grep -n "^\- \*\*" ` over `### Assumptions` and confirm no bullet opens with a bolded full sentence. If found, remove the bold from the opening clause and keep only inline term-bold where justified. | | Inferential reasoning sentence built on a `[fact]`-labeled premise inherits no label of its own | A sentence that draws an interpretive conclusion from an immediately preceding `[fact]`-labeled sentence — using connectives such as "weighs against", "would not be expected if", "is inconsistent with", "this suggests" — is itself an `[inference]`, not part of the preceding fact. Review flags these as mislabeled `[fact]` even though the underlying premise sentence is solidly sourced, because the derived reasoning step is the item's own interpretive contribution, not something the cited source states. | Before triggering review, scan every sentence that follows a `[fact]`-labeled sentence in the same paragraph for reasoning connectives ("weighs against", "would not be expected if", "is inconsistent with", "this suggests", "this implies"). Confirm each such sentence carries its own `[inference; source: URL]` label rather than being read as continuing the preceding fact's label. | | Mid-sentence bold used for emphasis in Key Findings | The `remove-ai-slop` Boldface rule prohibits bolding a statistic or phrase purely for emphasis, not only full-sentence bolding. Rewriting a full-sentence-bold Key Finding to bold only a short statistic or phrase (e.g. `outperformed the baseline by **77.6%**`) still fails review, because the rule reserves bold exclusively for a term being defined, a UI label, or a technical identifier — never for emphasis, regardless of span length. | Before drafting or after any bold-related review fix, run `grep -n "^[0-9]\+\. .*\*\*" ` over Key Findings and confirm zero matches unless the bolded span is a defined term, UI label, or technical identifier. Do not treat "shorten the bold span" as a valid fix for a full-sentence-bold violation. | | High-confidence Key Finding whose multiple cited sources originate from the same organisation or project | Citing two or more sources for a claim satisfies the multi-source citation format, but if all cited sources are produced by the same vendor, research team, or project (e.g. an arXiv paper and that same project's own documentation page), they do not count as independent for the purpose of assigning `High` confidence. Review treats same-origin source pairs the same as a single source. | Before assigning `high confidence` to a Key Finding with multiple citations, check whether the sources share an organisation, author group, or project. If they do, downgrade to `medium` or locate at least one genuinely independent corroborating source. | | `research-review.yml`'s own commit-and-push step races with a concurrent `docs: rebuild site` or research-loop commit landing on `main` | The review job pushes rejected (`! [rejected] HEAD -> main (fetch first)`); the review's PASS/FAIL verdict is still visible in the workflow logs, but `review_count` frontmatter is never incremented on `main` since the commit never landed. Observed twice in one session cluster (`tbox-abox-graphrag` and `aws-agentcore-knowledge-context-layer`), meeting the three-strikes threshold. | After triggering `research-review.yml`, always check `git log origin/main -- ` (or diff the pulled frontmatter's `review_count`) to confirm the review's own commit actually landed before counting it toward the max-review-count budget. A review pass whose commit failed to push does not count and the violations it reported must still be fixed and resubmitted under the same pass number. | | A bullet's leading `[fact]`/`[inference]`/`[assumption]` marker is internally consistent with a *later* sentence in the same bullet but contradicts the label bound to that bullet's own *first* sentence | Multi-sentence bullets in Risks/Gaps or Key Findings sometimes open with `[fact] Claim one.` then a second sentence carries its own correct `[inference; source: URL]` tag, but the leading marker matches neither the bullet's overall epistemic status nor its first sentence, so citation-discipline flags an internal contradiction even though every sentence individually has a label. Observed on `agent-memory-consolidation-episodic-semantic` pass 2. | The leading bullet marker must match the label of that bullet's own first sentence, not a later sentence. When a multi-sentence bullet mixes epistemic types, drop the single leading marker and label each sentence independently instead of prefixing the whole bullet with one marker. | | A Key Finding mirrors a `§2 Investigation` sentence that itself already splits a factual clause from an interpretive/derived clause under two separate labels, but the Findings mirror collapses both clauses into one Key Finding sentence under a single `[fact]` label | Review flags the Key Finding's `[fact]` label as inconsistent with the `[inference]` label correctly assigned to the same interpretive clause in `§2 Investigation`, because merging a source-supported clause and the item's own derived generalisation under one stronger label misrepresents the interpretive step as directly source-supported. Observed four times in one pass on `agent-memory-evaluation-framework`. | When summarising a `§2 Investigation` sentence into a Key Finding, preserve the same two-sentence, two-label split the source sentence already uses; do not collapse a factual clause and an interpretive clause into one Key Finding sentence under a single label matching only the stronger one. | | `§2 Investigation` paragraph-opening evaluative/comparative sentences left unlabeled | Sentences that open a `§2 Investigation` paragraph with a comparative or evaluative judgment ("X is more mature than Y", "Z remains fragmented", "the evidence shows a real but immature story") were left unlabeled even when every subsequent sentence in the same paragraph carried a correct label, because the self-review checklist explicitly enumerated Analysis, Executive Summary, and §5 lenses but did not name §2 paragraph openers as a distinct check. Six such instances were found in one pass on `hybrid-memory-integration-ontology-llm-weights`, alongside a fabricated proper noun ("ConflictCOLM") invented in place of an actual paper title. | Before triggering review, scan the first sentence of every paragraph in `§2 Investigation` for evaluative or comparative language and confirm it carries its own trailing `[inference; source: URL]` label, using the same scan already applied to §5 and Analysis. Separately, verify every named benchmark, system, or method mentioned in §2 against the actual title or abstract of its cited source before using the name, to catch plausible-sounding but fabricated proper nouns. | | Executive Summary and Assumptions paragraph-opening sentences left unlabeled when only the second sentence of the same paragraph carries a trailing label | A trailing `[inference; source: URL]` or `[assumption; source: URL]` label on a paragraph's *second* sentence does not retroactively cover the *first* (opening) sentence, even when both sentences make claims in the same block. This produced five separate citation-discipline/speculation-control failures in one review pass on `privacy-preserving-agent-long-term-memory`: the `§6 Synthesis` Executive Summary opening sentence, its Findings mirror, and all three Assumptions paragraph openers. Distinct from the already-documented "closing sentence" and "§3 causal chain" patterns — this is specifically about the *first* sentence of a multi-sentence block relying on a label meant for a later sentence. A companion issue in the same item: an arXiv citation's authorship ("Carlini et al.") was inherited from context without independent verification and was fabricated/wrong (actual authors: Morris et al. 2023) — only caught by re-fetching the source abstract during final review. | Before triggering any review, scan the *first* sentence of every paragraph/bullet in Executive Summary and Assumptions (in addition to the existing Analysis/§5/§3/§2 opener checks) and confirm it independently carries its own trailing label — do not assume a label on sentence 2 extends backward to sentence 1. Separately, always independently fetch and verify the author list of any arXiv/paper citation before finalizing, even when the citation or its wording is inherited from a prior completed item's Sources section. | | Self-referential reach/frequency superlatives ("widely cited", "widely referenced", "most frequently cited") sourced only to the artifact making the claim about itself | A phrase describing how well-known, influential, or frequently-cited a practitioner essay or source is, when the only citation given is the essay or source itself, is unverifiable — the source cannot attest to its own reach or citation frequency elsewhere. Review treats this the same as a vague quantifier in Analysis, but it recurs specifically in `§2 Investigation` prose introducing a named practitioner essay or taxonomy (e.g. "Gregor Hohpe, in his widely cited 'Architect Elevator' framing..."), and in the matching Key Finding mirror. A closely related failure: labelling the item's own reflective sourcing-quality assessment ("the most directly-verified claims in this item rest on...") as `[fact]` rather than `[inference]`, since that sentence is the item's own self-assessment, not a claim the cited sources themselves assert. Observed on `what-is-an-enterprise-architect` pass 2. | Before triggering review, grep for superlative/reach phrases ("widely cited", "widely referenced", "most frequently", "best known", "leading") in `§2 Investigation` and Key Findings, and remove the phrase or add an independent source measuring that reach — do not rely on the artifact's own prominence as self-evident. Separately, label any sentence assessing this item's own sourcing quality or evidence-weighting process as `[inference]`, never `[fact]`, since the sourced essays cannot verify a claim about the item's own methodology. | | `high confidence` assigned to a Key Finding backed by a single source, or by multiple sources that all originate from the same organisation | The `peer-reviewer` logical-coherence-and-evidence-sufficiency rule requires at least two *independent* sources for a `high confidence` label; a single vendor engineering post, a single primary-project doc, or a single paper is only `medium` confidence at best, no matter how authoritative that one source is. This failed on 7 of 12 Key Findings in one pass on `ai-coding-agent-runtime-security-evolution` because the self-review checklist checked label/source *presence* but not source *count and independence* per confidence tier. | Before triggering review, for every Key Finding labeled `high confidence`, count the distinct organisations behind its cited sources. If fewer than two independent organisations are represented, downgrade to `medium` (or locate a second independent source) in both the Evidence Map row and the mirrored Findings/§6 Key Finding. Run this check as a discrete step, separate from the label-presence and source-URL checks already in the self-review process. | | Acronym expanded correctly somewhere in the document while an earlier section still uses it bare (out-of-order expansion, not missing expansion) | The acronym-expansion self-review check is usually run as "is this acronym expanded somewhere in the document," which passes even when the *first* occurrence, in an earlier section such as `## Approach` or `§1 Question Decomposition`, is still bare and the expansion only appears later in `§2` or `§6`. Review treats this the same as a fully-missing expansion. Failed for AWS, IDE, VM, and SSH across two passes on `ai-coding-agent-runtime-security-evolution` because the self-review scan checked presence of an expansion, not its line-number position relative to every other use. | Run a line-numbered acronym scan (`grep -n` per acronym) across the *entire* document, sorted by line number, and confirm the expansion's line number is less than or equal to every bare-token line number for that acronym. Do this for every acronym, not just the ones named in the prompt's example table — the violating acronyms are frequently ones outside that table (VM, SSH, IP, HTTP). | | Three or more consecutive Assumptions (or similar bulleted/paragraph) blocks opening with an identical clause | `remove-ai-slop` flags repeated sentence-opening structure across consecutive blocks as a structural-repetition violation, independent of whether each block's content and citations are otherwise correct. Failed on `ai-coding-agent-runtime-security-evolution` pass 2 because all three `### Assumptions` paragraphs (in both the `§6 Synthesis` mirror and Findings) opened with "This item assumes," and the self-review checklist did not name this failure mode explicitly. | Before triggering review, scan every subsection with 3+ bulleted or paragraph blocks (Assumptions, Risks/Gaps, Key Findings) for a shared opening n-gram across three or more consecutive blocks. Vary sentence structure (e.g., front the subject of the assumption instead of repeating "This item assumes") rather than only rewording the claim content. | | A narrative connective paragraph inside a fenced `§4 Consistency Check` (or similar) section is left with zero epistemic labels and zero citations while every other paragraph in the same document has both | Paragraphs that read as pure connective reasoning ("this is resolved by...", "the contradiction is therefore...") are still claim-bearing prose under this repo's rules and need the same per-sentence `[fact]`/`[inference]`/`[assumption]` label and source binding as every other paragraph. Failed on `ai-coding-agent-runtime-security-evolution` pass 2 because the self-review label/source scan was applied to Findings, Analysis, and §2/§5 openers but not explicitly to the §4 narrative paragraph sitting above its own fenced metadata block. | Extend the per-sentence label/source scan to every prose paragraph in `§4 Consistency Check` that precedes the fenced `contradiction_scan:` metadata block, not only to the metadata block itself. Treat prose paragraphs in every numbered section (§0–§6) as in-scope for the label/source scan by default, and only exempt fenced metadata blocks. | | A frontmatter `related:` (or `cites:`) slug is listed but the corresponding completed item is never mentioned, cited, or linked anywhere in the document body | The `peer-reviewer` skill checks not just that cited items exist but that every listed `related`/`cites` slug is substantively discussed somewhere in `§0`, `§2 Investigation`, `§5`, `§6 Synthesis`, or Findings — listing a slug in frontmatter without a matching body mention is treated as an unexplained or fabricated connection. Failed on `llm-consumption-maturity-ladder` pass 2: the item's own `§0` prior-research cross-reference paragraph explicitly named three of four `related` items but omitted the fourth (an AgentCore/agentic-orchestration item), despite the item's own `§2` and `§5` directly discussing the agentic frontier that the omitted item covers. A closely related variant failed on `decision-governance` pass 2: `§0` explicitly asserted that a `cites:` item "bears on" a specific Approach point, but the corresponding investigation sub-questions, Key Findings, and Analysis never actually drew on that item's finding, so the asserted connection was unintegrated rather than merely unmentioned. | Before triggering the first review, cross-check every slug in frontmatter `related:` and `cites:` against a full-document grep for that slug's topic keywords or a mention of its title/URL. If a listed item is not mentioned in body prose, either add a substantive body mention (with a URL-backed citation) or remove the slug from frontmatter. If `§0` asserts that a cited item "bears on" or "is relevant to" a specific Approach point or sub-question, verify that the corresponding investigation section, at least one Key Finding, and the Analysis paragraph on that sub-question actually draw on that item's specific finding, not just mention its title — an assertion of relevance without downstream use of the finding is treated as a fabricated connection even when the item is genuinely cited elsewhere. | | Fixing a self-referential-citation violation (rule 2c) by deleting the fabricated citation strips the sentence's epistemic label along with it | When a sentence is flagged for citing itself (e.g., "source: research question Scope section of this item"), the correct fix is to replace the citation with a real, URL-backed source while keeping the `[fact]`/`[inference]`/`[assumption]` label — but it is easy to delete the entire bracketed clause (label and citation together) while rewriting the sentence, leaving it unlabeled. This is exactly the kind of unlabeled-paragraph violation the review then flags as a separate, new failure. Observed on `decision-governance` pass 2: a self-review fix to a §5 Regulatory/risk lens paragraph correctly removed a fabricated self-citation but left the paragraph with no label at all, which the next review pass caught. | When fixing a self-citation violation, treat "remove the fabricated citation" and "the sentence still needs a label and a real source" as two separate, both-mandatory steps in the same edit — never let a citation-removal edit implicitly delete the label. After the edit, grep the affected paragraph for a `[fact;`/`[inference;`/`[assumption;` pattern to confirm a label survived. | | Post-review remediation edits introduce a fresh, distinct set of violations rather than only fixing the flagged ones | Fixing pass-1 violations (adding new inline citations, expanding a newly-flagged domain term, adding a new cross-referenced item's discussion) writes brand-new prose sentences that themselves need the same acronym-ordering, per-sentence labeling, and banned-adverb checks as the original draft, but the self-review checklist is typically run once before the first draft commit and not re-applied to the specific passages touched by a remediation edit. Observed on `aws-coa-governance-latency-contextual-debt`: pass 2 failed on an entirely new violation set (an unexpanded "AWS", a bare "HITL" abbreviation, five newly-unlabeled interpretive sentences, three banned adverbs) none of which pass 1 had flagged, despite all pass-1 violations having been correctly fixed. | After every post-review remediation edit that adds or rewrites prose (not pure metadata), re-run the acronym-ordering, per-sentence labeling, domain-term-clarity, and banned-adverb checks specifically on the edited passages before pushing the fix commit — do not assume a targeted fix is violation-free just because it resolves the originally flagged issue. | | A historical or founding claim (named person/group, precise year, framework they originated) is labeled `[fact]` or `[inference]` and cited only to a secondary source that discusses the framework, not to the primary publication that established it | Review treats a well-sourced secondary citation (even a prior completed repository item) as insufficient support for a specific historical attribution such as "Alchourron, Gardenfors, and Makinson's 1985 framework" — the primary publication itself must be located and cited alongside or instead of the secondary source. Failed on `flat-vector-rag-context-collision` pass 1: the AGM postulates were correctly labeled and cited to a secondary repository item, but the 1985 Journal of Symbolic Logic paper establishing them was absent from `## Sources` and from the claim's citation. | Before triggering the first review, grep for possessive-attribution patterns (`'''s 19\d\d`, `'''s 20\d\d`, `et al\.'''s`, `originat`, `propose[ds]`) across `## Research Skill Output` and `## Findings`, and for each match confirm a primary-source citation is bound to the claim (search for a freely-accessible copy of the original paper if the item is old enough to predate open-access publishing norms), adding one before drafting if only a secondary source is currently cited. | | A `related:` or `cites:` frontmatter slug is only engaged with via a `## Sources` consultation mark, never in a URL-cited body-prose sentence | This is the same class of failure already documented in this table for `related:`/`cites:` slugs never mentioned in body prose, but recurs specifically when the item was only checked against the Sources list rather than a full-document grep for the slug's topic. Failed twice on `flat-vector-rag-context-collision`: pass 1 flagged two `related:` items never discussed in body prose despite being marked consulted in Sources, and pass 2 flagged a `cites:` item whose topic was discussed multiple times but never actually cited by its URL. | Before triggering any review, for every slug in `cites:` and `related:`, grep the slug's topic keywords across the entire document body (not just Sources) and confirm the slug'''s specific URL appears attached to a substantive claim. A Sources consultation mark or a topic mention without the item'''s own URL does not satisfy this check. | | An acronym expansion is written in reversed order, `(ABBR, Full Name)` instead of the required `Full Name (ABBR)` | The presence-of-expansion self-review check (does this acronym have an expansion somewhere near first use) passes even when the expansion is grammatically reversed, because it only checks for the parenthetical's existence, not its internal order. Review treats a reversed expansion the same as a missing one. Failed on `graphrag-macro-level-hallucination` pass 2: "(CS-RAG, Constraint-based and Sufficiency-guided Retrieval-Augmented Generation)" was flagged even though every other acronym in the same item was correctly ordered. | Run `grep -noE "\([A-Z]{2,}(-[A-Z]{2,})?,\s*[A-Z][a-z]" ` before triggering review and confirm zero matches (or that any match is a benign list, not an expansion attempt). Rewrite any match as `Full Name (ABBR)`. | When you identify a **new** recurring pattern (same friction in two or more sessions), add it to this table as part of the Mini-Retro for that session. ### Knowledge Graphing — Every Write Earns Its Place Every time you create or significantly update a file: 1. Search for 3 related existing files and link them in a `## Related` section. 2. Check for contradictions — supersede, don't delete. 3. Tag accurately in ADRs and docs. ### Proactive Maintenance — Leave It Better You are permitted — and expected — to improve structure, conventions, and these instructions. You are **not** permitted to delete history or introduce new structure without documenting why. ### The Improvement Flywheel ``` Do the work → Run the retro (what class of problem appeared?) → Fix or raise the root cause → Next session starts with a slightly better system ``` **When evaluating the system itself**, follow this protocol — do not self-inspect only: 1. Search for published best practices on the relevant topic (agent instructions, workflow design, etc.) 2. Audit factual claims in the instructions against the actual file contents and workflow behavior 3. Use open GitHub issues and repository state as evidence of what is not working in practice ### What "Done" Means - [ ] The work is complete - [ ] Session log in `progress/` is updated with a Mini-Retro - [ ] Any new decisions are recorded as ADRs - [ ] Any structural improvements spotted are raised in the backlog - [ ] `CHANGELOG.md` updated if behaviour changed - [ ] `remove-ai-slop` run on committed prose --- ## Chain-of-Thought Reasoning Before acting on any research task in this repo, reason explicitly through these steps: 1. **Source credibility first** — Before citing or building on a source, ask: "Who produced this? What incentives do they have? Is this primary research, a secondary summary, or opinion?" Weight primary sources higher. Flag opinion and vendor-produced content clearly. 2. **Triangulation** — Ask: "Is this finding corroborated by at least one independent source?" A single source is a lead, not a conclusion. If only one source supports a claim, note that explicitly rather than presenting it as established fact. 3. **Signal vs noise** — Ask: "Does this piece of information change what we'd recommend or decide? Or is it interesting but inconsequential?" Research that doesn't influence decisions is noise. Prioritise signal. 4. **Knowledge gap identification** — As you research, actively track what is *not* known. Ask: "What is the most important thing this research does not answer?" Unanswered questions are as valuable as answers — record them explicitly. 5. **Recency** — Ask: "When was this produced? Is this field moving fast enough that a 12-month-old source may already be outdated?" Flag recency risk on fast-moving topics. 6. **Synthesis over accumulation** — The goal is not to collect sources; it is to synthesise insight. Ask: "What is the single most important thing this body of research tells us, and why?" Lead with that. 7. **Improvement implication** — Does this session reveal a gap in research methodology, a missing source type, or a question that should become a standing research topic? Raise it in the Mini-Retro. --- ## When the Backlog Is Empty When `BACKLOG.md` has no pending items (all are `done` or `archived`): 1. Review `Research/backlog/` — pick up a research item that hasn't been started. 2. Review open GitHub issues — propose a new Epic based on project state and issue content. 3. If neither applies, surface the question to the owner: describe the current state and ask what to prioritise next. Do not invent work. Do not silently loop. Surface the state and ask.