Template
The HUD is an ephemeral status block (active ticket, done items, current
work, queued items, user action items) shown after significant responses.
Written to .crush/hud.md on disk to save context tokens; never committed.
Redmine/Discourse/git remain the durable systems of record.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
320 lines
19 KiB
Markdown
320 lines
19 KiB
Markdown
# <Project Name> — Agent Guidelines
|
||
|
||
> **Active agent:** <agent/model>. **Permission mode:** <yolo|normal>.
|
||
> **Read [STATUS.md](STATUS.md), [`.crush/memory/operational.md`](.crush/memory/operational.md), and [questions-v1.md](questions-v1.md) first, every session.**
|
||
> **Governing baseline:** [`BASELINE-PROMPT.md`](BASELINE-PROMPT.md) (delete if your project doesn't ship it; the principles still apply).
|
||
|
||
This file is the project-level policy. The git hooks and `scripts/check-rules.sh`
|
||
enforce the mechanical rules below; this document states the policy and intent.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
**You are an AI agent working on this project. Your first actions, in order:**
|
||
|
||
> **SESSION-START GATE (NON-NEGOTIABLE):** Steps 1-4 orient you. Step 5 is the
|
||
> check-for-understanding gate (rule summary). Steps 6-8 gather state. Step 9 is
|
||
> the scope-alignment gate. You MUST NOT begin any task work until the user
|
||
> (a) confirms your rule summary AND (b) names the ticket to work on. This runs
|
||
> **every session, automatically** — the user should never have to ask for it.
|
||
|
||
1. **Set up the environment:** `bash scripts/setup-hooks.sh` (installs git hooks — idempotent).
|
||
2. **Read [STATUS.md](STATUS.md)** — current state, inbox, blockers, tactical notes.
|
||
3. **Read [`.crush/memory/operational.md`](.crush/memory/operational.md)** — access details, key IDs, gotchas.
|
||
4. **Read [questions-v1.md](questions-v1.md)** — open questions awaiting human input.
|
||
5. **Check for understanding — GATE.** In your own words, summarize the rules
|
||
from this file back to the user: Systems of Record, Git Workflow, Questions
|
||
policy, Remote Access (if applicable), Conventions, and any project-specific
|
||
rules. Then stop and wait for the user to confirm. This checkpoint
|
||
guarantees every session starts aligned.
|
||
6. **Check Redmine** — `redmine list --assigned-to-me -p <project-id>` for active work.
|
||
7. **Check current state:** `git log --oneline -10`.
|
||
8. **Run rule audit:** `bash scripts/check-rules.sh --fast`.
|
||
9. **Scope-alignment — GATE.** Present the session handoff's priority list (or
|
||
the Redmine queue if no handoff exists). Do NOT scan the full ticket queue
|
||
and pick work on your own — **the user directs what gets worked on, always.**
|
||
Note any new or urgent items from the Redmine check, then ask which ticket
|
||
to work on tonight. **STOP and wait.** Do not set `.crush/active-ticket`,
|
||
do not read systems, do not run diagnostics, until the user names the
|
||
target. This gate prevents the agent from burning context on work the user
|
||
didn't ask for.
|
||
|
||
## Project Overview
|
||
|
||
<1–3 paragraphs: what this project is, the stack, and the hard constraints.
|
||
Replace this bracketed text.>
|
||
|
||
## Phase
|
||
|
||
> We have exited the "move fast and loose" phase. This is production
|
||
> infrastructure. The bar is the bar. (See `BASELINE-PROMPT.md`.)
|
||
|
||
<Adjust per project: is this in production? serving what? blast radius?>
|
||
|
||
## Repository Layout
|
||
|
||
```
|
||
<root>/
|
||
├── AGENTS.md ← THIS FILE — project policy
|
||
├── STATUS.md ← agent scratchpad (token-efficiency; NOT the system of record)
|
||
├── WORKING.md ← the ONLY in-repo task tracker
|
||
├── questions-v1.md ← git-tracked questions for the human (version up per round)
|
||
├── scripts/
|
||
│ ├── setup-hooks.sh ← install git hooks into .git/hooks/
|
||
│ ├── pre-commit ← fast rule audit (hot-path bypass for status/working)
|
||
│ ├── pre-push ← full audit + clean-tree gate
|
||
│ ├── check-rules.sh ← the rule audit engine (pass/warn/fail accumulator)
|
||
│ ├── test.sh ← project test runner (override per project)
|
||
│ ├── docker-run.sh ← canonical ephemeral-container wrapper
|
||
│ ├── up.sh / down.sh ← docker compose lifecycle wrappers
|
||
│ ├── garden.sh ← doc-sprawl / Discourse-migration report
|
||
│ └── lib/common.sh ← shared bash library (colors, log_*, docker_run, check)
|
||
├── Makefile ← convenience dispatch (setup/validate/fast/lint/test/garden/up/down/status)
|
||
├── docker-compose.yml.example ← lifecycle template (copy to docker-compose.yml)
|
||
├── .env.example ← copy to .env, fill in secrets
|
||
└── .crush/memory/ ← operational memory read each session (if using Crush)
|
||
```
|
||
|
||
## Systems of Record (do not duplicate)
|
||
|
||
- **Redmine** is the single system of record for ALL project work — tickets, schedules, Gantt, dependencies. Use the `redmine-cli`. **Do not use Gitea issues.** Reference tickets as `[#NNN]`.
|
||
- **NEVER close a Redmine ticket without explicit user permission.** You may SUGGEST a close when the result is clearly scoped and delivered. If it's ambiguous whether the work is truly complete, don't suggest a close — leave that decision to the user. This applies to ALL tickets, no exceptions.
|
||
- **Ticket-first enforcement (mechanically enforced).** Before starting any work, set the active ticket: `echo '#NNN' > .crush/active-ticket`. The Crush hook (`hooks/ticket-gate.sh`) blocks modifying operations until this file exists. If no ticket exists, CREATE ONE FIRST via redmine-cli, then set it. Clear when done: `> .crush/active-ticket`.
|
||
- **Discourse** is the single system of record for documentation. Use the `discourse-cli`. **Do not author long-form docs in gitea.**
|
||
- **Git-tracked `.md` files are stubs** that point to the relevant Discourse topic URL. Operational files that must live next to code (`AGENTS.md`, `STATUS.md`, `questions-v*.md`) are the documented exceptions.
|
||
- `STATUS.md` is a scratchpad for token efficiency, **not** a system of record.
|
||
|
||
### CLI invocation (no bin/ shortcuts — invoke the real containers)
|
||
|
||
```
|
||
# redmine-cli
|
||
docker run --rm --env-file ~/.creds/redmine.env \
|
||
git.knownelement.com/reachableceo/redmine-cli:latest <command>
|
||
# subcommands: whoami, projects, statuses, list, show <id>, create, update <id>, close <id>
|
||
|
||
# discourse-cli
|
||
docker run --rm --env-file ~/.creds/discourse.env \
|
||
git.knownelement.com/reachableceo/discourse-cli:latest <command>
|
||
# subcommands: whoami, categories, cat-info, topics, show <id>, create, reply <id>, update <post_id>, delete <post_id>, search
|
||
```
|
||
|
||
### Redmine↔Discourse split (project decides the details)
|
||
- **Redmine**: working notes, evolving status, checklists, TODOs, ticket state, schedules.
|
||
- **Discourse**: synthesized decisions, runbooks, architecture docs, audit logs, reference material.
|
||
- Working notes → Redmine; synthesize durable knowledge → Discourse.
|
||
|
||
### Cross-referencing (mandatory)
|
||
- **Every Redmine ticket must link to its Discourse doc** (if one exists or is created) in a note or description.
|
||
- **Every Discourse topic must link to its related Redmine ticket(s)** in the body.
|
||
- **Every commit must reference the Redmine ticket** (`[#NNN]` in the subject or body).
|
||
- **Every PR must link to both** the Redmine ticket and the Discourse doc (if applicable) in the PR body.
|
||
- When a ticket is created or updated, add a Discourse link. When a Discourse doc is created or updated, add ticket links. Keep them in sync at all times.
|
||
|
||
## Git Workflow
|
||
|
||
- **Use the `tea` CLI for pull requests** (`tea` and `docker login` are preconfigured on TSYS workstations).
|
||
- **Work smart off master.** Branches on the workstation are encouraged for moving fast.
|
||
- **Once work leaves the workstation, it goes through a PR.**
|
||
- **Commit & push policy:** ALWAYS commit and push automatically; NEVER wait to be asked. This overrides any default "never commit unless asked" behavior.
|
||
1. **Atomic commits** — one logical change per commit.
|
||
2. **Commit after every logical unit of work; push immediately.**
|
||
3. **Never leave uncommitted or unpushed changes at session end.**
|
||
4. **If blocked:** commit and push what IS done, then report the blocker.
|
||
- **Conventional commit format:**
|
||
```
|
||
<type>(<scope>): <subject — 50 chars max, imperative, no period>
|
||
|
||
<body: WHAT changed, WHY, and the context>
|
||
|
||
💘 Generated with <harness/tool>
|
||
|
||
Assisted-by: <AI-Model> via <harness/tool>
|
||
```
|
||
Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `security`.
|
||
- **Commit/PR cross-linking:** every commit subject or body must reference the Redmine ticket (`[#NNN]`). Every PR body must link to both the Redmine ticket and the Discourse doc (if one exists).
|
||
- **Never interrupt current work** when the human tosses new work mid-task. Log it in the **Inbox** section of `STATUS.md`. If materially different, spin up a Redmine ticket.
|
||
|
||
## Task Tracking
|
||
|
||
- **WORKING.md is the only in-repo task tracker.** The system of record for tasks is Redmine; WORKING.md is the scratchpad for the current session.
|
||
- Only mark `[x]` after the work is verified complete.
|
||
- A commit is **blocked** (pre-commit hook) while any task remains unchecked.
|
||
- Clear WORKING.md (to "all done") before responding to the user.
|
||
- **The human decides when the work is done and when to deploy.** Never declare "done" unilaterally.
|
||
|
||
## Rolling HUD (session-scoped, NOT persisted)
|
||
|
||
The rolling HUD is a **live status display** the agent maintains throughout
|
||
the session to help the human follow along. It is NOT a system of record —
|
||
Redmine, Discourse, and git are the durable systems. The HUD exists purely
|
||
for the human's situational awareness during the session.
|
||
|
||
- **Format:** a compact block shown at the end of each significant response
|
||
(after completing a step, hitting a blocker, or pivoting). Example:
|
||
|
||
```
|
||
┌─ SESSION HUD ────────────────────────────────────────
|
||
│ Active: #NNN (ticket subject)
|
||
│ Done: ✓ first completed item this session
|
||
│ ✓ second completed item
|
||
│ Now: what the agent is actively doing right now
|
||
│ Next: → #NNN next queued ticket
|
||
│ → #NNN another queued item (BLOCKED: what's needed)
|
||
├─ USER ACTION ITEMS ──────────────────────────────────
|
||
│ • Action only the user can take (unblocks #NNN)
|
||
│ • Another user action (unblocks #NNN)
|
||
└──────────────────────────────────────────────────────
|
||
```
|
||
|
||
- **Placement:** may be written to `.crush/hud.md` on disk to keep context
|
||
window smaller (re-read and update rather than hold in memory). Never
|
||
committed to git. Wiped at session end.
|
||
- **Sections:**
|
||
- **Active:** current ticket number + one-line description
|
||
- **Done:** ✓ items completed this session (append as work progresses)
|
||
- **Now:** what the agent is actively doing
|
||
- **Next:** the queued items (per handoff priority or user direction)
|
||
- **User action items:** things ONLY the user can do (populate creds,
|
||
physical work, manual deploys) with the tickets they unblock
|
||
- **When to show it:** after each logical unit of work, at blockers, and
|
||
when pivoting between tickets. Not every trivial response — use judgment.
|
||
- **NOT a substitute for Redmine/Discourse/git.** The HUD is ephemeral. When
|
||
work completes, update the durable systems (ticket notes, Discourse wiki,
|
||
commits). The HUD just tracks the live narrative for the human.
|
||
|
||
## Questions
|
||
|
||
- **NEVER use a harness "question"/"ask user" tool** (structured prompts,
|
||
modal forms, tabbed questions). Banned across every project, every
|
||
harness. Not portable, not version-controlled, bypasses the git record.
|
||
Non-negotiable. (See `BASELINE-PROMPT.md` §10.)
|
||
- **All questions go in the git-tracked `questions-v(N).md` file.** The
|
||
human edits the answer inline in the same file. **Version up the filename
|
||
each time answers land** (v1 → v2 → v3...): create `questions-v2.md`
|
||
with resolved Q&A marked, new questions appended. This preserves the
|
||
history of each Q&A round.
|
||
- Capture questions for the human in a git-tracked `questions-v(N).md` file.
|
||
- The human reviews and edits it inline. Version up when a round of answers lands.
|
||
- Questions, answers, and the reasoning behind decisions are often more important than the code. Synthesize resolved Q&A into Discourse (decisions) and Redmine (work items).
|
||
|
||
## Working Style
|
||
|
||
- **Stop over-thinking.** Get to code and output faster. Explore with code; gather ground truth. Do not burn tokens reasoning about things a quick command answers.
|
||
- **Ask questions early** via `questions-v(N).md`. Don't ruminate or self-debate at length.
|
||
- **Prefer Unix utilities (awk, sed, grep, cut, tr, etc.) for file editing and text processing** over built-in edit tools when feasible. Harness edit tools can be unreliable with whitespace/indentation; Unix tools are deterministic and exact. Use them for surgical edits, bulk substitutions, and structured text extraction.
|
||
- **Farm work out to deterministic tooling:** linters, LSPs, formatters, test runners. If an LSP is wired up, use it; otherwise pull a Docker image and lint inside it. Do not parse huge code blocks in context.
|
||
- **Use sub-agents as subcontractors** (see BASELINE-PROMPT.md §12): scoped spec in, distilled deliverable out. Never read 10+ files sequentially; batch into 2-3 agent calls. Read the 3-4 files you'll edit yourself; delegate the rest.
|
||
- **Command timeouts (NON-NEGOTIABLE):** Every command that touches a remote
|
||
system MUST be wrapped with `timeout`. Hard limits: 30s for quick reads
|
||
(status, ps, ls), 120s for standard operations, 300s for deployments/pulls.
|
||
If a command hits the timeout, STOP and investigate root cause — never
|
||
blindly retry. A hung command is a failed command. Detect failure fast,
|
||
diagnose, fix, move on. This applies to ALL tools — bash, docker, CLIs,
|
||
sub-agents.
|
||
|
||
## CI/CD
|
||
|
||
- The **local workstation must be able to run the same CI/CD** the hosted infrastructure runs. Maintain them in lockstep.
|
||
- **Shift left:** catch issues at `bash scripts/check-rules.sh --fast` (pre-commit) before push, before PR, before merge.
|
||
|
||
## Conventions
|
||
|
||
- **Host hygiene is inviolable.** The host runs only: `git`, `docker`, and standard Unix utilities (`awk`, `sed`, `grep`, `cut`, `tr`, `jq`, `find`, `xargs`, etc.). No language runtimes, no package-managed tools beyond the base OS.
|
||
- **If a one-off utility would help on the host,** `docker pull` a pinned image and run it ephemerally. Do not install it on the host.
|
||
- **If a tool would be broadly useful across projects,** create a Redmine ticket requesting the human add it via the system package manager. Do not install it yourself.
|
||
- **Docker/Kubernetes for everything** — cluster of 1 or 100 is the same. Don't presume scale.
|
||
- **All development work happens in containers** — custom, off-the-shelf, or a mix. `docker pull` freely without asking. Use `scripts/docker-run.sh <pinned-image>` for one-offs.
|
||
- **Container naming: never use Docker's default.** Always name with a project prefix (e.g. `<project>-<service>`). Enforced by `check-rules.sh`.
|
||
- **Pin every Docker image** — no `:latest` tags (enforced).
|
||
- **Use Docker Compose with lifecycle scripts** (`scripts/up.sh`, `scripts/down.sh`) to bring services up/down.
|
||
- **Shell scripts** use `#!/usr/bin/env bash` + `set -euo pipefail` and must pass `shellcheck` with zero warnings, including info-level.
|
||
- **Shared helpers** live in `scripts/lib/common.sh`; source it rather than re-pasting boilerplate.
|
||
- **Names:** lowercase, hyphen-separated.
|
||
- **Secrets:** NEVER commit secrets. Credentials come from env vars / `.env` (gitignored). Use placeholders in `.env.example`.
|
||
- **IAC testing:** test against the corresponding `sectestbed-` VM (snapshot to base state). `preprod-` VMs for vendor-upgrade testing. Do not require AWX as a prerequisite.
|
||
|
||
## Remote Access (projects with remote hosts)
|
||
|
||
**If this project manages remote hosts** (servers, VMs, network devices),
|
||
these rules are NON-NEGOTIABLE:
|
||
|
||
- **Ship a `remote.sh` chokepoint.** ALL SSH/SCP to any host MUST route
|
||
through a single chokepoint script (`tests/remote.sh` or equivalent).
|
||
NEVER call `ssh`/`scp` directly — the harness blocks raw ssh and the
|
||
command scanner rejects it. One script = one place to configure
|
||
hosts/users/keys, one place to audit.
|
||
- **DNS names ONLY.** NEVER use IP address literals in any command, script,
|
||
or config. ALWAYS use DNS names. If a DNS name does not resolve, fix DNS
|
||
first — do not fall back to IP literals. This eliminates the per-session
|
||
discovery tax of finding the right IP for each host.
|
||
- **If using qemu-guest-agent:** visibility only (`qm guest cmd <id> ping`,
|
||
`network-get-interfaces`). NEVER use `qm guest exec` or any wrapper as an
|
||
execution, access, or key-delivery channel. SSH is the only approved
|
||
remote access path.
|
||
|
||
## Key Commands
|
||
|
||
At a terminal (Mode 1), use `make` or call scripts directly:
|
||
|
||
```bash
|
||
bash scripts/setup-hooks.sh # make setup — install git hooks
|
||
bash scripts/check-rules.sh --fast # make fast — fast audit (pre-commit)
|
||
bash scripts/check-rules.sh # make validate — full audit (incl tests)
|
||
bash scripts/test.sh # make test — run test suite
|
||
bash scripts/garden.sh # make garden — doc-sprawl report
|
||
bash scripts/up.sh # make up — bring up stack
|
||
bash scripts/down.sh # make down — bring down stack
|
||
```
|
||
|
||
Via MCP/API (Mode 2): invoke the same scripts through container exec or MCP
|
||
tool calls. The scripts are the real entry points; `make` is shorthand.
|
||
|
||
## Enforcement Model (belt and suspenders)
|
||
|
||
Policy is enforced by **git hooks** (portable, harness-agnostic): `scripts/pre-commit`
|
||
runs a fast rule audit; `scripts/pre-push` runs the full audit + clean-tree gate.
|
||
Install with `bash scripts/setup-hooks.sh`. The checks are in `scripts/check-rules.sh` and cover:
|
||
shellcheck, image pinning, container naming, required files, doc freshness,
|
||
Discourse pointer-headers, WORKING.md completion, CNW markers, hygiene, and the test suite.
|
||
|
||
Bypass with `--no-verify` in genuine emergencies only.
|
||
|
||
## TDD & Linting
|
||
|
||
- **Code, docs, and tests must be kept in sync at all times.** When you change code, update the corresponding docs (Discourse) and tests in the same commit. Never leave them out of sync.
|
||
- **Red/green TDD for all code.** Write the failing test first.
|
||
- **Linters on all code, as early as possible.** Let deterministic tools find the issues.
|
||
|
||
## DO
|
||
|
||
- Read STATUS.md, questions file, operational memory, and Redmine BEFORE starting work.
|
||
- Write a failing test first (TDD).
|
||
- Read files before editing. Use exact text matching.
|
||
- Run `bash scripts/check-rules.sh` before committing.
|
||
- Use sub-agents to parallelize scoped work.
|
||
- Log interruptions to the STATUS.md Inbox.
|
||
- Keep code, docs, and tests in sync within the same commit.
|
||
- Cross-reference Redmine ↔ Discourse on every ticket/doc creation or update.
|
||
- Include `[#NNN]` in every commit; link Redmine + Discourse in every PR.
|
||
|
||
## DO NOT
|
||
|
||
- Wait to be asked before committing/pushing.
|
||
- Batch unrelated changes into one commit.
|
||
- Edit files you haven't read.
|
||
- Install language toolchains on the host.
|
||
- Use `:latest` image tags or Docker's default container naming.
|
||
- Author long-form docs in gitea — use Discourse.
|
||
- Call `ssh`/`scp` directly — always route through the project's `remote.sh` chokepoint.
|
||
- Use IP address literals for any host — always use DNS names.
|
||
- Use `qm guest exec` or any guest-agent execution channel — SSH only.
|
||
- Commit secrets.
|
||
- Run destructive git operations without explicit instruction.
|
||
- Declare the work "done" — the human decides that.
|
||
- Pivot to new mid-task requests — log them in the Inbox.
|
||
|
||
---
|
||
|
||
**Last Updated:** TEMPLATE-REPLACE-ME
|