
Standards
- 1.2k installs
- 416 repo stars
- Updated August 5, 2026
- boshu2/agentops
standards is an agentops skill that enforces workflow naming, manifest, and validation conventions across automation pipelines.
About
The agentops standards skill defines conventions for boshu2 agentops pipelines including artifact naming, manifest fields, validation checkpoints, and required metadata across automation stages. Agents use it to audit workflow repos for missing standard files, inconsistent step labels, or outputs that break downstream converter and deployment skills. It specifies how tasks should declare inputs, outputs, versioning, and failure reporting so multi-agent runs remain reproducible. Invoke when standardizing an agentops project, onboarding a new workflow stage, or reviewing whether existing automation complies with the shared standards document.
- Defines agentops naming, manifest, and validation conventions.
- Audits pipelines for missing metadata and inconsistent step labels.
- Aligns outputs with converter and deployment sibling skills.
- Requires explicit inputs, outputs, and failure reporting fields.
- Supports onboarding and compliance review for workflow repos.
Standards by the numbers
- 1,240 all-time installs (skills.sh)
- +26 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #247 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
standards capabilities & compatibility
- Capabilities
- pipeline convention audits · manifest metadata requirements · validation checkpoint definitions · downstream compatibility checks · failure reporting standardization
- Use cases
- orchestration
What standards says it does
standards
npx skills add https://github.com/boshu2/agentops --skill standardsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.2k |
|---|---|
| repo stars | ★ 416 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 5, 2026 |
| Repository | boshu2/agentops ↗ |
Does this agentops workflow meet shared naming, manifest, and validation standards for downstream steps?
Enforce agentops workflow standards for naming, manifests, validation, and pipeline consistency.
Who is it for?
Agents standardizing or auditing boshu2 agentops workflow repositories.
Skip if: Skip for unrelated CI YAML outside the agentops toolkit conventions.
When should I use this skill?
User standardizes agentops projects or audits workflow compliance with shared conventions.
What you get
A standards compliance review with missing metadata fixes and normalized pipeline conventions.
- ubiquitous-language register
- cited term definitions
Files
Standards Skill
Language-specific coding standards loaded on-demand by other skills.
Purpose
This is a library skill - it doesn't run standalone but provides standards references that other skills load based on file types being processed.
Standards Available
| Standard | Reference | Loaded By |
|---|---|---|
| Skill Structure | references/skill-structure.md | vibe (skill audits), doc (skill creation) |
| Python | references/python.md | vibe, implement, complexity |
| Go | references/go.md | vibe, implement, complexity |
| Rust | references/rust.md | vibe, implement, complexity |
| TypeScript | references/typescript.md | vibe, implement |
| JavaScript | references/javascript.md | vibe, implement |
| Shell | references/shell.md | vibe, implement |
| YAML | references/yaml.md | vibe |
| JSON | references/json.md | vibe |
| Markdown | references/markdown.md | vibe, doc |
| SQL Safety | references/sql-safety-checklist.md | vibe, pre-mortem (when DB code detected) |
| LLM Trust Boundaries | references/llm-trust-boundary-checklist.md | vibe, pre-mortem (when LLM code detected) |
| Race Conditions | references/race-condition-checklist.md | vibe, pre-mortem (when concurrent code detected) |
| Codex Skills | references/codex-skill.md | vibe (when skills-codex/ or converter files detected) |
| Behavioral Discipline | references/behavioral-discipline.md | implement, review, vibe, pre-mortem |
| Test Pyramid | references/test-pyramid.md | plan, pre-mortem, implement, crank, validation, post-mortem |
| SKILL.md Tier-Caps | references/skill-tier-caps.md | vibe (skill line-cap audits), doc, plan |
| External-Source Attribution | references/external-source-attribution.md | doc (when absorbing external corpora), heal-skill |
How It Works
Skills declare standards as a dependency:
skills:
- standardsThen load the appropriate reference based on file type:
# Pseudo-code for standard loading
if file.endswith('.py'):
load('standards/references/python.md')
elif file.endswith('.go'):
load('standards/references/go.md')
elif file.endswith('.rs'):
load('standards/references/rust.md')
# etc.Domain-Specific Checklists
Specialized checklists for high-risk code patterns. Loaded automatically by /validate and /pre-mortem when matching code patterns are detected:
| Checklist | Trigger Pattern | Risk Area |
|---|---|---|
sql-safety-checklist.md | SQL queries, ORM calls, migration files, database/sql, sqlalchemy, prisma | Injection, migration safety, N+1, transactions |
llm-trust-boundary-checklist.md | anthropic, openai imports, prompt templates, *llm*/*prompt* files | Prompt injection, output validation, cost control |
race-condition-checklist.md | Goroutines, threads, asyncio, sync.Mutex, shared file I/O | Shared state, file races, database races |
codex-skill.md | Files under skills-codex/, convert.sh, skills-codex-overrides/ | Codex API conformance, prohibited primitives, tool mapping |
behavioral-discipline.md | Execution, review, or plan-validation tasks with ambiguity or broad blast radius | Hidden assumptions, overbuilding, drive-by edits, weak verification |
Skills detect triggers via file content patterns and import statements. Each checklist's "When to Apply" section defines exact detection rules.
Deep Standards
For comprehensive audits, skills can load extended standards from vibe/references/*-standards.md which contain full compliance catalogs.
| Standard | Size | Use Case |
|---|---|---|
| Tier 1 (this skill) | ~5KB each | Normal validation |
| Tier 2 (vibe/references) | ~15-20KB each | Deep audits, --deep flag |
| Domain checklists | ~3-5KB each | Triggered by code pattern detection |
Integration
Skills that use standards:
/validate- Loads based on changed file types/implement- Loads for files being modified/review- Loads for change-quality and blast-radius checks/doc- Loads markdown standards/review- Loads for root cause analysis/refactor- Loads for refactoring recommendations
Examples
Vibe Loads Python Standards
User says: /validate (detects changed Python files)
What happens: 1. Vibe skill checks git diff for file types 2. Vibe finds auth.py in changeset 3. Vibe loads standards/references/python.md automatically 4. Vibe validates against Python standards (type hints, docstrings, error handling) 5. Vibe reports findings with standard references
Result: Python code validated against language-specific standards without manual reference loading.
Implement Loads Go Standards
User says: /implement ag-xyz-123 (issue modifies Go files)
What happens: 1. Implement skill reads issue metadata to identify file targets 2. Implement finds server.go in implementation scope 3. Implement loads standards/references/go.md for context 4. Implement writes code following Go standards (error handling, naming, package structure) 5. Implement validates output against loaded standards before committing
Result: Go code generated conforming to standards, reducing post-implementation vibe findings.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| Standards not loaded | File type not detected or standards skill missing | Check file extension matches reference; verify standards in dependencies |
| Wrong standard loaded | File type misidentified (e.g., .sh as .bash) | Manually specify standard; update file type detection logic |
| Deep standards missing | Vibe needs extended catalog, not found | Check vibe/references/*-standards.md exists; use --deep flag |
| Standard conflicts | Multiple languages in same changeset | Load all relevant standards; prioritize by primary language |
Reference Documents
- references/architecture-terms.md
- references/common-standards.md
- references/behavioral-discipline.md
- references/examples-troubleshooting-template.md
- references/cli-wireup-template.md — Reproducible cobra subcommand template (noun + verb, injectable Options, ~10 min/cycle)
- references/go.md
- references/json.md
- references/markdown.md
- references/python.md
- references/rust.md
- references/shell.md
- references/skill-structure.md
- references/standards-index.md
- references/typescript.md
- references/javascript.md
- references/sql-safety-checklist.md
- references/llm-trust-boundary-checklist.md
- references/race-condition-checklist.md
- references/codex-skill.md
- references/test-pyramid.md
- references/yaml.md
- references/skill-tier-caps.md
- references/external-source-attribution.md
Architecture Terms (Ubiquitous-Language Register)
This file is the canonical register for the DDD/Hexagonal vocabulary AgentOps uses. It complements PRACTICE-REGISTRY.md, which owns practice slugs rather than architecture terms. Every skill, hook, schema, and CLI command that names one of these concepts MUST use the term as defined here. New terms are appended; existing entries are revised in place rather than renamed. Citations point to the source work that named the concept (Author Year) or to a canonical AgentOps doctrine surface. Code anchors are relative-path links; some anchors point at files scheduled by later waves of the DDD+Hexagonal v1 plan and may not yet exist on disk — that is intentional forward referencing.
Aggregate / Aggregate Root
Aggregate / Aggregate Root — Evans, Domain-Driven Design, 2003 (Part II, ch. 6). An aggregate is a cluster of domain objects treated as a single unit for the purpose of invariant enforcement and persistence; the aggregate root is the one entity outside collaborators are allowed to reference, and it gate-keeps every mutation so the aggregate's invariants hold at every transaction boundary. In AgentOps v1, ExecutionPacket is the sole aggregate root — phase transitions, attempt history, and verdict assignment all flow through it so that "a packet is always in a legal phase with a consistent attempt graph" remains invariant. Anchor: `../../../cli/internal/domain/packet/aggregate.go` (created in Wave 1 #6).
Bounded Context
Bounded Context — Evans, Domain-Driven Design, 2003 (Part IV, ch. 14). A bounded context is an explicit boundary inside which a single model and its ubiquitous language apply consistently; outside the boundary the same words may mean different things, and translation is required at the seam. In AgentOps each skill directory under skills/<name>/ is treated as one bounded context: terms like "validate", "promote", or "phase" carry skill-local meanings, and cross-skill contracts go through declared inputs/outputs rather than shared mutable state. Anchor: `../../../skills/` (each subdirectory is one context).
Ubiquitous Language
Ubiquitous Language — Evans, Domain-Driven Design, 2003 (Part I, ch. 2). A ubiquitous language is a vocabulary deliberately shared between domain experts and code so that the same words name the same concepts in conversation, documentation, and source — eliminating the translation layer that otherwise rots requirements. AgentOps maintains its ubiquitous language as two coupled registries: the practice-slug registry at `../../../PRACTICE-REGISTRY.md` for cross-era practice names, and this file for DDD/Hexagonal architectural terms. Anchor: `../../../PRACTICE-REGISTRY.md` — the slug registry section.
Port / Adapter
Port / Adapter — Cockburn, "Hexagonal architecture" (alistair.cockburn.us), 2005. In hexagonal architecture a port is a domain-defined interface describing one kind of conversation the application can have with the outside world; an adapter is the concrete implementation of that conversation against a specific technology. Ports come in two flavors: primary (driving) ports are called by outside actors (CLI, HTTP, test harness) to invoke the domain, while secondary (driven) ports are called by the domain to reach external systems (storage, Git, LLM, beads). In AgentOps the PacketRepository, ClaimEmitter, and EvaluatorBus interfaces are secondary ports; the ao CLI commands are primary adapters that drive those ports. Anchor: `../../../cli/internal/ports/` (created in Wave 1 #6).
Anti-Corruption Layer (ACL)
Anti-Corruption Layer (ACL) — Evans, Domain-Driven Design, 2003 (Part IV, ch. 14, "Anticorruption Layer" pattern). An anti-corruption layer is a translating boundary placed between our model and a foreign model (legacy system, third-party API, vendor SDK) so that the foreign model's vocabulary and assumptions cannot leak into and corrupt our domain — concretely, the ACL exposes a clean port-shaped interface to our side and absorbs all the impedance-matching work on the other. AgentOps plans ACLs at every external seam: Git operations (commit, branch, push), LLM provider calls (Anthropic / OpenAI / local), and beads issue-tracker integration each get their own translator so the domain layer never imports git, anthropic, or bd types directly. Anchor: `../../../cli/internal/adapters/` — each subdirectory (git/, llm/, beads/) is one ACL (created in Wave 2).
Context Map
Context Map — Evans, Domain-Driven Design, 2003 (Part IV, ch. 14, "Context Map" pattern). A context map is a diagram (and/or document) that enumerates every bounded context in the system, names them, and labels the relationship between each pair — shared kernel, customer/supplier, conformist, anti-corruption layer, separate ways, open-host service, published language. AgentOps generates its context map programmatically from skill frontmatter (hexagonal_role, consumes, produces, context_rel) so the map stays in sync with code rather than drifting into wishful documentation. Anchor: `../../../docs/contracts/context-map.md` (auto-generated in Wave 2 #4).
Invariant
Invariant — Meyer, Object-Oriented Software Construction, 1988/1997 (Design by Contract: preconditions, postconditions, class invariants). An invariant is a predicate that must hold over an object's observable state at every stable point — after construction, before and after every public method, and across the boundaries of any operation that exposes the object to other code. In AgentOps the ExecutionPacket aggregate carries explicit invariants — phase ∈ a legal set, attempt sequence is monotonically numbered, verdict is set iff phase is terminal, citations reference resolvable artifacts — and each is enforced inside the aggregate rather than scattered through callers. Anchor: `../../../cli/internal/domain/packet/invariants.go` (created in Wave 1 #6).
Tracer Bullet
Tracer Bullet — Hunt & Thomas, The Pragmatic Programmer, 1999 (Topic 11, "Tracer Bullets"). A tracer bullet is a thin end-to-end slice of working functionality built early to validate the architectural path under realistic load before thickening any layer — the slice goes from outermost input to outermost output, all the seams light up, and you adjust aim from feedback rather than from a paper plan. AgentOps's DDD+Hexagonal v1 picked ExecutionPacket as its tracer aggregate: one aggregate, one set of ports, one set of adapters, historically exercised end-to-end through the retired RPI CLI and now through the operating loop plus cli/internal/rpi tests; only after that path is green do other types (claims, verdicts, briefings) get migrated into the hexagonal seam. Anchor: `../../../cli/internal/rpi/execution_packet.go` — the current ExecutionPacket source, which the v1 slice will alias and re-home through the new domain package.
Behavioral Discipline for Agentic Work
Purpose: Reduce common agent failure modes during implementation and review. Use this alongside language standards, not instead of them.
When to Apply
Apply this reference for:
- non-trivial
/implement,/review,/validate, and/pre-mortemwork - ambiguous requests where different interpretations would change the solution
- agent-authored diffs, broad refactors, or any task with blast-radius risk
1. Think Before Coding
Do not silently pick an interpretation and run with it.
- State the task in your own words before editing.
- Name assumptions that materially affect the solution.
- If two reasonable interpretations lead to different code, ask or present options.
- Surface tradeoffs when the requested path looks heavier than necessary.
- Stop when repo reality contradicts the prompt. Resolve the mismatch first.
AgentOps Translation
- Check whether the capability already exists before proposing or building it.
- Prefer runtime truth (
cli/**,hooks/**,scripts/**, generated docs) over memory or explanatory docs. - If a simpler file boundary exists, choose it and explain why.
2. Simplicity First
Choose the smallest change that satisfies the request.
- Reuse existing helpers, commands, hooks, and patterns before adding new ones.
- Do not add configurability, abstractions, or extension points without a present requirement.
- Do not build a framework for a one-off need.
- Do not add defensive branches for impossible or unobserved scenarios just to look thorough.
- If the patch keeps growing, pause and ask whether there is a smaller cut.
Quick Test
Would a senior engineer call this solution obviously larger than the problem? If yes, simplify.
3. Surgical Changes
Keep the blast radius tight.
- Define what files or surfaces are in scope before editing.
- Every changed line should map to the request, acceptance criteria, or cleanup made necessary by your change.
- Do not fold in adjacent refactors, formatting passes, or comment rewrites unless they are required.
- Match local style and structure unless the task explicitly includes a style correction.
- Only remove dead code or imports that your own change made obsolete.
- If you find an unrelated problem, record it separately instead of bundling it into the patch.
AgentOps Translation
- If unrelated follow-up work appears, create a bead instead of smuggling the fix into the current change.
- Avoid touching generated or mirrored artifacts unless the workflow requires it.
4. Goal-Driven Execution
Turn requests into verifiable outcomes.
- Rewrite the task as success criteria before editing.
- Prefer evidence: tests, smoke commands, parity checks, schema validation, or focused diffs.
- For multi-step work, pair each step with a verification check.
- Do not claim completion without the evidence that matches the requested outcome.
- If validation could not be run, say so explicitly and explain the gap.
AgentOps Translation
- "Fix the CLI bug" becomes "add a reproducer, patch it, run the targeted test."
- "Improve this skill" becomes "update the contract, validate skill integrity, then check the shipped runtime copy."
- "Clean up the hook" becomes "preserve the contract, edit only the required files, then run the hook/doc parity gate."
Before/After Examples
Example 1: Hidden Assumptions
Request: "Make search faster"
Before
- picks one meaning of "faster" without clarifying
- adds caching, async work, and new knobs all at once
- verifies only that the code still runs
After
- asks whether "faster" means latency, throughput, or perceived speed
- picks the smallest change that matches the answer
- verifies against the metric that actually mattered
Example 2: Overbuilt Solution
Request: "Add a discount helper"
Before
- introduces strategies, factories, and extension points for a one-off need
- adds flexibility nobody asked for
After
- starts with a small helper that solves the current requirement
- defers abstraction until a real second use case exists
Example 3: Drive-By Editing
Request: "Fix empty emails crashing the validator"
Before
- rewrites adjacent validation logic
- edits comments and formatting unrelated to the bug
- leaves a large diff for a narrow request
After
- adds a reproducer for the empty-email case
- fixes only the email path plus cleanup caused by that fix
- records unrelated cleanup separately instead of bundling it into the patch
Example 4: Weak Proof
Request: "Improve this AgentOps skill"
Before
- edits the skill doc and stops
- forgets the mirrored Codex artifact or validation step
After
- updates the shared contract and the checked-in Codex copy
- regenerates affected artifact metadata when needed
- runs the relevant validation commands before claiming completion
Review Questions
Use these four questions when validating a plan, patch, or PR:
1. What assumptions is this change making, and were they surfaced or silently chosen? 2. Could the same outcome be achieved with a smaller or more local change? 3. Does every changed line trace back to the stated goal? 4. Is the verification checking the behavior that was claimed, or only that the code compiles?
CLI Wireup Template (Go + cobra + Hexagonal Adapter)
The reproducible cycle-shape for exposing a production adapter as an ao subcommand. Empirically derived across /evolve cycles 144-146 (three ao subcommands shipped in ~10 minutes each, ~250-335 LOC including tests + docs). Captured durably here so any future port-to-subcommand slice is mechanical.
Mirror of docs/learnings/2026-05-13-cli-wiring-cycle-shape.md (canonical source). Copied per CI's no-symlinks rule so /standards-consuming agents discover it via the skill-link path.
The 3 Reference Cycles (Empirical Baseline)
| Cycle | Subcommand | Adapter | Time | LOC | Tests |
|---|---|---|---|---|---|
| 144 | ao loop history | productionLoopReader | ~10 min | 335 | 6 |
| 145 | ao ci latest/recent | productionCIStatus | ~8 min | 258 | 4 |
| 146 | ao corpus inject | productionCorpusReader | ~8 min | 252 | 5 |
8-10 minutes wall-clock, ~250-335 LOC. Adapter-side complexity dominates the variation — Loop took longer because it needed JSON slicing logic; CI was simplest because the adapter already had a clean stub-injectable shape.
The Template
// cli/cmd/ao/<noun>.go
var <noun>Cmd = &cobra.Command{
Use: "<noun>",
Short: "BC<n> <surface> operations",
}
var <noun><Verb>Cmd = &cobra.Command{
Use: "<verb> [flags]",
Short: "Short imperative description",
Long: `Long description with Examples block.`,
RunE: run<Noun><Verb>,
}
type <noun><Verb>Options struct {
// flag-derived fields
writer io.Writer
// injectFn lets tests substitute the port without real I/O
injectFn func(ctx context.Context, opts <noun><Verb>Options) ([]ports.X, error)
}
func init() {
<noun>Cmd.GroupID = "core"
rootCmd.AddCommand(<noun>Cmd)
// flag registrations
<noun>Cmd.AddCommand(<noun><Verb>Cmd)
}
func run<Noun><Verb>(cmd *cobra.Command, _ []string) error {
// pull flag values, build options, delegate
return <noun><Verb>Run(cmd.Context(), opts)
}
func <noun><Verb>Run(ctx context.Context, opts <noun><Verb>Options) error {
if opts.writer == nil { opts.writer = os.Stdout }
fn := opts.injectFn
if fn == nil { fn = <noun><Verb>ViaPort }
items, err := fn(ctx, opts)
if err != nil { return fmt.Errorf("<noun> <verb>: %w", err) }
enc := json.NewEncoder(opts.writer)
for _, item := range items {
if err := enc.Encode(item); err != nil {
return fmt.Errorf("<noun> <verb> encode: %w", err)
}
}
return nil
}
func <noun><Verb>ViaPort(ctx context.Context, opts <noun><Verb>Options) ([]ports.X, error) {
adapter := newProduction<X>(/* construction args */)
return adapter.<Method>(ctx, /* args */)
}// cli/cmd/ao/<noun>_test.go
// 4-6 tests covering:
// - stub returns N items → N lines emitted
// - stub returns empty → 0 bytes emitted
// - stub error → wrapped error
// - live root (filesystem fixture) → walks correctly
// - flag combinations (limit, range, etc.) honoredAfter the .go + _test.go files: scripts/generate-cli-reference.sh regenerates cli/docs/COMMANDS.md. Don't forget `generate-registry.sh` too — the cli-command-dual-generator learning documents the failure mode where one is regenerated and the other isn't.
Why This Shape Works
1. Parent noun + verb subcommands. ao loop history reads better than the flat loop-history spelling. The parent groups future subcommands (ao loop write, ao loop tail) under one verb-space. cobra handles this natively.
2. Injectable function field on Options. Production runs use the default port wrapper; tests substitute a stub. This is the same pattern cycle 117's productionCIStatus.runGH proved — refined here from a struct field to an option-bag function. Faster than fake-file-tree harnesses and platform-neutral.
3. Line-delimited JSON output. One record per line means the output composes with jq -c, head, grep, awk. Operators don't need to remember the schema; they pipe and jq '.field'.
4. Error wrapping with the command name. "<noun> <verb>: underlying error" makes debugging easy when the cobra layer surfaces an error to stderr.
5. Validate by live smoke after build. Each cycle ran make build then ./bin/ao <noun> <verb> <args> against real data. Proves end-to-end semantic correctness, not just compilation.
Anti-Patterns (Observed Across The 3 Cycles)
- Name collisions in `cli/cmd/ao`. Cycle 144's first helper was
named loadCycleHistory — collided with an existing function in metrics_health.go. go vet caught it; renamed to loadCycleHistoryViaPort. Always `grep` before naming helpers in `cli/cmd/ao` (the package is ~150 files).
- Shadowing Go builtins. Cycle 117 used
cap := limit; same rule
applies to CLI helpers.
- Dead imports of `internal/ports` in test files. Cycle 144's
first test file had a dead import; go vet caught it.
- Forgetting the registry regen.
scripts/generate-cli-reference.sh
is the obvious regen; scripts/generate-registry.sh is the not-obvious one. Both are required when adding a cobra.Command. See the cli-command-dual-generator learning for the failure mode.
Pre-Flight Checklist
Before writing the .go file:
# 1. Pick noun + verb. Verify no collision in cli/cmd/ao:
grep -rn "Cmd = &cobra.Command" cli/cmd/ao/ | grep -i "<noun>"
# 2. Verify helper names don't collide:
grep -rn "func <helperName>\b" cli/cmd/ao/
# 3. Confirm the port + production adapter exist:
ls cli/internal/ports/<surface>.go cli/internal/adapters/*<X>*.goAfter committing:
# Regenerate BOTH:
scripts/generate-cli-reference.sh
scripts/generate-registry.sh
# Smoke test:
cd cli && make build && ./bin/ao <noun> <verb> <args>Worked Reference Implementations
The 3 reference implementations on main:
cli/cmd/ao/loop.go+loop_test.go—ao loop historycli/cmd/ao/ci.go+ci_test.go—ao ci latest/recentcli/cmd/ao/corpus_inject.go+corpus_inject_test.go—ao corpus inject
Read those 3 pairs before writing a new wireup; they're shorter than this template.
See Also
docs/learnings/2026-05-13-cli-wiring-cycle-shape.md— canonical
source (this file is the skill-side mirror)
docs/learnings/2026-05-13-bc-ports-wire-up-arc.md— the broader
14-port wire-up arc (cycle 122); historical/architecture, not a reusable template
docs/learnings/2026-05-13-bc-ports-narrowness-postmortem.md— the
narrowness debate that preceded the wire-up
references/go.md— Go conventions this template assumes
Codex Skill Standards
Canonical Contract
Source of truth: docs/contracts/codex-skill-api.md
Frontmatter
Codex SKILL.md frontmatter must contain only name and description:
---
name: skill-name
description: 'When this skill triggers and when it does not.'
---Prohibited fields (Claude-internal, ignored by Codex): skill_api_version, context, metadata, allowed-tools, model, user-invocable, output_contract
Tool References
Skills must reference only tools available in Codex sessions:
| Codex Tool | Purpose | Claude Equivalent |
|---|---|---|
read_file | Read file contents | Read |
apply_patch | Apply file edits | Edit |
rg | Search file contents | Grep |
glob_file_search | Find files by pattern | Glob |
cmd | Shell execution | Bash |
git | Git operations | Bash(git ...) |
list_dir | List directory | Bash(ls) |
spawn_agent | Spawn a focused sub-agent | Agent |
send_input | Send follow-up input to a sub-agent | SendMessage |
wait_agent | Wait for one or more sub-agents | Built into Agent tool |
close_agent | Stop a stuck or no-longer-needed sub-agent | TaskStop |
Prohibited Tool References
These Claude Code primitives have no Codex equivalent and must not appear:
TaskCreate,TaskList,TaskUpdate,TaskGet,TaskStopTeamCreate,TeamDelete,SendMessageEnterPlanMode,ExitPlanMode,EnterWorktreeSkill(skill=...)— Codex uses$skill-nameinvocation, not a Skill toolAgent(subagent_type=...)— Codex uses agent roles, not subagent_type
Mapped Forms Also Prohibited
Lowercase-hyphenated forms are equally invalid (task-create, team-create, send-message).
The previously-mapped todo_write and update_plan are not available as general-purpose tools in Codex sessions (empirically verified via codex exec).
Skill Discovery Paths
| Scope | Path |
|---|---|
| Repo | .agents/skills/ |
| User | ~/.agents/skills/ |
| Admin | /etc/codex/skills/ |
Prohibited paths: ~/.claude/skills/, ~/.codex/skills/
Sub-Agent Patterns
Codex orchestration uses:
| Pattern | Tool | Use Case |
|---|---|---|
| Repeated spawn | spawn_agent | Many similar tasks, one agent per unit of work |
| Agent roles | agent_type | Specialized sub-agents (worker, explorer, monitor) |
| Shell orchestration | cmd + bd CLI | Issue tracking, wave management |
NOT: TaskList-based queueing, TeamCreate/SendMessage coordination, or Skill tool chaining.
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
TaskCreate(subject=...) | Claude primitive, doesn't exist | Use bd create via shell or spawn_agent |
TeamCreate(team_name=...) | Claude primitive, doesn't exist | Use agent roles in config |
SendMessage(to=...) | Claude primitive, doesn't exist | Use send_input for brief follow-up messages |
Skill(skill="vibe") | Claude Skill tool, doesn't exist | Use $validate invocation syntax |
context.window: fork | Claude frontmatter, ignored | Remove from Codex SKILL.md |
~/.claude/skills/ | Wrong path | Use .agents/skills/ |
todo_write(...) | Not available in Codex sessions | Use bd CLI or file-based tracking |
Testing Codex Skills
Two-Phase Validation (Recommended)
Use a two-phase approach for comprehensive coverage at minimal cost:
Phase 1 — Static (fast, no API cost):
- Check frontmatter has only
name+description - Grep for Claude-only primitives (TaskCreate, TeamCreate, SendMessage, etc.)
- Check for
~/.claude/paths - Verify reference files are also clean
Phase 2 — Live (thorough, requires Codex API):
# Check if skill loads and is understood
codex exec -s read-only -C "$(pwd)" \
"Read \$skill-name. Verify it loads, check all referenced tools exist. Rate PASS/PARTIAL/FAIL."DAG-First Traversal
When validating multiple interdependent skills, traverse in dependency order (leaves first). This ensures that when a skill references $other-skill, the referenced skill has already been validated. Encode the dependency graph explicitly — computed DAGs from frontmatter parsing are error-prone.
Prompt Constraint Boundaries
When using LLM judges to evaluate skills, always include explicit constraint boundaries:
- "Read-only sandbox and missing network access are NOT reasons to FAIL — those are test environment limits, not skill defects"
- "Rate the skill's design quality, not whether it can execute in this test environment"
Without these boundaries, judges conflate environment limits with skill defects.
Shell Compatibility
Scripts that validate Codex skills must work on both macOS (BSD tools) and Linux (GNU tools):
- Use
[[:space:]]not\sin grep patterns (BSD grep doesn't support\s) - Use
awkinstead of BSD-incompatiblesedcompound expressions - Pre-process multi-line LLM output with
tr -d '\n'before regex extraction
Release Gate Script
Full DAG-based validation: scripts/smoke-test-codex-skills.sh
scripts/smoke-test-codex-skills.sh --static-only # Fast CI check (no API)
scripts/smoke-test-codex-skills.sh --chain 2 # Test one chain
scripts/smoke-test-codex-skills.sh # Full 54-skill live testChecklist
When reviewing Codex skills (skills-codex/*/SKILL.md):
- [ ] Frontmatter has only
name+description - [ ] No Claude primitive names (PascalCase or lowercase-hyphenated)
- [ ] No
~/.claude/paths - [ ] No
Skill(skill=...)tool invocations - [ ] No
Agent(subagent_type=...)tool invocations - [ ] No
context.*ormetadata.*frontmatter - [ ] Reference files (
references/*.md) also free of Claude primitives - [ ] Instructions are actionable for a Codex agent with only Codex tools
Common Standards Catalog - Cross-Language Patterns
Version: 1.0.0 Last Updated: 2026-03-03 Purpose: Universal coding standards shared across all languages. Language-specific files reference this document for philosophical and cross-cutting patterns, keeping language-specific implementation details in their own catalogs.
---
Table of Contents
1. Error Handling Philosophy 2. Testing Best Practices 3. Security Principles 4. Documentation Standards 5. Code Organization Principles 6. Dedup Manifest
---
Error Handling Philosophy
Errors are first-class citizens. Every language has different mechanisms (Result types, exceptions, error returns), but the underlying principles are universal.
Core Rules
| Rule | ALWAYS | NEVER |
|---|---|---|
| Visibility | Log or propagate every error | Suppress errors silently |
| Specificity | Use specific error types/exceptions | Catch-all without re-raising |
| Context | Add context when propagating | Lose the original error chain |
| Recovery | Distinguish recoverable vs fatal | Treat all errors the same |
| Documentation | Document error behavior in public APIs | Assume callers know failure modes |
| Libraries | Log before raising in library boundaries | Swallow errors inside libraries |
Error Chain Preservation
Every language provides a mechanism for preserving error chains. Use it.
| Language | Mechanism | Example |
|---|---|---|
| Go | fmt.Errorf("context: %w", err) | Preserves errors.Is() / errors.As() |
| Python | raise NewError("context") from exc | Preserves __cause__ chain |
| Rust | ? with .context() / #[source] | Preserves Error::source() chain |
| TypeScript | new AppError("context", { cause: err }) | Preserves Error.cause chain |
| Shell | err "context: $cmd failed"; return $exit_code | Preserves exit code semantics |
Intentional Error Ignores
When errors are intentionally ignored (e.g., best-effort cleanup), document the reason:
| Language | Pattern |
|---|---|
| Go | _ = conn.Close() // nolint:errcheck - best effort cleanup |
| Python | except SpecificError: pass # best effort cleanup with comment |
| Rust | let _ = conn.close(); // Intentional ignore: best effort cleanup |
| TypeScript | void promise.catch(() => {}); // fire-and-forget, logged elsewhere |
| Shell | `rm -rf "$TMPDIR" 2>/dev/null \ |
Error Aggregation
When multiple operations can fail independently (parallel execution, multi-step cleanup), use the language's error aggregation mechanism rather than discarding all but the first error.
| Language | Mechanism |
|---|---|
| Go | errors.Join(err1, err2) (1.20+) |
| Python | ExceptionGroup (3.11+) |
| Rust | Custom Vec<Error> or anyhow context chain |
| TypeScript | AggregateError |
Custom Error Hierarchies
Define a base error type per project/crate/package. Subtypes encode categories.
Principles:
- Base type enables catch-all at API boundaries
- Subtypes enable programmatic handling by callers
- Machine-readable codes (where applicable) enable telemetry
- Human-readable messages enable debugging
Severity Classification
| Level | Definition | Action |
|---|---|---|
| Fatal | Process cannot continue | Log, clean up, exit non-zero |
| Recoverable | Operation failed, process continues | Log, retry or degrade gracefully |
| Warning | Non-ideal but not broken | Log at warning level, continue |
| Informational | Expected alternative path | Log at debug level |
Anti-Patterns (Universal)
| Anti-Pattern | Why It's Bad | Instead |
|---|---|---|
Silent suppression (catch {}, except: pass, _ = without comment) | Hides bugs, makes debugging impossible | Log, propagate, or document the ignore |
| String-only errors | Not matchable, no programmatic handling | Use typed/structured errors |
| Catching too broadly | Masks unrelated failures | Catch the most specific type possible |
| Logging AND re-raising the same error | Duplicate log entries at every layer | Log at the boundary, propagate elsewhere |
| Panic/throw in library code for expected failures | Crashes callers unexpectedly | Return error types; reserve panic for invariant violations |
---
Testing Best Practices
Test Organization
| Layer | Scope | Speed | When to Run |
|---|---|---|---|
| Unit | Single function/method | < 100ms | Every commit |
| Integration | Multiple components, real I/O | < 30s | Every PR |
| End-to-end | Full system with real deps | < 5min | Pre-release |
| Property-based | Invariant fuzzing | Varies | CI nightly or on critical paths |
Table-Driven / Parameterized Tests
The table-driven pattern is universal. Define inputs and expected outputs in a data structure, then iterate.
| Language | Mechanism |
|---|---|
| Go | []struct{ name, input, want } + t.Run() |
| Python | @pytest.mark.parametrize("input,expected", [...]) |
| Rust | #[test] with loop or proptest! macro |
| TypeScript | test.each([...]) or describe.each([...]) |
| Shell | BATS @test with parameterized fixtures |
Benefits:
- Easy to add new cases (one line per case)
- Clear test naming
- DRY -- assertion logic written once
Fixtures and Mocking Philosophy
| Principle | ALWAYS | NEVER |
|---|---|---|
| External boundaries | Mock external services, APIs, databases | Let tests hit real external services in unit tests |
| Internal code | Test real internal implementations | Mock internal functions (couples tests to implementation) |
| Test isolation | Each test sets up its own state | Share mutable state between tests |
| Cleanup | Clean up resources (files, containers, connections) | Leave test artifacts behind |
Test Double Types
| Type | Purpose | When to Use |
|---|---|---|
| Stub | Returns canned data | Simple happy/sad path |
| Mock | Verifies interactions were called | Behavior verification |
| Fake | Working lightweight implementation | Integration-like tests without real infra |
| Spy | Records calls for later assertion | Interaction counting/ordering |
Coverage Targets
| Metric | Minimum | Target | Critical Paths |
|---|---|---|---|
| Line coverage | 60% | 80% | 90%+ |
| Branch coverage | 50% | 70% | 85%+ |
Coverage philosophy:
- Coverage is a floor, not a ceiling -- low coverage signals under-testing, high coverage does not guarantee quality
- Prioritize critical paths (error handling, security, data integrity) over boilerplate
- Measure branch coverage, not just line coverage -- untested branches hide bugs
Property-Based Testing
Test invariants that must hold for ALL inputs, not just hand-picked examples.
When to use:
- Serialization roundtrips (encode then decode = original)
- Mathematical properties (commutativity, associativity)
- Parser contracts (valid input always parses, invalid always fails)
- Boundary conditions (output never exceeds input, no negative values)
Doc Tests / Example Tests
Code examples in documentation should be executable tests. Guarantees documentation accuracy.
| Language | Mechanism |
|---|---|
| Go | func Example* in _test.go files |
| Python | Doctest in docstrings, or >>> examples |
| Rust | Code blocks in /// doc comments |
| TypeScript | JSDoc @example blocks (manual verification) |
---
Security Principles
No Hardcoded Secrets
| ALWAYS | NEVER |
|---|---|
| Load secrets from environment variables or secret stores | Hardcode API keys, tokens, passwords in source |
Use .env files locally (gitignored) | Commit .env or credential files |
| Rotate secrets on exposure | Assume secrets are safe in private repos |
| Audit git history for leaked secrets | Rely on .gitignore alone for protection |
Detection: Prescan pattern P2 flags hardcoded secrets in all languages.
Input Validation
Validate at system boundaries (user input, external APIs, file reads). Trust internal code within the same trust boundary.
| Rule | Description |
|---|---|
| Validate early | Check inputs at the entry point, not deep in business logic |
| Fail fast | Reject invalid input immediately with clear error messages |
| Allowlist over denylist | Define what IS valid, not what ISN'T |
| Type-safe parsing | Parse into typed structures, not raw strings |
Injection Prevention
| Attack Vector | Prevention |
|---|---|
| SQL injection | Parameterized queries / prepared statements. NEVER string interpolation. |
| Command injection | Use array-based exec (no shell). Avoid eval(), exec(), system(). |
| Template injection | Use auto-escaping template engines. Escape user input in templates. |
| Path traversal | Resolve to absolute path, verify within allowed directory. Block .. sequences. |
| JSON/YAML injection | Use proper serialization libraries (e.g., jq in shell). NEVER string interpolation for structured formats. |
Cryptographic Best Practices
| ALWAYS | NEVER |
|---|---|
| Use timing-safe comparison for secrets | Use == for secret/token comparison |
| Use established crypto libraries | Roll your own cryptography |
| Use strong hash functions (SHA-256+, bcrypt, argon2) | Use MD5 or SHA-1 for security |
| Enforce TLS 1.2+ (prefer 1.3) | Disable certificate verification in production |
| Generate random values with crypto-grade RNG | Use math/random for security-sensitive values |
Dependency Auditing
| Practice | Frequency |
|---|---|
Run audit command (npm audit, cargo audit, pip-audit, govulncheck) | Every CI build |
| Pin dependency versions with lock files | Always committed for applications |
| Review new dependencies before adding | Before merge |
| Monitor for CVEs in transitive dependencies | Automated via Dependabot/Renovate |
eval/exec/system Avoidance
| Rule | Description |
|---|---|
Avoid eval() in all languages | Executes arbitrary code; use structured dispatch instead |
| Avoid shell execution from application code | Use library APIs instead of shelling out |
| If shell execution is unavoidable | Use array-based exec with no interpolation |
| Shell scripts | Avoid eval for user-provided data; use functions for dispatch |
OWASP Top 10 Mapping
| # | OWASP Category | Prevention Pattern | Detection |
|---|---|---|---|
| A01 | Broken Access Control | Deny by default; enforce server-side auth on every endpoint | Prescan P3: missing auth middleware |
| A02 | Cryptographic Failures | TLS 1.2+, strong hashing (bcrypt/argon2), no plaintext secrets | Prescan P2: hardcoded secrets |
| A03 | Injection | Parameterized queries, array-based exec, template auto-escaping | Prescan P1: string interpolation in queries/commands |
| A04 | Insecure Design | Threat modeling, abuse case testing, rate limiting | Architecture review |
| A05 | Security Misconfiguration | Minimal permissions, disable defaults, harden headers | Config audit |
| A06 | Vulnerable Components | govulncheck, npm audit, pip-audit, cargo audit | CI dependency scan |
| A07 | Auth Failures | MFA, strong passwords, session timeout, credential rotation | Auth integration tests |
| A08 | Data Integrity Failures | Signed updates, verified CI/CD pipeline, SBOM | Supply chain review |
| A09 | Logging Failures | Log auth events, access control failures, input validation | Log coverage audit |
| A10 | SSRF | Allowlist outbound hosts, block internal IPs, validate URLs | Prescan P4: unvalidated URL construction |
HTTP Handler Security Patterns
| Pattern | ALWAYS | NEVER |
|---|---|---|
| Request validation | Validate Content-Type, Content-Length, and body schema before processing | Process requests without type checking |
| Response escaping | Use framework auto-escaping; set explicit Content-Type headers | Return user data in responses without escaping |
| Content-Type | Set Content-Type and X-Content-Type-Options: nosniff on every response | Rely on browser MIME-sniffing |
| CORS | Restrict Access-Control-Allow-Origin to known domains | Use wildcard (*) origin with credentials |
| CSRF | Use anti-CSRF tokens for state-changing operations | Rely solely on cookies for authentication |
| Rate limiting | Apply rate limits to authentication, API, and upload endpoints | Allow unlimited requests to sensitive endpoints |
| Headers | Set Strict-Transport-Security, X-Frame-Options, Content-Security-Policy | Omit security headers from responses |
Path Traversal Prevention
Resolve user-supplied paths to absolute form, then verify the result stays within the allowed directory.
| Language | Pattern |
|---|---|
| Go | cleaned := filepath.Clean(userPath); if !strings.HasPrefix(filepath.Join(baseDir, cleaned), baseDir) { reject } |
| Python | resolved = (base_dir / user_path).resolve(); if not str(resolved).startswith(str(base_dir.resolve())): raise |
| Node | const resolved = path.resolve(baseDir, userPath); if (!resolved.startsWith(baseDir)) throw |
| Shell | `realpath "$user_path" |
Key rules:
- Always resolve BEFORE checking —
../sequences bypass naive prefix checks - Block null bytes (
\0) in file paths — some runtimes truncate at null - Reject absolute paths in user input when relative paths are expected
Logging Security
| Rule | Description |
|---|---|
| Never log passwords | Hash or mask credentials before any log statement |
| Never log tokens | API keys, JWTs, session tokens — redact to first/last 4 chars max |
| Never log PII | Email, SSN, phone numbers — mask or omit in logs |
| Structured logging | Use structured fields (JSON) to prevent log injection via newlines |
| Log levels for security events | Auth failures = WARN, access control violations = ERROR, suspected attacks = CRITICAL |
| Retention | Define log retention policy; purge logs containing sensitive data on schedule |
Rate Limiting Guidance
| Endpoint Type | Recommended Limit | Strategy |
|---|---|---|
| Authentication (login, register) | 5-10 req/min per IP | Token bucket with exponential backoff |
| API (authenticated) | 100-1000 req/min per user | Sliding window counter |
| File upload | 5-10 req/hour per user | Fixed window with size limits |
| Password reset | 3-5 req/hour per email | Fixed window, no enumeration leak |
| Public (unauthenticated) | 30-60 req/min per IP | Sliding window with CAPTCHA fallback |
Implementation notes:
- Apply rate limits at the reverse proxy / API gateway level when possible
- Return
429 Too Many RequestswithRetry-Afterheader - Log rate limit hits for abuse detection
- Consider separate limits for read vs write operations
---
Documentation Standards
What to Document
| Document | Why |
|---|---|
| Public API signatures | Callers need to know parameters, return types, error behavior |
| Non-obvious logic | Future readers (including yourself) need to understand WHY, not WHAT |
| Error behavior | Callers must know what can fail and how |
| Security-sensitive decisions | Reviewers need to verify threat model compliance |
| Configuration options | Users need to know defaults, valid ranges, and effects |
| Architecture decisions | Teams need to understand trade-offs and constraints |
What NOT to Document
| Skip | Why |
|---|---|
Obvious code (i++, return nil) | Comments add noise, not signal |
| Implementation details of private functions | Changes frequently; comments go stale |
| Type information already in signatures | Redundant with the type system |
| "What" the code does (when code is clear) | The code itself is the documentation |
Examples in Documentation
- Include usage examples for public APIs
- Examples should be runnable (doc tests where supported)
- Show the common case first, edge cases second
- Include error handling in examples
Keeping Documentation in Sync
| Practice | Description |
|---|---|
| Doc tests | Executable examples catch staleness automatically |
| Review docs with code changes | PR reviews should include doc updates |
| Delete docs for deleted features | Stale docs are worse than no docs |
| Version documentation | Match docs to release versions |
Cross-Reference Patterns
- Link to related concepts rather than duplicating content
- Use relative paths within a project
- Reference external standards by URL (e.g., RFC numbers, OWASP guides)
---
Code Organization Principles
Module/Package Naming
| Convention | Description |
|---|---|
| Short, descriptive names | config, handlers, models -- not configurationManager |
| Lowercase with language-appropriate separators | snake_case (Python/Rust/Go), kebab-case (npm/crate names), camelCase (TS) |
| No stuttering | config.Config is fine; config.ConfigConfig is not |
| Domain-driven grouping | Group by feature/domain, not by technical layer |
Public vs Private Visibility
| Rule | Description |
|---|---|
| Minimize public API surface | Export only what callers need |
| Default to private | Make things public only when required |
| Use explicit re-exports | Control the public API from a single entry point |
| Hide implementation details | Internal helpers, data structures, and algorithms stay private |
Circular Dependency Avoidance
| Strategy | Description |
|---|---|
| Dependency inversion | Depend on abstractions (interfaces/traits), not implementations |
| Extract shared types | Move shared types to a separate, leaf-level module |
| Event-based decoupling | Use events/callbacks instead of direct cross-module calls |
| Layer discipline | Higher layers depend on lower layers, never the reverse |
File Size Heuristics
| Size | Status | Action |
|---|---|---|
| < 300 lines | Excellent | Maintain |
| 300-500 lines | Acceptable | Monitor |
| 500-800 lines | Warning | Consider splitting |
| 800+ lines | Critical | Split into submodules |
Version-Aware Development
Language-specific standards SHOULD declare the target language/runtime version and organize modern features by version availability. This prevents using features unavailable in the target version and ensures developers adopt modern alternatives when available.
| Language | Version Source | Example Modern Features |
|---|---|---|
| Go | go.mod go directive | slices (1.21+), range n (1.22+), t.Context() (1.24+) |
| Python | pyproject.toml requires-python | match (3.10+), tomllib (3.11+), exception groups (3.11+) |
| Rust | Cargo.toml edition | let-else (2021+), async fn in trait (2024+) |
| TypeScript | tsconfig.json target | satisfies (4.9+), using (5.2+) |
Import Ordering
All languages follow the same conceptual grouping:
1. Standard library imports 2. External/third-party imports 3. Internal/project imports
Separated by blank lines. Alphabetical within each group.
---
Dedup Manifest
This table maps which sections in each language-specific file contain universal philosophical content that can be replaced with a cross-reference to this document, and which must remain because they contain language-specific implementation details.
| Language File | Section | Action | Rationale |
|---|---|---|---|
go.md | Error Handling | keep-as-is | Go-specific %w, errors.Is(), errors.Join(), custom error types |
go.md | Modern Standard Library | keep-as-is | Entirely Go-specific stdlib packages (slices, maps, cmp) and version-gated features |
go.md | Concurrency | keep-as-is | Go-specific sync.OnceFunc, type-safe atomics, context patterns |
go.md | Future Features | keep-as-is | Go-specific version-gated features for upgrade readiness |
python-standards.md | Error Handling | keep-as-is | Python-specific exception hierarchy, from exc chaining, bare except rules |
python-standards.md | Testing | keep-as-is | Pytest-specific fixtures, conftest.py, testcontainers, parametrize |
python-standards.md | Docstrings | keep-as-is | Google style docstrings, Python-specific sections (Args, Returns, Raises) |
rust-standards.md | Error Handling Patterns | keep-as-is | Rust-specific thiserror/anyhow, ? operator, Result aliases |
rust-standards.md | Testing Patterns | keep-as-is | Rust-specific #[cfg(test)], doc tests, proptest!, criterion benchmarks |
rust-standards.md | Unsafe Code | keep-as-is | Entirely Rust-specific (SAFETY comments, FFI, scope minimization) |
typescript-standards.md | Error Handling | keep-as-is | TS-specific Result pattern, type guards, error classes with branded types |
typescript-standards.md | Type System Patterns | keep-as-is | Entirely TS-specific (generics, utility types, conditional types) |
shell-standards.md | Error Handling | keep-as-is | Shell-specific set -eEuo pipefail, ERR trap, exit codes |
shell-standards.md | Security | keep-as-is | Shell-specific sed injection, jq for JSON, CLI secret handling |
shell-standards.md | Testing | keep-as-is | BATS-specific test patterns, shellcheck integration |
| ALL | Compliance Assessment | keep-as-is | Grading scales are language-specific (different tool outputs, thresholds) |
| ALL | Vibe Integration | keep-as-is | Prescan patterns and JIT loading are language-specific |
| ALL | Anti-Patterns | trim-universal-keep-specific | Add cross-ref to common anti-patterns; keep language-specific examples |
| ALL | Code Quality Metrics | trim-universal-keep-specific | Add cross-ref to common coverage targets; keep language-specific tool commands |
Legend:
- keep-as-is -- Section contains primarily language-specific implementation details. No changes needed.
- trim-universal-keep-specific -- Section contains some universal philosophical content that overlaps with this document. Add a cross-reference note at the top of the section pointing here, but keep all language-specific examples and tool commands.
- replace-with-ref -- Section is entirely universal philosophy. Replace with a cross-reference. (None found -- all language sections contain significant implementation details.)
Conservative approach: All language-specific files retain their full content. Only two section categories get a small cross-reference header added. This ensures no loss of language-specific implementation guidance.
---
Related: Language-specific standards in go.md, python.md, rust.md, typescript.md, shell.md
Examples + Troubleshooting Template
Reference template for adding## Examplesand## Troubleshootingsections to skills. Workers MUST follow this format exactly.
Section Placement
## Examplesgoes BEFORE## See Also(or at end of file if no See Also)## Troubleshootinggoes AFTER## Examples, BEFORE## See Also
Append-vs-Create Rules (4 cases)
1. Neither section exists: CREATE both ## Examples and ## Troubleshooting before ## See Also 2. Only `## Examples` exists: APPEND new examples below existing ones; CREATE ## Troubleshooting after Examples 3. Only `## Troubleshooting` exists: CREATE ## Examples before Troubleshooting; APPEND new rows to existing table 4. Both exist: APPEND new examples and new troubleshooting rows to existing sections (don't rewrite)
Examples Format
## Examples
### <Scenario Title>
**User says:** `/<skill> <args>`
**What happens:**
1. Agent does X
2. Agent does Y
3. Output written to Z
**Result:** Brief description of outcomeEach example MUST include:
- A realistic trigger phrase showing how a user invokes the skill
- Step-by-step behavior description (what the agent actually does)
- Expected output or result
Troubleshooting Format
## Troubleshooting
| Problem | Cause | Solution |
|---------|-------|----------|
| Error message or symptom | Why it happens | How to fix |Each entry MUST include:
- A specific, recognizable error message or symptom
- The root cause explanation
- A concrete fix (command, config change, or workaround)
Per-Tier Requirements
| Tier | Skills | Examples | Troubleshooting | Word Budget |
|---|---|---|---|---|
| Tier 1 | council, crank, vibe | 3+ scenarios | 3+ entries | Examples: max 400 words |
| Tier 2 | research, plan, implement, pre-mortem, post-mortem, rpi | 2+ scenarios | 2+ entries (skip if exists) | Examples: max 250 words |
| Tier 3 | swarm, codex-team, evolve, release, quickstart, handoff | 2+ scenarios | 2+ entries (skip if exists) | Examples: max 250 words |
| Tier 4 | bug-hunt, complexity, doc, product, status, trace, inbox, knowledge, retro | 2 scenarios | 2-3 entries | Examples: max 250 words |
| Internal | extract, flywheel, forge, inject, provenance, ratchet, standards, using-agentops | 1-2 scenarios | 2 entries | Examples: max 200 words |
Note: shared is excluded — it's a reference collection, not a skill.
Troubleshooting: max 200 words per skill across all tiers.
Total SKILL.md word count MUST stay under 5000 words. Verify with wc -w SKILL.md.
Quality Bar
- Examples must reflect actual skill behavior (not placeholder text)
- Troubleshooting entries must describe real failure modes users encounter
- Internal skills: show programmatic invocation (how other skills call them)
- User-facing skills: show natural language triggers a human would type
Attribution Patterns for External-Source Absorption
When a skill or reference absorbs patterns from an external corpus (ACFS, gstack, or any third-party knowledge source), attribution is required even when the absorption is pattern-only and contains no verbatim text. Two attribution patterns are supported. Pick one per skill based on source-count.
Pattern A — Skill-level LICENSE.md
Use when the skill is primarily derived from a single external source.
- Place at
skills/<name>/LICENSE.md. - Reference from
SKILL.mdas plain text, never as a relative markdown link: - ✅
See \LICENSE.md\in this skill directory for attribution. - ❌
See [LICENSE.md](LICENSE.md) for attribution. - The relative-link form breaks
mkdocs --strictbecause mkdocs flattens skill directories during build (skills/<name>/SKILL.md→skills/<name>.md), so the sibling-file link[LICENSE.md](LICENSE.md)resolves toskills/LICENSE.mdwhich does not exist. - Example path:
skills/<name>/LICENSE.md(the formersystem-tuningskill used this pattern before its 2026-06 retirement).
Pattern B — Per-reference footer
Use when the skill's references/*.md files draw from two or more external sources.
- Each
references/<topic>.mdfile gets a footer block at the bottom:
---
**Source:** Adapted from <corpus> / <doc-name>. Pattern-only, no verbatim text.- The skill's
SKILL.mddoes not need its own attribution — the per-ref footers cover all absorbed material.
Choice rule
| If | Use |
|---|---|
| All references in the skill share one source | Pattern A (skill-level LICENSE.md) |
| References draw from 2+ sources | Pattern B (per-reference footer) |
| Mix: skill body has one primary source but one reference is from another | Pattern A for the skill + Pattern B footer on the divergent reference |
Constraints (binding for any pattern)
- Pattern-only, no verbatim text. Do not copy >5 consecutive words from the external source.
- Attribute the canonical pattern title when one exists (e.g., "Asuper-style sync" → cite Asupersync corpus).
- Do not use relative markdown links to root-level co-located files in `SKILL.md` —
references/subdir works ([name](references/name.md)), but root-level files (LICENSE.md,notes.md) do not. mkdocs--strictwill fail the build. - Apply a clean-room policy for any external-corpus-derived work. Allowed observations are counts, paths, filenames, metadata, package shape, validation outcomes, CLI behavior, and derived categories. Do not copy external prose, prompts, examples, references, scripts, templates, or role text.
Cross-references
- Pattern A example: any skill carrying a single-source
LICENSE.mdat its root (see the path rule above).
Go Standards (Tier 1)
Target Version
Detect from go.mod. Use all features up to and including that version. Never use features from newer versions. Current project target: Go 1.26.
Required
gofmt(automatic)golangci-lint runpasses- All exported symbols documented
Error Handling
- Always check errors:
if err != nil - Wrap errors with context:
fmt.Errorf("doing X: %w", err) - Never
_ = errwithout// nolint:errcheckcomment - Use
errors.Is(err, target)instead oferr == target-- works with wrapped errors (1.13+) - Use
errors.Join(err1, err2)to aggregate errors from parallel operations or multi-step cleanup (1.20+) - Use
context.WithCancelCause/context.Causeto attach error reasons to cancellations (1.20+)
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
%v for errors | Breaks error chain | Use %w |
panic() in library | Crashes caller | Return error |
| Naked goroutine | No error handling | errgroup or channels |
interface{} | Type safety loss | Use any (1.18+), generics, or specific types |
err == target | Misses wrapped errors | errors.Is(err, target) (1.13+) |
atomic.StoreInt32 | Type-unsafe | atomic.Bool / atomic.Int64 / atomic.Pointer[T] (1.19+) |
for i := 0; i < n; i++ | Verbose | for i := range n (1.22+) |
| Manual loop for contains/sort | Error-prone, verbose | slices.Contains, slices.SortFunc (1.21+) |
sync.Once + closure wrapper | Verbose, easy to misuse | sync.OnceFunc / sync.OnceValue (1.21+) |
Interfaces
- Accept interfaces, return structs
- Keep interfaces small (1-3 methods)
- Define interfaces where used, not implemented
Documentation
- All exported symbols must have godoc comments starting with the symbol name
- Package-level doc in
doc.gofor non-trivial packages - Include runnable
Example_*functions in_test.gofiles - Run
go doc ./...to verify documentation
Concurrency
- Always pass
context.Contextas first param - Use
sync.Mutexfor shared state; use type-safe atomics (atomic.Bool,atomic.Int64,atomic.Pointer[T]) for simple flags/counters (1.19+) - Prefer channels for communication
- Use
sync.OnceFunc(fn)instead ofsync.Once+ wrapper;sync.OnceValue(fn)when returning a value (1.21+) - Use
context.AfterFunc(ctx, cleanup)to register cleanup on cancellation (1.21+) - Loop variables are safe to capture in goroutines since 1.22 (each iteration gets its own copy)
Modern Standard Library
slices package (1.21+)
Prefer slices over hand-written loops:
| Function | Replaces |
|---|---|
slices.Contains(items, x) | Manual search loop |
slices.Index(items, x) | Manual search loop returning index |
slices.IndexFunc(items, fn) | Manual search loop with predicate |
slices.Sort(items) | sort.Slice / sort.Strings |
slices.SortFunc(items, cmp) | sort.Slice with less function |
slices.Max(items) / slices.Min(items) | Manual loop tracking max/min |
slices.Reverse(items) | Manual swap loop |
slices.Compact(items) | Manual dedup of consecutive elements |
slices.Clip(s) | s[:len(s):len(s)] to remove excess capacity |
slices.Clone(s) | append([]T(nil), s...) |
Iterator consumption (1.23+):
| Function | Usage |
|---|---|
slices.Collect(iter) | Build slice from iterator |
slices.Sorted(iter) | Collect and sort in one step |
maps package (1.21+; Keys/Values return iterators as of 1.23)
| Function | Replaces |
|---|---|
maps.Clone(m) | Manual map copy loop |
maps.Copy(dst, src) | Manual map merge loop |
maps.DeleteFunc(m, fn) | Manual delete loop with predicate |
maps.Keys(m) | Manual key collection loop (returns iterator, 1.23+) |
maps.Values(m) | Manual value collection loop (returns iterator, 1.23+) |
cmp package (1.22+)
cmp.Or(a, b, c)-- returns first non-zero value. Replacesif x == "" { x = default }chains:
name := cmp.Or(os.Getenv("NAME"), config.Name, "default")strings / bytes improvements
| Function | Version | Replaces |
|---|---|---|
strings.Cut(s, sep) / bytes.Cut(b, sep) | 1.18+ | Index + slice arithmetic |
strings.CutPrefix(s, prefix) / strings.CutSuffix(s, suffix) | 1.20+ | HasPrefix + TrimPrefix |
strings.Clone(s) / bytes.Clone(b) | 1.20+ | Manual copy (prevents memory leaks from substring references) |
net/http improvements (1.22+)
Enhanced ServeMux with method and path parameters:
mux.HandleFunc("GET /api/users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
// ...
})May eliminate the need for third-party routers for simple APIs.
Other stdlib
| Function | Version | Replaces |
|---|---|---|
fmt.Appendf(buf, fmt, args...) | 1.19+ | []byte(fmt.Sprintf(...)) -- avoids allocation |
time.Since(start) | 1.0+ | time.Now().Sub(start) |
time.Until(deadline) | 1.8+ | deadline.Sub(time.Now()) |
errors.Join(err1, err2) | 1.20+ | Discarding all but the first error (see Error Handling) |
reflect.TypeFor[T]() | 1.22+ | reflect.TypeOf((*T)(nil)).Elem() |
min(a, b) / max(a, b) | 1.21+ | if a > b patterns or custom helpers |
clear(m) / clear(s) | 1.21+ | Manual map deletion loop / manual slice zeroing |
Struct Contract Completeness
When adding fields to a struct, every code path that creates an instance must populate them. Partial population creates an inconsistent contract for consumers.
| Anti-Pattern | Problem | Fix |
|---|---|---|
| New field on struct, some constructors don't set it | Consumers see zero-value for some paths, real value for others | Grep all StructName{ literals; verify each sets the new field |
| Synthesized instances (e.g., end-of-batch summaries) skip fields | Downstream code assumes all instances have the same shape | Store provenance metadata alongside state so synthesized instances can populate fields from last-seen values |
| Index fields after sort | EventIndex points to sorted position, not caller's original position | Wrap items with original index before sorting; emit original index in output |
Checklist for adding struct fields: 1. Grep StructName{ across the package — every literal must set the new field 2. Check factory functions and builder patterns 3. Check synthesized/summary instances created outside the main loop 4. Add a structural assertion test: iterate all output instances, assert new field is non-zero (or document why zero is valid)
Wire Input Validation
When parsing external JSON/YAML into structs with enum-like fields, validate against an allowlist before trusting the value.
// BAD: trust whatever the wire sends
if ev.ErrorClass != "" {
// use it as-is — "bogus" passes through
}
// GOOD: validate against known values
var validClasses = map[ErrorClass]bool{ ... }
if ev.ErrorClass != "" && !validClasses[ev.ErrorClass] {
ev.ErrorClass = classify(ev) // reclassify from content
}Also normalize impossible states: if IsError=false but ErrorClass="timeout", clear it.
Testing
Exact Assertion Rule
Always assert the exact expected value, never just "not the wrong one."
// BAD: passes even if classification drifts to a different wrong class
if got == StreamErrorClassRateLimit {
t.Errorf("should not be rate_limit")
}
// GOOD: pins the exact expected behavior
if got != StreamErrorClassExecutionError {
t.Errorf("got %q, want execution_error", got)
}This applies to all classifier/enum tests. != X assertions silently pass when the result drifts to a third, equally wrong value.
Structural Invariant Tests
For structs with required fields, add a sweep test that asserts ALL output instances populate them:
func TestAllViolationsHaveStructuredFields(t *testing.T) {
// Run through multiple scenarios, collect all violations
for _, v := range allViolations {
if v.TeamName == "" && v.Rule != RuleSomeException {
t.Errorf("violation %+v missing TeamName", v)
}
if v.Timestamp.IsZero() {
t.Errorf("violation %+v missing Timestamp", v)
}
}
}CI-Safe Test Pattern
When testing functions that shell out to external CLIs (bd, ao, gh, etc.), test the low-level function directly instead of the wrapper that invokes the CLI. This ensures tests pass in CI where the CLI may not be installed.
// BAD: calls processDiscoveryPhase() which requires bd CLI
func TestGateDiscoveryVerdictC2Event(t *testing.T) {
processDiscoveryPhase(ctx, root, opts) // fails in CI — bd not available
}
// GOOD: test event shape directly via the underlying function
func TestGateDiscoveryVerdictC2Event(t *testing.T) {
ev, err := appendRPIC2Event(root, rpiC2EventInput{
RunID: runID, Phase: 1, Type: "gate.discovery.verdict",
Message: "Pre-mortem verdict: PASS",
Details: map[string]any{"verdict": "PASS", "report": "report.md"},
})
require.NoError(t, err)
assert.Equal(t, "gate.discovery.verdict", ev.Type)
}Rule: If a function's only untestable part is the external CLI call, extract the testable logic (event emission, state mutation, file I/O) into a separate function and test that.
Table-Driven Tests
Prefer table-driven tests for functions with multiple input/output cases:
func TestClassifyServeArg(t *testing.T) {
tests := []struct {
name string
flagRunID string
args []string
wantGoal string
wantRunID string
}{
{"empty", "", nil, "", ""},
{"flag run-id", "rpi-abc12345", nil, "", "rpi-abc12345"},
{"arg goal", "", []string{"fix the bug"}, "fix the bug", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
goal, runID := classifyServeArg(tt.flagRunID, tt.args)
assert.Equal(t, tt.wantGoal, goal)
assert.Equal(t, tt.wantRunID, runID)
})
}
}Test Conventions
- File naming: Test files MUST be named
<source>_test.go. NEVERcov*_test.go,*_extra_test.go, or other non-standard prefixes. Keep all tests for a source file in one test file. - Function naming:
Test<Uppercase>(e.g.,TestFoo_Bar). Go requires uppercase letter afterTest. - No coverage-padding: Tests that use trivial
!= ""or!= nilassertions solely to inflate coverage are banned. Every test must assert behavioral correctness. - No zero-assertion smoke tests: Every test must have assertions. For print/output functions, use
captureStdoutand assert output contains expected strings. - Assert exact expected values: Use
== expected, never!= wrong. (See Exact Assertion Rule above.) - Table-driven tests preferred for multi-case functions. (See example above.)
- Test low-level functions directly; don't depend on external CLIs (
bd,ao) in tests. (See CI-Safe Test Pattern above.) - Guard-test fixtures must use the real persisted shape. Skip/dedup/consumed/idempotency/regression guard tests must round-trip a real persisted sample (production writer → production reader) or assert against a checked-in real example — never a hand-built in-memory constructor that sets a marker at a granularity the on-disk format never emits (e.g.
consumedat item-level whennext-work.jsonlmarks it at batch-level). A fixture of a shape production can't produce gives a false green (ag-mjlg / PR #652). Full rationale:test-pyramid.md→ "Fixture Fidelity". - Test isolation — restore shared global/process state via `t.Cleanup`.
cli/cmd/aotests share onerootCmd+ package-global cobra flag vars and run inside the repo tree, so a test that mutates shared state without restoring it leaks into whatever test the-shuffle=onorder runs next. This is a recurring flake class: goalsgoalsMeasureScenariosOnlycobra-global (a9dab21c4),core.baregit-env (ek8v), cwd floor (hvb). - Set a package-global cobra flag only through a self-cleaning helper, so every set-site auto-restores and no order can leak it:
func setGoalsMeasureScenariosOnly(t *testing.T, v bool) {
t.Helper()
old := goalsMeasureScenariosOnly
goalsMeasureScenariosOnly = v
t.Cleanup(func() { goalsMeasureScenariosOnly = old })
}- Scope process state:
t.Chdir(t.TempDir()),t.Setenv, andgit -C <tempRepo>withcmd.Dirset. Never run a state-mutatinggitop against the real repo via an unsetcmd.Dir/ leakedGIT_DIR. - Find leakers by analysis (grep set-sites for a missing reset), not by chasing reproducing seeds: order-dependent flakes are population+seed-specific, so "couldn't reproduce" ≠ fixed — close on the root (the missing cleanup).
- The push==CI full race suite runs
-shuffle=onas the late backstop; it is not the primary guard.
Benchmark Tests (BF7)
Use Go's built-in benchmark support for hot-path functions:
func BenchmarkParseConfig(b *testing.B) {
input := generateLargeConfig(1000)
b.ResetTimer()
for b.Loop() { // Go 1.24+; use `for i := 0; i < b.N; i++` for older versions
parseConfig(input)
}
}Run with: go test -bench=. -benchmem ./...
Compare across changes with benchstat:
go test -bench=. -count=10 ./... > old.txt
# ... make changes ...
go test -bench=. -count=10 ./... > new.txt
benchstat old.txt new.txtBackward Compatibility Tests (BF8)
Maintain golden fixtures in testdata/compat/:
func TestBackwardCompat(t *testing.T) {
fixtures, err := filepath.Glob("testdata/compat/*.json")
require.NoError(t, err)
require.NotEmpty(t, fixtures, "compat fixtures must exist")
for _, f := range fixtures {
t.Run(filepath.Base(f), func(t *testing.T) {
data, _ := os.ReadFile(f)
result, err := ParseConfig(data)
require.NoError(t, err, "legacy format must still parse")
assert.NotEmpty(t, result.Name)
})
}
}Regression Tests (BF6)
Name after the bug ID. Reproduce the exact failure:
func TestBug_AG_XYZ_NilMapPanic(t *testing.T) {
// Regression: processGoals panicked on nil options map (ag-xyz)
result, err := processGoals(nil)
require.NoError(t, err)
assert.Empty(t, result)
}Security Tests (BF9)
Test path traversal rejection and secrets redaction:
func TestRejectsPathTraversal(t *testing.T) {
payloads := []string{"../../../etc/passwd", "..\\windows", "foo/../bar"}
for _, p := range payloads {
t.Run(p, func(t *testing.T) {
_, err := LoadConfig(p)
assert.Error(t, err, "must reject path traversal")
})
}
}Complexity Budget
- Warn at cyclomatic complexity 15, fail at 25.
- Run
golangci-lint runto check.
Before Committing Go Changes
cd cli && go build ./... && go vet ./... && go test ./...Or equivalently: cd cli && make build && make test
HTTP Handler Security
Go HTTP handlers in this codebase are localhost-only but should still follow defense-in-depth:
| Pattern | Risk | Fix |
|---|---|---|
innerHTML = userInput in embedded HTML | XSS | Use DOM construction (createElement + textContent) |
r.URL.Query().Get("param") used in file paths | Path traversal | Reject .., /, \ before use |
fmt.Fprintf(w, userInput) in HTML handler | XSS | Use html/template or text/template with escaping |
filepath.Join(root, userInput) | Path traversal | Validate input against allowlist pattern (e.g., regexp) |
Access-Control-Allow-Origin: * | CORS bypass | Acceptable for localhost-only; restrict for public APIs |
Query parameter validation pattern:
param := strings.TrimSpace(r.URL.Query().Get("id"))
if param != "" && (strings.Contains(param, "..") || strings.Contains(param, "/") || strings.Contains(param, "\\")) {
http.Error(w, "invalid parameter", http.StatusBadRequest)
return
}DOM construction instead of innerHTML:
// BAD: innerHTML with user-controlled data
el.innerHTML = '<span>' + userInput + '</span>';
// GOOD: DOM construction
const span = document.createElement('span');
span.textContent = userInput;
el.appendChild(span);Security-Lint Suppressions (gosec + semgrep)
When a security-lint finding is a false positive on intentional crypto (e.g. SHA-1 used for git object IDs, not as a security primitive), the suppression needs TWO independent annotations on the SAME line. gosec and semgrep run as separate scanners and each ignores the other's directives.
| Scanner | What it ignores | What suppresses it |
|---|---|---|
| gosec (standalone) | //nolint:gosec (golangci-lint-only) | // #nosec G<NN> directive, e.g. // #nosec G401 G505 |
| semgrep | qualified nosemgrep: <rule-id> (does NOT suppress) | a bare // nosemgrep |
Combine both into one comment and place it on both the import line and the usage/call site — each is flagged independently:
import (
"crypto/sha1" // #nosec G505 nosemgrep -- git object IDs are SHA-1 by definition; not a security primitive here.
)
func gitBlobID(content []byte) string {
h := sha1.New() // #nosec G401 nosemgrep -- git blob IDs are SHA-1; matching git.
// ...
}The G<NN> codes differ by site: G505 flags the crypto/sha1 import (blocklisted import), G401 flags the sha1.New() call (weak crypto primitive). Pass every code that fires on a given line.
Canonical example in this repo: cli/internal/drrebuild/drrebuild.go.
Future Features (Go 1.24+)
This section tracks features by first-supported Go version and can be used to plan future target upgrades.
| Feature | Version | What It Replaces |
|---|---|---|
t.Context() | 1.24+ | context.WithCancel(context.Background()) in tests |
b.Loop() | 1.24+ | for i := 0; i < b.N; i++ in benchmarks |
omitzero JSON tag | 1.24+ | omitempty (which fails for time.Duration, structs, slices, maps) |
strings.SplitSeq / FieldsSeq | 1.24+ | strings.Split when iterating (avoids intermediate slice) |
wg.Go(fn) | 1.25+ | wg.Add(1) + go func() { defer wg.Done(); ... }() |
new(val) | 1.26+ | x := val; &x for pointer creation |
errors.AsType[T](err) | 1.26+ | var target T; errors.As(err, &target) |
JavaScript Standards (Tier 1)
Required
- ES2020 or newer (Node 18+ runtime).
prettierfor formatting;eslintwith the recommended ruleset.package.jsondeclares"type": "module"for new packages.
Style
constby default;letonly when reassignment is required; nevervar.- Arrow functions for callbacks; named
functionfor top-level declarations. - Strict equality (
===/!==) — no loose equality. - One module per file; default export only when the module is the unit.
Async
async/awaitover raw.then()chains.- Always
awaitor explicitly handle returned Promises. - Reject errors with
Errorinstances, never raw strings.
Error Handling
- No empty
catch {}blocks; either re-throw or log with context. - Use
try/catchonly at boundaries (HTTP, IO, IPC); let errors bubble inside pure logic. - Validate external input before use; trust internal callers.
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
==, != | Coerces types silently | Use ===, !== |
parseInt(x) | Defaults to base 10 only since ES5 but easy to miss | Pass radix: parseInt(x, 10) |
for...in on arrays | Iterates inherited enumerable props | Use for...of or .forEach |
| Mutating shared state | Hard-to-trace bugs | Spread/Object.assign for copies; Array methods that return new arrays |
| Float arithmetic | 0.1 + 0.2 !== 0.3 | Round to integer cents before compare |
Testing
- Vitest or Jest;
node --testis acceptable for small libraries. - Use
describe/itblocks; one logical assertion perit. - Mock external services; don't mock the unit under test.
- Snapshot tests only for stable serialized output, never for UI-rich strings.
Security
- Never use
eval(),Function(), ornew Function()with untrusted input. - Sanitize HTML before injecting into the DOM; prefer
textContentoverinnerHTML. - Use
crypto.randomUUID()/crypto.getRandomValues(), notMath.random(), for tokens. - Pin dependency versions in
package-lock.jsonorpnpm-lock.yaml; audit withnpm auditbefore release.
JSON Standards (Tier 1)
Validation
- Valid JSON (use
jq .to verify) - Consistent formatting (2-space indent)
- No trailing commas
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
| Trailing comma | Parse error | Remove |
| Single quotes | Invalid JSON | Double quotes only |
| Comments | Invalid JSON | Remove or use JSONC |
| Unquoted keys | Invalid JSON | Quote all keys |
JSONL (newline-delimited)
- One JSON object per line
- No trailing newline on last line
- Each line must be valid JSON
Schema Validation
- Use JSON Schema for validation
- Reference:
"$schema": "https://..." - Required fields should be explicit
Security
- Never use
eval()orFunction()to parse JSON — useJSON.parse() - Validate against JSON Schema before processing untrusted input
- Watch for prototype pollution in JavaScript/TypeScript JSON handling
- Sanitize keys and values when constructing JSON from user input
Large Files
- Consider JSONL for append-only logs
- Use streaming parsers for large files
- Compress with gzip for storage
LLM Trust Boundary Checklist
Domain-specific checklist for code that calls LLM APIs or processes LLM outputs.
Mandatory Checks
Input Validation
- [ ] User-supplied prompts are sanitized (no prompt injection vectors)
- [ ] System prompts are not exposed to end users
- [ ] Prompt templates use parameterized injection points, not string concatenation
- [ ] Input length limits enforced before API call (prevent token budget exhaustion)
Output Validation
- [ ] LLM output is validated against expected schema before use
- [ ] JSON responses are parsed with strict schema validation (not just
json.loads()) - [ ] Hallucinated field names/values are detected and rejected
- [ ] Output is never used as code input without sandboxing (
eval(),exec(), shell commands) - [ ] Empty responses handled explicitly (not silently passed through)
Error Handling
- [ ] API timeout has explicit handling (retry with backoff)
- [ ] Rate limit (429) has backoff strategy
- [ ] Model refusal detected and handled (not treated as valid output)
- [ ] Malformed response has retry-with-stricter-prompt fallback
- [ ] Cost/token budget tracked per request (prevent runaway spending)
Trust Boundaries
- [ ] LLM output treated as untrusted input at every boundary
- [ ] No direct database writes from LLM output without validation
- [ ] No file system operations from LLM output without path validation
- [ ] No network requests to LLM-generated URLs without allowlist check
- [ ] User-visible LLM output has content safety filtering
Observability
- [ ] Request/response pairs logged (with PII redaction)
- [ ] Token usage tracked per call and per session
- [ ] Latency metrics captured (p50, p95, p99)
- [ ] Retry counts and failure modes tracked
- [ ] Model version pinned and logged (not just "latest")
Testing
- [ ] Tests cover malformed response handling
- [ ] Tests cover empty response handling
- [ ] Tests cover refusal handling
- [ ] Tests use deterministic fixtures, not live API calls
- [ ] Evaluation suite exists for output quality regression
When to Apply
Load this checklist when:
- Changed files import
anthropic,openai,google.generativeai, or similar - Code constructs prompts or processes LLM responses
- Plan includes LLM integration or AI-powered features
- Files match patterns:
*llm*,*ai*,*prompt*,*completion*,*chat*
Markdown Standards (Tier 1)
Structure
- Single H1 (
#) at top - Hierarchical headings (don't skip levels)
- Blank line before/after headings
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
| Multiple H1s | Confusing structure | Single H1 |
| Skipped heading | H1 → H3 | H1 → H2 → H3 |
| No blank lines | Rendering issues | Blank before/after blocks |
| Hard line breaks | Formatting | Let text wrap naturally |
Tables
| Header | Header |
|--------|--------|
| Cell | Cell |- Align
|for readability - Use
-for header separator
Code Blocks
- Always specify language:
`python - Use inline `
code` for short refs - 4-space indent also works (but fenced preferred)
Links
- Use descriptive link text, not generic "click here"
- Use relative paths for local references
- Check links aren't broken
Python Standards (Tier 1)
Required
ruff checkpasses (orflake8)ruff format(orblack) for formatting- Type hints on public functions
- Docstrings on public classes/functions
Error Handling
- Never bare
except:- always specify exception type - Use
raise ... from eto preserve stack traces - Log before raising in library code
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
except Exception: | Too broad | Catch specific exceptions |
# type: ignore | Hiding problems | Fix the type error |
eval() / exec() | Security risk | Use safer alternatives |
| Mutable default args | Shared state bugs | Use None + conditional |
Security
- Never use
eval(),exec(), or__import__()with untrusted input - Use
secretsmodule for tokens, notrandom - Validate and sanitize all external input (user data, file paths, URLs)
- Use parameterized queries for SQL — never string formatting
Dataclass & Model Contract Completeness
When adding fields to a dataclass, Pydantic model, or TypedDict, every code path that creates an instance must populate them.
| Anti-Pattern | Problem | Fix |
|---|---|---|
New field with default=None, some constructors never set it | Consumers see None for some paths, real value for others | Grep all ClassName( calls; verify each sets the new field |
| Synthesized instances (e.g., summary dicts, fallback objects) skip fields | Downstream code assumes all instances have the same shape | Store provenance metadata alongside state; populate synthesized instances from it |
| Index fields after sort | event_index points to sorted position, not caller's original position | Zip with enumerate() before sorting; emit original index |
__init__ sets fields conditionally | Some branches leave fields unset | Use field(default_factory=...) or set in all branches |
Checklist for adding fields: 1. Grep ClassName( across the package — every constructor call must set the new field 2. Check factory functions (from_dict, from_json, create_*) 3. Check synthesized/summary instances created outside the main loop 4. Add a structural assertion test (see below)
Wire Input Validation
When parsing external JSON/YAML into models with enum-like fields, validate against known values before trusting.
# BAD: trust whatever the wire sends
if event.error_class:
# use as-is — "bogus" passes through
# GOOD: validate against known values
VALID_ERROR_CLASSES = {"timeout", "rate_limit", "auth_failure", ...}
if event.error_class and event.error_class not in VALID_ERROR_CLASSES:
event.error_class = classify_error(event) # reclassify from contentFor Pydantic models, use Literal types or @field_validator to reject invalid values at parse time:
from typing import Literal
class StreamEvent(BaseModel):
error_class: Literal["timeout", "rate_limit", "auth_failure", ""] = ""Also normalize impossible states: if is_error=False but error_class="timeout", use a @model_validator to clear it.
Classification & Pattern Matching
When classifying inputs by string patterns (error types, log levels, status codes):
| Anti-Pattern | Problem | Fix |
|---|---|---|
"429" in msg | Matches port numbers, line numbers | Use regex with context: `r'\b(status |
Bare keyword match ("sandbox" in msg) | "sandbox startup failed" misclassifies as sandbox violation | Require compound match: keyword + policy phrase (denied, violation) |
| Meaningless default case | return "unknown" for both truly-unknown and simply-unrecognized | Make default semantic: "execution_error" for non-empty, "unknown" for empty |
| No false-positive test coverage | Tests only check happy paths | Generate 5+ realistic false-positive inputs per pattern |
Testing
Exact Assertion Rule
Always assert the exact expected value, never just "not the wrong one."
# BAD: passes even if classification drifts to a different wrong class
assert classify(msg) != "rate_limit"
# GOOD: pins the exact expected behavior
assert classify(msg) == "execution_error"This applies to all classifier/enum tests. != X assertions silently pass when the result drifts to a third, equally wrong value.
Structural Invariant Tests
For dataclasses/models with required fields, add a sweep test that asserts ALL output instances populate them:
def test_all_violations_have_structured_fields(violations):
"""Every violation must populate team_name, timestamp, and event_index."""
for v in violations:
assert v.team_name, f"violation {v} missing team_name"
assert v.timestamp is not None, f"violation {v} missing timestamp"Property-Based Tests (BF1)
Use Hypothesis to randomize inputs to data transformations:
from hypothesis import given
import hypothesis.strategies as st
@given(st.dictionaries(
keys=st.from_regex(r'[A-Z_]+', fullmatch=True),
values=st.text(min_size=0, max_size=200),
min_size=1,
))
def test_parse_reader_never_crashes(env_vars):
"""Any valid config must parse without crashing."""
stream = io.StringIO("\n".join(f"{k}={v}" for k, v in env_vars.items()))
ctx = parse_reader(stream)
assert isinstance(ctx, SiteContext)Target: every parser, serializer, and data transformer. If it accepts external input, fuzz it.
Backward Compatibility Tests (BF8)
Maintain a corpus of real inputs from prior versions as fixtures:
from glob import glob
@pytest.mark.parametrize("fixture", sorted(glob("tests/fixtures/compat/*.env")))
def test_legacy_config_parses(fixture):
"""Every historical config format must still parse."""
ctx = parse_config_env(fixture)
assert ctx.site_name # at least one required field populatedRule: When changing input formats, add the OLD format as a fixture BEFORE making the change.
Performance/Benchmark Tests (BF7)
Use pytest-benchmark for hot-path functions:
def test_parse_config_performance(benchmark):
"""Parser must handle large configs without regression."""
large_config = "\n".join(f"KEY_{i}=value_{i}" for i in range(1000))
result = benchmark(parse_reader, io.StringIO(large_config))
assert isinstance(result, SiteContext)Install: pip install pytest-benchmark. Run: pytest --benchmark-only.
Regression Tests (BF6)
Every bug fix gets a reproducing test named after the bug ID:
def test_bug_ag_m0r_empty_value_crashes():
"""Regression: parse_reader crashed on config lines with empty values (ag-m0r)."""
stream = io.StringIO("SITE_NAME=\nDB_HOST=prod-db")
ctx = parse_reader(stream)
assert ctx.site_name == ""
assert ctx.db_host == "prod-db"Security Tests (BF9)
Test secrets redaction and input sanitization:
def test_render_export_redacts_secrets():
"""render_export must never emit raw secret values."""
ctx = SiteContext(site_name="test", db_password="s3cr3t!", api_key="ak-12345")
output = render_export(ctx)
assert "s3cr3t!" not in output, "raw password leaked"
assert "ak-12345" not in output, "raw API key leaked"
def test_rejects_path_traversal():
"""Config paths must reject traversal attempts."""
for payload in ["../../../etc/passwd", "..\\windows", "foo/../bar"]:
with pytest.raises(ValueError):
load_config(payload)General
- pytest preferred
conftest.pyfor shared fixtures- Mock external services, not internal code
Test Conventions
- pytest preferred;
conftest.pyfor shared fixtures. - ruff linter:
ruff checkmust pass. - mypy for type checking.
- Black formatter with 100-character line length. Config in
pyproject.toml. - Type hints on all public functions.
- Docstrings on all public classes and functions.
Security
- Never use
eval(),exec(), or__import__()with untrusted input. - Use
secretsmodule for tokens, notrandom. - Validate all external input.
- Never bare
except:— always specify the exception type. - Use
raise ... from eto preserve stack traces.
Race Condition Checklist
Domain-specific checklist for concurrent, parallel, or multi-process code.
Mandatory Checks
Shared State
- [ ] All shared mutable state protected by mutex/lock/atomic
- [ ] No global mutable variables accessed from multiple goroutines/threads
- [ ] Map/dict access synchronized (Go maps are NOT goroutine-safe)
- [ ] Slice/list append operations synchronized when shared
- [ ] Read-write locks used where reads dominate (not exclusive mutex everywhere)
File System Races
- [ ] Check-then-act on files uses atomic operations (temp file + rename)
- [ ] File locks used for multi-process coordination
- [ ] PID files checked with
flockor equivalent, not just[ -f ] - [ ] Directory creation uses
mkdir -p(idempotent), not check-then-create - [ ] Log file rotation handles concurrent writers
Database Races
- [ ] Upsert uses
INSERT ... ON CONFLICT(not check-then-insert) - [ ] Counter increments use
UPDATE ... SET x = x + 1(not read-modify-write) - [ ] Unique constraint violations handled with retry (not just error)
- [ ] Optimistic locking uses version column for concurrent updates
- [ ] Queue consumers use
SELECT ... FOR UPDATE SKIP LOCKED
API / Network Races
- [ ] Idempotency keys used for non-idempotent API calls
- [ ] Retry logic uses exponential backoff (not fixed delay)
- [ ] Circuit breaker pattern for failing external services
- [ ] Request deduplication for concurrent identical requests
- [ ] Webhook handlers are idempotent (same event delivered twice = same result)
Go-Specific
- [ ] Channel sends/receives have timeout or context cancellation
- [ ]
sync.WaitGroupcounter matches goroutine count exactly - [ ]
defer mu.Unlock()immediately aftermu.Lock()(no early return gap) - [ ] Race detector run:
go test -race ./... - [ ] Context propagation through goroutine chains (no orphaned goroutines)
Python-Specific
- [ ]
threading.Lockused for shared state (GIL doesn't protect everything) - [ ]
asynciotasks properly awaited (no fire-and-forget without tracking) - [ ]
multiprocessingshared state usesManagerorValue/Array - [ ] File I/O in async code uses
aiofiles(not blockingopen())
Testing
- [ ] Concurrent tests exist (multiple goroutines/threads hitting same code)
- [ ] Race detector enabled in CI (
go test -race,PYTHONFAULTHANDLER=1) - [ ] Stress tests for hot paths (100+ concurrent operations)
- [ ] Deterministic ordering tests (verify no output depends on scheduling)
When to Apply
Load this checklist when:
- Code uses goroutines, threads,
asyncio,multiprocessing, orconcurrent.futures - Multiple processes read/write the same files
- Database operations involve concurrent access patterns
- Plan mentions "parallel", "concurrent", "async", "worker pool", or "queue"
- Code uses
sync.Mutex,threading.Lock,asyncio.Lock, or similar primitives
Rust Standards (Tier 1)
Required
cargo fmt(automatic)cargo clippypasses (no warnings)- All public items documented (rustdoc)
Error Handling
- Use
Result<T, E>for fallible operations - Implement custom errors with
thiserrororanyhow - Never
unwrap()in library code (OK in tests/bins) - Use
?operator for error propagation
Adapter Recursion Guard
- Subprocess adapters that can invoke their own kernel must set a guard env var
on every child command: <TOOL>_IN_PROGRESS=1.
- Kernel entry must reject re-entry when that env var is already present.
- This is a two-end check: set-on-spawn plus check-at-entry. One end alone is
not enough.
- Source pattern: commit
97e16fe, beadmo-l1tyqp.23, and
MTO_SKILL_AUDIT_IN_PROGRESS from the Mt Olympus skill-audit adapter fix.
pub const GUARD_ENV: &str = "MY_TOOL_IN_PROGRESS";
fn command() -> std::process::Command {
let mut cmd = std::process::Command::new("sh");
cmd.env(GUARD_ENV, "1");
cmd
}
fn entry() -> Result<(), MyError> {
if std::env::var_os(GUARD_ENV).is_some() {
return Err(MyError::Recursion);
}
Ok(())
}Ownership & Borrowing
- Prefer references over cloning
- Use
&strin function params overString - Add explicit lifetime annotations when needed
- Clone sparingly and document why
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
unwrap() | Panic on None/Err | Use ? or pattern match |
| Mutable statics | Data races | Use once_cell or Mutex |
| String allocation | Performance | Use &str in function params |
| Lifetime errors | Borrow checker reject | Add explicit lifetimes |
| Unsafe block | Memory unsafety | Add // SAFETY: comment |
Excessive .clone() | Performance waste | Use references or Cow<T> |
Unsafe Code
- Always add
// SAFETY:comment explaining invariants - Minimize unsafe scope
- Prefer safe abstractions
Security
- Minimize
unsafeblocks — each needs// SAFETY:justification - Use
secrecy::Secret<T>for sensitive values (prevents accidental logging) - Validate all external input before deserialization (
serdevalidators) - Prefer
ringorrustlsover OpenSSL bindings
Documentation
- All public items must have rustdoc comments (
///) - Include
# Examplessection in doc comments for complex APIs - Use
#![deny(missing_docs)]in library crates - Run
cargo doc --no-depsto verify doc builds
Testing
cargo test(built-in)cargo test --doc(doc tests)- Use
#[cfg(test)]modules cargo benchfor benchmarks
Shell Standards (Tier 1)
Required Header
#!/usr/bin/env bash
set -euo pipefailValidation
shellcheckmust pass- Quote all variables:
"$var"not$var
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
Unquoted $var | Word splitting | "$var" |
cd without check | Silent failure | `cd dir \ |
[ ] vs [[ ]] | Portability | Use [[ ]] in bash |
| Backticks | Nesting issues | Use $(command) |
Best Practices
- Use
localfor function variables - Trap errors:
trap 'cleanup' ERR EXIT - Check command existence:
command -v foo >/dev/null - Use
readonlyfor constants
Cluster Scripts
- Always verify connectivity first:
oc whoami &>/dev/null || { echo "Not logged in"; exit 1; }Skill Structure Standard
Version: 2.0.0 Last Updated: 2026-02-20 Source: Claude Code official documentation (https://code.claude.com/docs/en/skills) Purpose: Defines the required structure, frontmatter, and quality standards for all AgentOps skills.
---
Table of Contents
1. File Structure 2. YAML Frontmatter 3. Description Field 4. Body Structure 5. Progressive Disclosure 6. Quality Checklist 7. AgentOps Extensions
---
File Structure
skill-name/
├── SKILL.md # Required — exact case, no variations
├── SELF-TEST.md # Optional — trigger and behavior self-test
├── scripts/ # Optional — helper code
├── references/ # Optional — progressive disclosure docs
└── assets/ # Optional — templates, fonts, iconsRules
| Rule | ALWAYS | NEVER |
|---|---|---|
| Entry point | SKILL.md (exact case) | skill.md, SKILL.MD, Skill.md |
| Folder name | kebab-case (bug-hunt) | spaces, underscores, capitals |
| Name match | Folder name = name: field | Mismatch between folder and frontmatter |
| README | None inside repo-runtime skill folders unless an external package profile explicitly allows it | Accidental README.md drift in normal AgentOps skill directories |
| Self-test | SELF-TEST.md for market-facing execution, judgment, and product skills | User-facing publishable skill with no trigger/behavior test |
| Reserved | Any valid kebab-case name | claude-* or anthropic-* prefixes |
---
YAML Frontmatter
Required Fields
---
name: skill-name
description: 'What it does. When to use it. Trigger phrases.'
---Only description is technically required (recommended). If name is omitted, the directory name is used.
All Claude Code Frontmatter Fields
| Field | Required | Purpose |
|---|---|---|
name | No | Display name. Lowercase letters, numbers, hyphens only (max 64 chars). Defaults to directory name. |
description | Recommended | What the skill does and when to use it. Claude uses this to decide when to load the skill. |
argument-hint | No | Hint shown during autocomplete (e.g., [issue-number], [filename] [format]). |
disable-model-invocation | No | Set to true to prevent Claude from auto-loading. User must invoke with /name. Default: false. |
user-invocable | No | Set to false to hide from / menu. Use for background knowledge. Default: true. |
allowed-tools | No | Tools Claude can use without permission when skill is active (e.g., Read, Grep, Glob). |
model | No | Model to use when skill is active (sonnet, opus, haiku, inherit). |
context | No | Set to fork to run in a forked subagent context. Only for worker spawner skills (e.g., council, codex-team). Never set on orchestrators (evolve, rpi, crank) — they need visibility. See two-tier rule in SKILL-TIERS.md. |
agent | No | Which subagent type to use when context: fork is set (e.g., Explore, Plan, general-purpose). |
hooks | No | Hooks scoped to this skill's lifecycle. |
Execution Mode (Three-Tier Rule)
Skills follow a three-tier execution model based on what the caller needs to see:
| Mode | context: { window: fork } | When to use |
|---|---|---|
| Orchestrator | Do NOT set | Skills that loop, gate phases, or report progress (evolve, rpi, crank) |
| Discovery primitive | Set window: fork | Skills that explore/decompose and produce filesystem artifacts (research, plan) |
| Worker spawner / Judgment | Set window: fork | Skills that fan out parallel workers or validate artifacts (council, vibe, pre-mortem) |
When window: fork is set, the skill's markdown body becomes the task prompt for a forked subagent. The subagent runs in isolation — only the summary returns to the caller's context.
Optionally add execution_mode to the metadata block for documentation (informational only — no tooling reads this field):
metadata:
tier: execution
execution_mode: orchestrator # informational — stays in main contextSee SKILL-TIERS.md for the full classification table and tier definitions.
Invocation Control Matrix
| Frontmatter | User can invoke | Claude can invoke | Context loading |
|---|---|---|---|
| (default) | Yes | Yes | Description always in context, full skill loads when invoked |
disable-model-invocation: true | Yes | No | Description not in context, full skill loads when user invokes |
user-invocable: false | No | Yes | Description always in context, full skill loads when invoked |
String Substitutions
| Variable | Description |
|---|---|
$ARGUMENTS | All arguments passed when invoking the skill |
$ARGUMENTS[N] | Specific argument by 0-based index |
$N | Shorthand for $ARGUMENTS[N] |
${CLAUDE_SESSION_ID} | Current session ID |
Dynamic Context Injection
The ` !command ` syntax runs shell commands before skill content is sent to Claude:
## Context
- Current branch: !`git branch --show-current`
- Recent changes: !`git log --oneline -5`AgentOps Extension Fields (under metadata:)
AgentOps uses these custom fields under metadata: for tooling integration:
metadata:
tier: execution # see schema enum + tier-caps reference below
dependencies: # List of skill names this skill depends on
- standards
- council
internal: true # true for non-user-facing skills
replaces: old-name # Deprecated skill this replacesAllowed tier values (binding — from scripts/validate-skill-schema.sh:174-178): judgment, execution, library, session, product, contribute, meta, background, orchestration, cross-vendor, knowledge.
Per-tier line caps: see `skill-tier-caps.md` for the canonical mapping enforced by tests/skills/lint-skills.sh:65-77.
SKILL-TIERS.md describes "utility", "team", and "solo" categories — these are narrative groupings, NOT binding metadata.tier values. Skills in those categories use tier: execution (e.g., bug-hunt, brainstorm, system-tuning).
Security Restrictions
- No XML angle brackets (
<>) in frontmatter - No
claudeoranthropicin skill names - YAML safe parsing only (no code execution)
---
Description Field
The description is the most critical field — it determines when Claude loads the skill.
Structure
[What it does] + [When to use it] + [Key capabilities]Requirements
- Target under 120 characters; hard limit 180 characters for the repo catalog
- Converter adapters may still enforce their own external hard caps, but repo
skills should stay far below those caps so all descriptions fit the always-loaded catalog
- MUST include trigger phrases users would actually say
- MUST explain what the skill does (not just when)
- No XML tags
Good Examples
# Specific + actionable + triggers
description: 'Investigate bugs or audit code with repro evidence, root cause analysis, and fixes.'
# Clear value prop + multiple triggers
description: 'Validate code readiness with complexity checks and council or inline review.'Bad Examples
# Too vague
description: Helps with projects.
# Missing triggers
description: Creates sophisticated multi-page documentation systems.
# Too technical, no user triggers
description: Implements the Project entity model with hierarchical relationships.Internal Skills Exception
Library/background/meta skills that are auto-loaded (not user-invoked) may describe their loading mechanism instead of user triggers:
description: 'Auto-loaded by /validate, /implement based on file types.'---
Body Structure
Recommended Template
---
name: skill-name
description: '...'
metadata:
tier: execution
---
# Skill Name
## Quick Start
Example invocations showing common usage patterns.
## Instructions
### Step 1: [First Major Step]
Specific, actionable instructions with exact commands.
### Step 2: [Next Step]
...
## Examples
### Example 1: [Common scenario]
User says: "..."
Actions: ...
Result: ...
## Troubleshooting
### Error: [Common error]
Cause: ...
Solution: ...Requirements
| Aspect | Requirement |
|---|---|
| Size | Under 5,000 words; per-tier line caps enforced (see skill-tier-caps.md). PreToolUse hook warns at 248 lines globally. |
| Instructions | Specific and actionable (exact commands, not "validate the data") |
| Examples | At least 2-3 usage examples for user-facing skills |
| Error handling | Troubleshooting section for common failures |
| References | Link to references/ for detailed docs (don't inline everything) |
---
Progressive Disclosure
Skills use three levels:
1. Frontmatter — Always in system prompt. Minimal: name + description. 2. SKILL.md body — Loaded when skill is relevant. Core instructions. 3. references/ — Loaded on-demand. Detailed docs, schemas, examples.
Rules
- Keep SKILL.md focused on core workflow
- Move detailed reference material to
references/ - Explicitly link to references: "Read
references/api-patterns.mdfor..." - Move scripts >20 lines to
scripts/directory - Move inline bash >30 lines to
scripts/orreferences/
---
Quality Checklist
Before Commit
- [ ]
SKILL.mdexists (exact case) - [ ] Folder name matches
name:field - [ ] Folder name is kebab-case
- [ ] Description includes WHAT + WHEN (triggers)
- [ ] Description under 180 characters, preferably under 120
- [ ] No XML tags in frontmatter
- [ ] No
claude/anthropicin name - [ ]
metadata.tieris set and valid - [ ] SKILL.md under 5,000 words
- [ ] User-facing skills have examples section
- [ ] User-facing skills have troubleshooting section
- [ ] Market-facing user skills have
SELF-TEST.md - [ ] Detailed docs in references/, not inlined
- [ ] No README.md in repo-runtime skill folder unless an external package profile explicitly permits it
Trigger Testing
- [ ] Triggers on 3+ obvious phrases
- [ ] Triggers on paraphrased requests
- [ ] Does NOT trigger on unrelated topics
---
AgentOps Extensions
These are AgentOps-specific patterns not in the Claude Code spec:
Tier System
Controls line limits and categorization. Enforced by tests/skills/lint-skills.sh.
Dependencies
Declared under metadata.dependencies. Validated by tests/skills/validate-skill.sh.
Skill Tiers Document
Full taxonomy at skills/SKILL-TIERS.md.
Standards Loading
Language standards loaded JIT by /validate, /implement — see references/standards-index.md.
Marketplace Export Profile
A marketplace-facing package shape differs from AgentOps' repo-runtime shape:
- Published package candidates should pass the target marketplace validator before release.
- Package-clean skills should stay at or under a 50-file validator limit.
- Mega skills above that limit should be split or handled as an explicit product-bundle profile.
- Exported
scripts/files should be non-executable for marketplace validation, even if repo-native AgentOps scripts remain executable. - Strong market-facing skills should include
SELF-TEST.md. - Large skills should keep
SKILL.mdas a routing kernel and move expensive context intoreferences/,scripts/,assets/, and, when justified,subagents/.
Use docs/reference/skill-quality-rubric.md for scoring against this profile.
SKILL.md Tier-Specific Line Caps
Source of truth: tests/skills/lint-skills.sh:65-77 (case statement). The PreToolUse hook warns at 248 lines globally, but the binding cap is per-tier. A 260-line meta-tier skill is broken; a 260-line execution-tier skill is fine.
Caps by tier
| Tier | Cap (lines) |
|---|---|
library, meta | 250 |
background | 300 |
execution | 800 |
judgment, product, session, knowledge, contribute, cross-vendor, orchestration | 1050 |
Why per-tier instead of global
metaandlibraryskills are short reference contracts; long bodies indicate misuse.backgroundskills are autoloaded — long bodies bloat the always-loaded context.executionskills run multi-step workflows; their bodies legitimately need more room for steps and examples.judgment/product/sessionskills carry decision frameworks and personas that benefit from narrative depth.
How to plan trimming work
1. Read the skill's tier: frontmatter. 2. Look up the tier's cap in the table above. 3. If wc -l SKILL.md exceeds the cap, plan trimming. Otherwise no work needed.
The 248-line PreToolUse warning is global pre-lint awareness only — it does not mean the skill is in violation.
Cross-references
- Lint enforcement:
tests/skills/lint-skills.sh:65-77 - Schema enum:
scripts/validate-skill-schema.sh:174-178(the bindingmetadata.tierenum) - Narrative tier categories:
skills/SKILL-TIERS.md(note: "utility category" there is a grouping, not a binding tier value)
SQL Safety Checklist
Domain-specific checklist for code that interacts with databases.
Mandatory Checks
Injection Prevention
- [ ] All user input is parameterized (no string interpolation in queries)
- [ ] ORM queries use parameter binding, not f-strings or
.format() - [ ] Raw SQL uses
?or$Nplaceholders, never concatenation - [ ] Dynamic table/column names are validated against an allowlist
Migration Safety
- [ ] Migrations are reversible (both
upanddowndefined) - [ ] No
DROP TABLEorDROP COLUMNwithout explicit data migration plan - [ ] Large table migrations use batched operations (not full-table locks)
- [ ] Index creation uses
CONCURRENTLYwhere supported (PostgreSQL) - [ ] Migration tested on production-size dataset (not just empty dev DB)
Query Performance
- [ ] Queries touching >1000 rows have appropriate indexes
- [ ] No
SELECT *in production code (explicit column lists) - [ ] N+1 queries identified and resolved (use
includes/preload/JOIN) - [ ] Pagination used for unbounded result sets
- [ ]
EXPLAIN ANALYZErun on new queries touching large tables
Transaction Safety
- [ ] Long-running transactions avoided (< 30s)
- [ ] Deadlock-prone operations use consistent lock ordering
- [ ] Retry logic for serialization failures / deadlocks
- [ ] Connection pool sized for peak concurrent transactions
Data Integrity
- [ ] Foreign keys enforced at database level (not just application)
- [ ] NOT NULL constraints on required fields
- [ ] Unique constraints on business-key columns
- [ ] Check constraints on bounded values (enums, ranges)
- [ ] Soft deletes use
deleted_attimestamp, not boolean
When to Apply
Load this checklist when:
- Changed files contain SQL queries or ORM calls
- Migration files are in the changeset
- Database schema changes are proposed in the plan
- Code interacts with
database/sql,sqlx,gorm,sqlalchemy,activerecord,prisma,knex, or similar
Standards Index
JIT loading map for validation agents. Load only what you need based on file types.
Extension to Standard Map
| Extension | Standard File | Size |
|---|---|---|
.py | skills/validate/references/python-standards.md | 32k |
.go | skills/validate/references/go-standards.md | 28k |
.rs | skills/validate/references/rust-standards.md | 40k |
.ts, .tsx | skills/validate/references/typescript-standards.md | 24k |
.sh, .bash | skills/validate/references/shell-standards.md | 20k |
.yaml, .yml | skills/validate/references/yaml-standards.md | 16k |
.json | skills/validate/references/json-standards.md | 12k |
.md | skills/validate/references/markdown-standards.md | 8k |
Universal Standards (Always Load)
| Standard | File | Purpose |
|---|---|---|
| Vibe-Coding | skills/validate/references/vibe-coding.md | Trust calibration, metrics, failure patterns |
| Common Standards | skills/standards/references/common-standards.md | Cross-language patterns: error handling, testing, security, docs, organization |
| Behavioral Discipline | skills/standards/references/behavioral-discipline.md | Assumptions, simplicity bias, blast-radius control, verification discipline |
| Skill Structure | skills/standards/references/skill-structure.md | Anthropic-compliant skill structure, frontmatter, quality checklist |
Always load vibe-coding.md first, then common-standards.md and behavioral-discipline.md for universal patterns.
Pattern Files (Load When Relevant)
| Pattern Type | File | When to Load |
|---|---|---|
| Go patterns | skills/validate/references/go-patterns.md | Go architecture review |
| General patterns | skills/validate/references/patterns.md | Design review |
| Report format | skills/validate/references/report-format.md | Writing vibe reports |
| Codex skill standard | skills/standards/references/codex-skill.md | Codex skill files, converter output, skills-codex/ |
JIT Loading Pattern for Agents
## Step 1: Detect File Types
Scan the target files to identify languages:
- Use Glob to find files
- Note extensions present
## Step 2: Load Relevant Standards
For each language detected, use Read tool:
Tool: Read
Parameters:
file_path: "skills/validate/references/<language>-standards.md"
Only load standards for languages actually present in the review.
## Step 3: Apply Standards
Reference the loaded standards when validating code.
Cite specific sections: "Per python-standards.md section 3.2..."Example: Mixed Python/Go Review
Files detected: src/main.py, pkg/handler.go, scripts/deploy.sh
Load (3 standards only):
1. Read("skills/validate/references/python-standards.md")
2. Read("skills/validate/references/go-standards.md")
3. Read("skills/validate/references/shell-standards.md")
Skip: typescript, yaml, json, markdown (not present)Context Budget
| Agent Model | Context Budget | Max Standards |
|---|---|---|
| haiku | ~100k | 3-4 standards |
| opus | ~200k | All if needed |
Keep agents lean. Load only what's needed.
JIT Loading Order
1. vibe-coding.md (universal — trust calibration, failure patterns) 2. common-standards.md (universal — cross-language error handling, testing, security, docs, organization) 3. behavioral-discipline.md (universal — assumptions, simplicity, scope control, verification) 4. Language standards (per detected extensions) 5. Pattern files (if architecture/discovery review)
TypeScript Standards (Tier 1)
Required
strict: truein tsconfig.jsonprettierfor formattingeslintwith recommended rules
Type Safety
- No
any- useunknown+ type guards - No
@ts-ignorewithout explanation - Prefer
interfacefor objects,typefor unions
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
as Type | Unsafe cast | Type guards or satisfies |
! (non-null) | Runtime errors | Proper null checks |
== null | Loose equality | `=== null \ |
Implicit any | Type safety loss | Enable noImplicitAny |
React (if applicable)
- Functional components only
useState/useReducerfor stateuseEffectwith proper deps array- No inline object/function props (memo issues)
Testing
- Jest or Vitest
- React Testing Library for components
- MSW for API mocking
YAML Standards (Tier 1)
Validation
yamllintmust pass- 2-space indentation
- No trailing whitespace
Common Issues
| Pattern | Problem | Fix |
|---|---|---|
| Tabs | Invalid YAML | 2 spaces |
yes/no unquoted | Becomes boolean | Quote: "yes" |
: in value | Parse error | Quote the value |
| Long lines | Readability | Use > or `\ |
Kubernetes/Helm
- Use
---between documents - Labels:
app.kubernetes.io/* - Always specify
resources.limits - Use ConfigMaps for config, Secrets for secrets
Security
- Never use
yaml.load()(Python) — alwaysyaml.safe_load() - Quote values that look like booleans (
"yes","no","true") - Validate against schema before processing untrusted YAML
- Avoid anchors/aliases (
*/&) in user-facing configs — confusing and exploitable
Multiline Strings
# Literal (preserves newlines)
description: |
Line 1
Line 2
# Folded (joins lines)
description: >
This becomes
one line#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
PASS=0; FAIL=0
check() { if bash -c "$2"; then echo "PASS: $1"; PASS=$((PASS + 1)); else echo "FAIL: $1"; FAIL=$((FAIL + 1)); fi; }
check "SKILL.md exists" "[ -f '$SKILL_DIR/SKILL.md' ]"
check "SKILL.md has YAML frontmatter" "head -1 '$SKILL_DIR/SKILL.md' | grep -q '^---$'"
check "name is standards" "grep -q '^name: standards' '$SKILL_DIR/SKILL.md'"
check "mentions language-specific or coding standards" "grep -qiE 'language-specific|coding standards' '$SKILL_DIR/SKILL.md'"
check "has references directory" "[ -d '$SKILL_DIR/references' ]"
echo ""; echo "Results: $PASS passed, $FAIL failed"
[ $FAIL -eq 0 ] && exit 0 || exit 1
Standards Skill Self-Test
Trigger Cases
- User says: "audit this skill against AgentOps skill structure standards."
- Expected: load
standardsas a library skill and usereferences/skill-structure.md.
- User says: "validate this Go change against repo standards."
- Expected: load
standardsand usereferences/go.md.
- User says: "check whether this skill can absorb an external corpus safely."
- Expected: load
references/external-source-attribution.mdand apply its clean-room policy when applicable.
Non-Trigger Cases
- User asks to implement a feature with no files or language context.
- Expected: do not load every standards reference; wait until file types or risk patterns are known.
- User asks for general product strategy.
- Expected: use product/discovery skills, not
standards, unless code or skill standards become relevant.
Behavior Checks
standardsstays a library skill and writes no artifacts by itself.- Language standards load on demand by file type rather than all at once.
- Domain checklists load only when matching risk patterns are present.
- External-source absorption follows
references/external-source-attribution.md. - Skill authoring and export guidance points to
references/skill-structure.md.
Validation Commands
Run from the repo root:
bash skills/standards/scripts/validate.sh
bash skills/heal-skill/scripts/heal.sh --strict
bash scripts/validate-skill-frontmatter.sh --strictFailure Cases
- Missing reference file: fail skill validation and restore the missing file or remove the link from
SKILL.md. - Wrong standard selected: identify the file type or risk detector that selected it and update the relevant loading rule.
- External corpus content copied verbatim: stop, remove copied content, and keep only pattern-level observations with attribution.
Related skills
How it compares
Choose AgentOps standards when you need enforced ubiquitous-language consistency across agent surfaces rather than a standalone DDD explainer document.
FAQ
What does agentops standards cover?
Artifact naming, manifest fields, validation checkpoints, and required pipeline metadata.
Why align with converter skills?
So downstream converter and deployment steps receive predictable standardized outputs.
When should compliance be checked?
When onboarding new workflow stages or reviewing existing agentops automation repos.
Is Standards safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.