From d3ac522faf45037c339b47b16ab26e8ebeb0debf Mon Sep 17 00:00:00 2001 From: Steven Gates Date: Thu, 10 Sep 2026 12:32:23 -0500 Subject: [PATCH] docs(agents): add shared engineering guidance --- .github/workflows/doc-layout.yml | 10 +-- AGENTS.md | 2 + CLAUDE.md | 2 + Engineering-Prompt.md | 125 +++++++++++++++++++++++++++++++ docs/DEFINITION_OF_DONE.md | 10 +-- docs/OPENLOOP_INSTRUCTIONS.md | 2 +- docs/README.md | 8 +- docs/STATIC_ANALYSIS.md | 2 +- 8 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 Engineering-Prompt.md diff --git a/.github/workflows/doc-layout.yml b/.github/workflows/doc-layout.yml index a3730e6b..9e16938d 100644 --- a/.github/workflows/doc-layout.yml +++ b/.github/workflows/doc-layout.yml @@ -1,6 +1,6 @@ name: Doc layout -# Hard gate: new Markdown files must live under docs/ (except root README.md, CLAUDE.md, AGENTS.md). +# Hard gate: new Markdown files must use the locations listed in docs/README.md. # Design: docs/README.md § Enforcement on: @@ -45,10 +45,8 @@ jobs: # Root tool-discovery entry points, not documentation. Every LLM harness looks for its # own filename at the repo ROOT and nowhere else — Claude Code reads CLAUDE.md, Codex / # Cursor / Copilot read AGENTS.md — so these cannot live under docs/ and still function. - # Both are two-line pointers at docs/OPERATING_INSTRUCTIONS.md and - # docs/OPENLOOP_INSTRUCTIONS.md, which hold the one shared copy of the instructions - # (owner instruction, 2026-08-30: one source of truth across multiple LLMs). - README.md|CLAUDE.md|AGENTS.md) + # The pointers also load the owner's shared Engineering-Prompt.md from the root. + README.md|CLAUDE.md|AGENTS.md|Engineering-Prompt.md) ;; docs/*) ;; @@ -67,7 +65,7 @@ jobs: done if [ -n "$VIOLATIONS" ]; then - echo "::error::New Markdown must live under docs/ (except root README.md, CLAUDE.md, AGENTS.md)." + echo "::error::New Markdown must use the allowed locations in docs/README.md." printf '%b' "$VIOLATIONS" exit 1 fi diff --git a/AGENTS.md b/AGENTS.md index 74e035e7..1a83f27e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,5 @@ +Read and follow [Engineering-Prompt.md](Engineering-Prompt.md) before starting work in this project. + First Read: `docs/OPERATING_INSTRUCTIONS.md` — how to work here. Then Read: `docs/OPENLOOP_INSTRUCTIONS.md` — what OpenLoop is. diff --git a/CLAUDE.md b/CLAUDE.md index 74e035e7..1a83f27e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,5 @@ +Read and follow [Engineering-Prompt.md](Engineering-Prompt.md) before starting work in this project. + First Read: `docs/OPERATING_INSTRUCTIONS.md` — how to work here. Then Read: `docs/OPENLOOP_INSTRUCTIONS.md` — what OpenLoop is. diff --git a/Engineering-Prompt.md b/Engineering-Prompt.md new file mode 100644 index 00000000..17782a25 --- /dev/null +++ b/Engineering-Prompt.md @@ -0,0 +1,125 @@ +Before implementing anything, follow a **reuse-first / ecosystem-first engineering approach**. + +Your job is not merely to make the feature work. Your job is to solve it using the **smallest, most standard, most maintainable solution that already exists** whenever possible. + +## Mandatory reconnaissance before writing code + +Before designing or implementing a custom solution: + +1. **Inspect the existing codebase** + - Understand the current architecture and conventions. + - Identify existing abstractions, utilities, services, packages, SDKs, helpers, patterns, and extension points that are relevant. + - Prefer extending existing code over creating parallel implementations. + - Check dependency manifests and determine what libraries/SDKs are already installed and what versions are pinned. + +2. **Investigate existing SDK/library capabilities** + - Check whether an SDK or package we already use supports the required functionality. + - Do not assume the currently pinned version represents the latest capabilities. + - Check newer versions, release notes, migration guides, API references, generated docs, and examples. + - If a newer SDK version solves the problem cleanly, evaluate upgrading it before implementing the capability ourselves. + - A major-version bump is not automatically disqualifying. Evaluate the actual migration cost and breaking changes. + +3. **Search the broader ecosystem** + - Search GitHub for existing implementations, issues, discussions, examples, reference projects, and upstream solutions. + - Check official repositories before random third-party examples. + - Look for maintained packages, SDKs, framework integrations, plugins, extensions, adapters, and tools that already solve the problem. + - Check package registries relevant to the stack: npm, NuGet, PyPI, Go modules, Maven, Cargo, etc. + - Check available ChatGPT/Codex plugins, skills, tools, MCP servers, or connected services when they can provide the capability directly. + +4. **Check the platform/framework standard library** + - Before inventing infrastructure, verify whether the language or framework already provides it. + - Prefer standard facilities for HTTP, testing, mocking, serialization, retries, caching, dependency injection, logging, authentication, configuration, concurrency, etc. + - Example: do not create a custom HTTP-client abstraction merely to make tests possible if the language's normal HTTP testing utilities already support the requirement. + +## Preferred solution hierarchy + +Use this order of preference: + +Existing codebase capability +→ Existing dependency capability +→ Upgrade an existing dependency +→ Official SDK/library/package +→ Standard library/framework feature +→ Well-maintained community package +→ Small adapter around one of the above +→ Custom implementation + +A custom implementation should be the **last resort**, not the starting point. + +## Before choosing custom code + +If you believe a custom implementation is necessary, explicitly explain: + +- What existing code you inspected. +- Which SDKs/packages you evaluated. +- Whether newer versions contain the capability. +- Which official docs/repositories/GitHub issues you checked. +- Why existing solutions are insufficient. +- Why extending or upgrading an existing dependency is worse. +- What maintenance burden the custom implementation introduces. + +If you cannot provide a convincing answer to those questions, **continue researching instead of writing the custom implementation**. + +## Avoid accidental infrastructure + +Be especially suspicious if your solution starts requiring custom versions of foundational infrastructure such as: + +- HTTP clients +- retry frameworks +- authentication clients +- serialization layers +- logging frameworks +- test/mocking frameworks +- dependency injection systems +- caching systems +- database clients +- API clients that duplicate an official SDK + +These are strong signals that you may be solving the problem at the wrong abstraction level. + +## Optimize for engineering taste, not code volume + +Do not reward yourself for writing more code. + +Prefer solutions that: + +- reduce code ownership +- reduce maintenance burden +- follow existing project conventions +- use battle-tested libraries +- minimize new abstractions +- minimize surface area +- remain easy for another senior engineer to understand +- make future upgrades easier rather than harder + +Do not create an abstraction merely because it makes the implementation look architecturally complete. + +## Research before implementation + +For non-trivial tasks, do not immediately start modifying files. + +First produce a short **reconnaissance summary** containing: + +**Existing:** What relevant capabilities already exist in the repository. + +**Ecosystem:** Relevant SDKs, packages, standard-library features, plugins, skills, or upstream implementations you found. + +**Upgrade path:** Whether updating an existing dependency would solve the problem. + +**Recommendation:** The simplest approach and why. + +**Rejected alternatives:** Any tempting custom approaches and why they are unnecessary. + +**Do not begin implementation until the reconnaissance phase is complete.** + +## Final sanity check + +Immediately before implementing, ask: + +> "Am I writing code that someone else has already written, maintained, tested, documented, and packaged for this exact ecosystem?" + +If the answer might be yes, investigate that option first. + +The goal is: + +**Use what exists. Extend before replacing. Upgrade before reimplementing. Wrap before rebuilding. Own as little infrastructure as possible.** diff --git a/docs/DEFINITION_OF_DONE.md b/docs/DEFINITION_OF_DONE.md index e5679f4a..973aa442 100644 --- a/docs/DEFINITION_OF_DONE.md +++ b/docs/DEFINITION_OF_DONE.md @@ -90,15 +90,15 @@ Both have failed repeatedly in CI (PR #161 hit both at once), which is why they ### M1. New Markdown lives under `docs/` **A brand-new `.md` file goes under `docs/`.** The only exceptions are the allowlist in -[`docs/README.md` § Enforcement](README.md#enforcement) — root `README.md` / `CLAUDE.md` / `AGENTS.md` +[`docs/README.md` § Enforcement](README.md#enforcement) — root `README.md` / `CLAUDE.md` / `AGENTS.md` / `Engineering-Prompt.md` and the agent-harness paths (`.claude/`, `.cursor/`, `.codex/`, `swarm/`). That list is the single source of truth; it is enforced by [`.github/workflows/doc-layout.yml`](../.github/workflows/doc-layout.yml) against `git diff --diff-filter=A`. - **Do not widen the allowlist to make your file fit.** Move the file into `docs/`. Widening the gate is an owner decision, requested explicitly and justified in the PR — never a workaround for a red check. - (The one widening so far: root `AGENTS.md`, because each LLM harness auto-discovers only *its* filename - at the repo root. See M3.) + The owner requested root `AGENTS.md` for harness discovery and root `Engineering-Prompt.md` for + shared engineering guidance across projects. See M3. - Editing an existing root `.md` is fine (the gate only sees *added* files) — but a doc that would be new today belongs in `docs/` today. - Check yourself before committing: @@ -126,12 +126,12 @@ one newline), **MD022** (blank line above *and below* every heading), **MD032** The owner drives this repo with several LLMs, so the instructions exist **once** (owner instruction, 2026-08-30): [`docs/OPERATING_INSTRUCTIONS.md`](OPERATING_INSTRUCTIONS.md) (how to work here) and [`docs/OPENLOOP_INSTRUCTIONS.md`](OPENLOOP_INSTRUCTIONS.md) (what OpenLoop is). Root `CLAUDE.md` and -`AGENTS.md` are byte-identical two-line pointers at those files and carry **no content of their own** — +`AGENTS.md` are byte-identical pointers to the root `Engineering-Prompt.md` and those files, and carry **no content of their own** — they exist only because each harness auto-discovers its own filename at the repo root. - **Never fork a per-tool copy** of an instruction file, and never paste content back into a root pointer. Edit the `docs/` file; every LLM picks the change up. -- Adding a harness that reads a different root filename? Add another two-line pointer *and* the +- Adding a harness that reads a different root filename? Add another pointer *and* the allowlist entry in `doc-layout.yml` + `docs/README.md` § Enforcement, in the same PR. ### M4. Moved or renamed something? Grep the whole repo for stale references diff --git a/docs/OPENLOOP_INSTRUCTIONS.md b/docs/OPENLOOP_INSTRUCTIONS.md index ae05d889..756e91fc 100644 --- a/docs/OPENLOOP_INSTRUCTIONS.md +++ b/docs/OPENLOOP_INSTRUCTIONS.md @@ -93,7 +93,7 @@ Show reasoning, not just conclusions. I value breadth and rigor equally — cast When operating in a specific subfolder that has its own agent instructions, respect that folder's voice and approach. This file provides the defaults; subfolder overrides take precedence. -All project documentation (`.md` files) belongs in the `docs/` directory — not the project root. The only exceptions are `README.md` and the two harness pointers `CLAUDE.md` / `AGENTS.md`, which live at the root by convention. **Folder map and placement rules:** [`docs/README.md`](README.md) (Markdown layout, image assets, gitignored `docs/local/` for private notes). +Project documentation (`.md` files) belongs in `docs/`. Root exceptions are `README.md`, the two harness pointers `CLAUDE.md` / `AGENTS.md`, and the owner's shared `Engineering-Prompt.md`. **Folder map and placement rules:** [`docs/README.md`](README.md) (Markdown layout, image assets, gitignored `docs/local/` for private notes). ## Architecture Snapshot diff --git a/docs/README.md b/docs/README.md index a15dcdf1..52231f2a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,13 +1,13 @@ # Documentation layout — OpenLoop **Single rule:** all project documentation lives under `docs/`. The only exceptions at the repo -root are `README.md`, `CLAUDE.md`, and `AGENTS.md` (convention). +root are `README.md`, `CLAUDE.md`, `AGENTS.md`, and the owner's shared `Engineering-Prompt.md`. **Agent instructions are shared, not per-tool** (owner instruction, 2026-08-30). The owner works this repo with several LLMs, so the instructions exist **once**, under `docs/`: [`OPERATING_INSTRUCTIONS.md`](OPERATING_INSTRUCTIONS.md) (how to work here) and [`OPENLOOP_INSTRUCTIONS.md`](OPENLOOP_INSTRUCTIONS.md) (what OpenLoop is). Root `CLAUDE.md` and -`AGENTS.md` are two-line pointers at those files and hold no content of their own — each harness only +`AGENTS.md` first link to [`Engineering-Prompt.md`](../Engineering-Prompt.md), then to those files, and hold no content of their own. Each harness only auto-discovers *its* filename at the repo root, which is why the pointers can't move into `docs/`. **Never fork a per-tool copy**; edit the `docs/` file and every LLM picks the change up. @@ -85,8 +85,8 @@ In-app launcher assets live only under `app/src/main/res/` (see root [`README.md ## Enforcement 1. **Agents:** `CLAUDE.md` mandates reading the core `docs/lessons_learned/` tier and this layout before adding docs. -2. **PR review:** the [`pr-reviewer`](../.claude/skills/pr-reviewer/SKILL.md) skill flags new `.md` outside `docs/` (except root `README.md` / `CLAUDE.md`). -3. **CI — doc layout gate:** [`.github/workflows/doc-layout.yml`](../.github/workflows/doc-layout.yml) fails PRs that **add** new `*.md` outside allowed paths. Allowed today: `docs/`, root `README.md` / `CLAUDE.md` / `AGENTS.md` (tool-discovery entry points — each harness only looks for its own filename at the repo root, so the pointers cannot move; owner instruction, 2026-08-30), `swarm/`, `.claude/`, `.cursor/`, `.codex/` (Cursor and Codex `SKILL.md` packages, same class as `.claude/skills/`). +2. **PR review:** the [`pr-reviewer`](../.claude/skills/pr-reviewer/SKILL.md) skill flags new `.md` outside the allowed locations below. +3. **CI — doc layout gate:** [`.github/workflows/doc-layout.yml`](../.github/workflows/doc-layout.yml) fails PRs that **add** new `*.md` outside allowed paths. Allowed today: `docs/`, root `README.md` / `CLAUDE.md` / `AGENTS.md` (tool-discovery entry points), root `Engineering-Prompt.md` (the owner requires this shared prompt at the same path across projects), `swarm/`, `.claude/`, `.cursor/`, `.codex/` (agent skill packages). 4. **CI / Tier 3 static analysis:** [`STATIC_ANALYSIS.md`](STATIC_ANALYSIS.md) — markdownlint, table alignment, link check, harness skill-tree identity, cspell and JSON validity over the whole tracked tree, hard. Locally the same checks are gates 6–8 of `scripts/pre-pr-sweep.ps1` (tooling lives in `scripts/`, not here — it is not documentation). 5. **Secrets:** `keystore.properties`, `*.jks`, and `docs/local/` are gitignored. diff --git a/docs/STATIC_ANALYSIS.md b/docs/STATIC_ANALYSIS.md index ebb200fb..3a6c9104 100644 --- a/docs/STATIC_ANALYSIS.md +++ b/docs/STATIC_ANALYSIS.md @@ -256,7 +256,7 @@ broken references on `main`** (not introduced by this work): ### Doc layout gate — GitHub Actions (hard) **`.github/workflows/doc-layout.yml`** runs on every pull request. It fails if the PR **adds** any -`*.md` file outside `docs/` (allowed exceptions: root `README.md`, `CLAUDE.md`, `AGENTS.md`). Policy: +`*.md` file outside the allowed locations listed in `docs/README.md`. Policy: [`docs/README.md`](README.md) § Enforcement. ### Hosting Tier 3 — GitHub Actions (hard)