agentleFS
Build-FractalBuild-Fractal/orchestrator/scripts/AGENTS.md

orchestrator / scripts

AGENTS.md1 starsChanged 6 months agoMITView on GitHub

Progressive disclosure reference for contributing to orchestrator scripts. Self-contained -- read this document to understand coding conventions, testing patterns, and compliance requirements without cross-referencing other docs. Audience: contributors This guide covers everything needed to write, modify, and review scripts in the scripts/ directory. It targets both human contributors and AI agents executing tasks. The project is a spec-kit extension composed of markdown commands and bash helper scripts. All scripts live under scripts/ organized by concern: Scripts live under scripts/ organized…

  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,542 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository4 other files it gives its agents.

The file

Show its rules and text

We split this AGENTS.md into the rules it gives an agent

23 rules. About 0 are common, 17 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈17

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Every library file in scripts/lib/ MUST have a double-sourcing guard at the top, immediately after the header comment.

    line 84
  2. Distinctive:

    Every script follows this structure:

    line 178
  3. Very specific:

    Always use #!/usr/bin/env bash as the shebang

    line 218
  4. Very specific:

    Always set set -euo pipefail (strict mode)

    line 219
  5. Distinctive:

    Never mix free-form text with structured output on stdout.

    line 239
  6. Distinctive:

    Every test suite uses the same pass/fail counting pattern:

    line 263
  7. Very specific:

    Test fixtures live in tests/fixtures/.

    line 294
  8. Distinctive:

    When adding new functionality:

    line 307
  9. Distinctive:

    Add assertions to the appropriate existing test suite (match by concern).

    line 309
  10. Very specific:

    If the feature introduces a new concern, create a new suite as test-s##-<name>.sh with the next sequential number.

    line 310
  11. Distinctive:

    Use fixture directories for any test that needs disk state.

    line 312
  12. Distinctive:

    Test both the happy path AND error paths (missing arguments, invalid state).

    line 313
  13. Distinctive:

    Prefer testing script output (structured prefixed lines) over testing internal state.

    line 314
  14. Distinctive:

    When writing or reviewing scripts, verify compliance with each applicable principle.

    line 331
  15. Distinctive:

    All state is derived from files, never cached in memory.

    line 369
  16. Distinctive:

    Do knowledge-producing scripts write to the correct location in the hierarchy?

    line 377
  17. Distinctive:

    Every script must be reachable from a live code path.

    line 382
  18. Distinctive:

    If removing the script, does anything break?

    line 384
  19. Distinctive:

    If behavior changes require editing this script instead of a template, the boundary is wrong.

    line 396
  20. Distinctive:

    No duplicated state or configuration.

    line 401
  21. Very specific:

    Do context-assembly scripts follow the recipe declared in context-recipe.yaml?

    line 415
  22. Distinctive:

    Before merging any script change, verify:

    line 424
  23. Distinctive:

    When a script or command file emits bash commands for an agent to execute, do not embed for-loops, if-chains, or compound statements inline.

    line 484

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Contributor Guide

Progressive disclosure reference for contributing to orchestrator scripts. Self-contained -- read this document to understand coding conventions, testing patterns, and compliance requirements without cross-referencing other docs.

Audience: contributors

Overview

This guide covers everything needed to write, modify, and review scripts in the scripts/ directory. It targets both human contributors and AI agents executing tasks. The project is a spec-kit extension composed of markdown commands and bash helper scripts. All scripts live under scripts/ organized by concern:

  • state/ -- read-only state derivation (no side effects)
  • dispatch/ -- context assembly for task dispatch
  • verify/ -- mechanical verification with structured PASS/FAIL output
  • knowledge/ -- knowledge artifact generation (append-only where applicable)
  • lifecycle/ -- orchestration lifecycle management (creates/modifies files)
  • telemetry/ -- metrics recording and aggregation
  • lib/ -- shared libraries sourced by other scripts

Scripts live under scripts/ organized by concern. New scripts must be referenced by a command, a hook, or another script — dangling scripts are dead infrastructure (Principle VIII).


Coding Conventions

Bash 3.2 Compatibility

macOS ships Bash 3.2. All scripts MUST work on Bash 3.2. The following constructs are banned:

Banned constructBash versionAlternative
declare -A (associative arrays)4.0+Use parallel indexed arrays
${var,,} / ${var^^} (case transform)4.0+Use tr '[:upper:]' '[:lower:]'
`&` (pipe stderr)4.0+Use 2>&1 |
<(command) as redirect target4.0+ (reliable)Use temp file + while read < "$tmp"
readarray / mapfile4.0+Use while IFS= read -r loop
${!prefix@} (indirect expansion)4.0+Use eval carefully or restructure
coproc4.0+Not needed in this project

Parallel indexed arrays example (instead of associative arrays):

# WRONG (Bash 4+)
declare -A config
config[key]="value"

# RIGHT (Bash 3.2)
CONFIG_KEYS=(key1 key2 key3)
CONFIG_VALS=(val1 val2 val3)
# Iterate with index
i=0
for key in "${CONFIG_KEYS[@]}"; do
  val="${CONFIG_VALS[$i]}"
  i=$((i + 1))
done

Portable sed In-Place Editing

BSD sed (macOS) and GNU sed (Linux) handle -i differently. Never use sed -i or sed -i.bak directly. Use a portable helper:

sed_i() {
  if sed --version 2>/dev/null | grep -q GNU; then
    sed -i "$@"
  else
    sed -i '' "$@"
  fi
}

Better: source the shared helper if one exists in scripts/lib/.

Double-Sourcing Guards

Every library file in scripts/lib/ MUST have a double-sourcing guard at the top, immediately after the header comment. This prevents re-initialization when multiple scripts source the same library.

#!/usr/bin/env bash
# scripts/lib/example.sh -- Description of the library.
[ -n "${_EXAMPLE_SOURCED:-}" ] && return 0
_EXAMPLE_SOURCED=1

# ... library code ...

The guard variable MUST follow the pattern _LIBNAME_SOURCED where LIBNAME is derived from the filename (e.g., _ERRORS_SOURCED for errors.sh, _STALENESS_SOURCED for staleness.sh). See AP-003 in ../ANTIPATTERNS.md for the incident that led to this requirement.

Error and Event Emission

Engine-managed scripts MUST emit structured events and a final result. This is a constitution requirement (Principle II). A script that runs to completion without emitting a RESULT line is treated as a silent failure.

emit_result protocol (from scripts/lib/errors.sh):

Called exactly once per script at completion. Prints a single RESULT: line.

source "$LIB_DIR/errors.sh"

# Success
emit_result ok "" "phase advanced to P03"

# Failure with error taxonomy
emit_result error CONFIG "routing.yaml missing required field models.heavy.id"
emit_result error DISPATCH "all models in fallback chain exhausted"
emit_result error STATE "milestone directory does not exist"

The error taxonomy is a closed set of six kinds:

  • CONFIG -- configuration errors (missing fields, invalid YAML)
  • STATE -- state derivation errors (missing directories, invalid state)
  • DISPATCH -- dispatch pipeline errors (model exhaustion, payload issues)
  • VERIFY -- verification errors (check failures)
  • BUDGET -- budget exhaustion (dispatch count, duration)
  • IO -- file system I/O errors

emit_event protocol (from scripts/lib/events.sh):

Prints EVENT: lines throughout execution for observability. Each event includes a timestamp, run ID, and key-value pairs.

source "$LIB_DIR/events.sh"

emit_event SESSION_START run_id="$ORCH_RUN_ID" milestone=M004
emit_event TASK_START task=T02 phase=P02
emit_event GUARD_BLOCKED guard=payload_sanity reason="empty payload"

Canonical event types: SESSION_START, SESSION_END, PHASE_START, PHASE_COMPLETE, TASK_START, TASK_COMPLETE, DISPATCH_START, DISPATCH_FALLBACK, VERIFY_START, VERIFY_COMPLETE, GUARD_BLOCKED, GUARD_WARNING, SAFETY_WARNING, HOOK_START, HOOK_COMPLETE, HOOK_BLOCKED, HOOK_VIOLATION, CHECKPOINT_WRITE, CHECKPOINT_RESUME.

Unknown event types are emitted but also trigger a companion SAFETY_WARNING so the drift is observable.

Atomic Writes

Any script that writes to a file other agents or scripts might read MUST use the temp-file-then-rename pattern to avoid partial reads:

_tmp="$(mktemp)"
# Write content to temp file
printf '%s\n' "$content" > "$_tmp"
# Atomic move into place
mv "$_tmp" "$target_file"

This is especially important for:

  • Execution logs (JSONL append-only files)
  • Lock files
  • State files read by derive-phase.sh

For append operations (JSONL logs), use >> with a single printf call to minimize the window where the file is being written.

Script Structure

Every script follows this structure:

#!/usr/bin/env bash
# scripts/<dir>/<name>.sh -- One-line description
# Multi-line description of behavior, usage, arguments.
#
# Usage: <name>.sh <required-args> [optional-args]
#
# Bash 3.2 compatible (NFR-200).

set -euo pipefail

# --- Resolve paths ---
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# --- Source libraries ---
. "$SCRIPT_DIR/../lib/errors.sh"
. "$SCRIPT_DIR/../lib/events.sh"

# --- Argument validation ---
if [ $# -lt 1 ]; then
  echo "<name>.sh: missing required argument" >&2
  echo "Usage: <name>.sh <arg>" >&2
  exit 1
fi

# --- Functions ---
do_work() {
  local input="$1"
  # ...
}

# --- Main logic ---
do_work "$1"
emit_result ok "" "completed successfully"

Key conventions:

  • Always use #!/usr/bin/env bash as the shebang
  • Always set set -euo pipefail (strict mode)
  • Resolve SCRIPT_DIR and PROJECT_ROOT using cd + pwd (not readlink)
  • Source libraries relative to SCRIPT_DIR
  • Validate arguments before doing any work
  • Output structured lines to stdout (e.g., PASS:, RESULT:, RECORD:)
  • Output diagnostics and metrics to stderr
  • Exit 0 for success, exit 1 for failure

Structured Output Prefixes

Scripts communicate with the engine and other scripts through prefixed stdout lines. Common prefixes:

  • PASS: / FAIL: -- verification results (used by verify/ scripts)
  • RESULT:{...} -- final script result as JSON (emitted by emit_result)
  • EVENT:TYPE ... -- lifecycle events (emitted by emit_event)
  • RECORD:APPENDED -- log entry written
  • LOCK: -- lock file operations
  • CONSOLIDATE: -- consolidation operations

Never mix free-form text with structured output on stdout. Use stderr for human-readable diagnostics.


Testing Patterns

Test File Naming

Tests are organized as numbered suites in tests/:

tests/test-s01-structure.sh      -- Structural invariants
tests/test-s02-state-machine.sh  -- State machine contracts
tests/test-s03-design-artifacts.sh
tests/test-s04-core-commands.sh
tests/test-s05-autonomous-mode.sh
tests/test-s06-knowledge-lifecycle.sh
tests/test-s07-integration.sh
tests/test-s08-auto-safety.sh

Assertion Pattern

Every test suite uses the same pass/fail counting pattern:

#!/usr/bin/env bash
set -euo pipefail

PASS_COUNT=0
FAIL_COUNT=0
TOTAL=0

pass() {
  TOTAL=$((TOTAL + 1))
  PASS_COUNT=$((PASS_COUNT + 1))
  echo "PASS: $1"
}

fail() {
  TOTAL=$((TOTAL + 1))
  FAIL_COUNT=$((FAIL_COUNT + 1))
  echo "FAIL: $1"
}

# ... test sections ...

echo ""
echo "=== Results: $PASS_COUNT/$TOTAL passed ==="
[ "$FAIL_COUNT" -eq 0 ] && exit 0 || exit 1

Fixture Directories

Test fixtures live in tests/fixtures/. Each fixture simulates a specific disk state. For example, state derivation tests use fixture directories that represent each of the 10 canonical states:

tests/fixtures/state-pre-planning/
tests/fixtures/state-discussing/
tests/fixtures/state-executing/
...

Writing Tests

When adding new functionality:

  1. Add assertions to the appropriate existing test suite (match by concern).
  2. If the feature introduces a new concern, create a new suite as test-s##-<name>.sh with the next sequential number.
  3. Use fixture directories for any test that needs disk state.
  4. Test both the happy path AND error paths (missing arguments, invalid state).
  5. Prefer testing script output (structured prefixed lines) over testing internal state.

Running Tests

# Run a single suite
bash tests/test-s01-structure.sh

# Run all suites
for t in tests/test-s*.sh; do bash "$t"; done

Constitution v2.0 Compliance Checklist

When writing or reviewing scripts, verify compliance with each applicable principle. Not every principle applies to every script -- use judgment on which are relevant, but explicitly consider each one.

I. Context Minimization

Scripts receive only what they need and produce only what is needed. Check:

  • Does the script read files outside its stated scope?
  • Does the output contain unnecessary data that inflates downstream context?

II. Evidence Before Claims

Scripts emit structured evidence of their outcomes. Check:

  • Does the script call emit_result exactly once before exiting?
  • Does the script emit emit_event calls at significant lifecycle points?
  • Would a silent failure (no RESULT line) be detectable?

III. Design Before Code

New scripts require a design step in the task plan. Check:

  • Was the script's interface (arguments, output, exit codes) designed before implementation?
  • Is the script documented in its header comment?

IV. Plans Assume Zero Context

Script documentation must be self-contained. Check:

  • Does the header comment include usage, arguments, and output format?
  • Could an agent with no codebase knowledge use this script from its header?

V. Fresh Context Per Unit

Scripts must not carry state between invocations. Check:

  • Does the script rely on any in-memory state from a prior run?
  • Are all inputs passed as arguments or read from disk?

VI. State On Disk Is Truth

All state is derived from files, never cached in memory. Check:

  • Does the script write its results to disk before claiming completion?
  • Can derive-phase.sh detect the script's effect from disk alone?

VII. Knowledge Compounds

Scripts that generate knowledge must produce structured, discoverable output. Check:

  • Do knowledge-producing scripts write to the correct location in the hierarchy?
  • Are outputs in the expected format (YAML frontmatter, markdown body)?

VIII. No Dead Infrastructure

Every script must be reachable from a live code path. Check:

  • Is the script referenced by at least one command or other script?
  • If removing the script, does anything break?

IX. Reproducibility Over Convenience

Identical inputs must produce identical outputs. Check:

  • Does the script use $ORCH_STARTED_AT instead of inline date calls?
  • Are there any sources of non-determinism (random IDs without seed control)?

X. Templating Over Inference

Policy is declared in templates, mechanics in scripts. Check:

  • Does the script hard-code any policy that should be in a YAML config?
  • If behavior changes require editing this script instead of a template, the boundary is wrong.

XI. Single Source of Truth

No duplicated state or configuration. Check:

  • Does the script derive state (via derive-phase.sh) rather than caching it?
  • Does it read configuration from one authoritative location?

XII. Hook Isolation

Hook scripts operate in a sandbox. Check (if the script is a hook):

  • Does the hook modify engine state or write to orchestrator directories?
  • Does the hook respect the timeout ($ORCH_HOOK_TIMEOUT_SEC, default 30s)?
  • Does it only read from the $ORCH_HOOK_SNAPSHOT path?

XIII. Agent Instruction Schema

Dispatch instructions follow a declared schema. Check:

  • Do context-assembly scripts follow the recipe declared in context-recipe.yaml?
  • Would changing instruction format require a script change (bad) or a recipe change (good)?

PR Review Checklist

Before merging any script change, verify:

  • Bash 3.2 compatible -- no banned constructs (see table above)
  • Double-sourcing guard -- present on all library files in lib/
  • Portable sed -- uses sed_i helper, never raw sed -i
  • Structured output -- emit_result called exactly once; output uses correct prefixes
  • Error paths -- invalid arguments produce stderr message + exit 1
  • Header comment -- includes usage, arguments, output format, exit codes
  • Tests exist -- new functionality has assertions in the appropriate test suite
  • Tests pass -- bash tests/test-s##-<name>.sh exits 0
  • No dead infrastructure -- every new file is referenced from a live code path
  • Atomic writes -- files read by other scripts use temp+mv pattern
  • No inline date -- uses $ORCH_STARTED_AT for timestamps
  • Constitution check -- applicable principles reviewed (see checklist above)

Anti-Patterns

These are documented incidents from real development. Each references a specific antipattern ID from the project antipattern register (../ANTIPATTERNS.md).

AP-001: Platform-Specific Bash Syntax

Using done < <(command) (process substitution as redirect target). Works on Bash 4+ but fails on macOS Bash 3.2. Use the temp file pattern instead:

# WRONG
while IFS= read -r line; do
  process "$line"
done < <(find . -name "*.md")

# RIGHT
_tmp="$(mktemp)"
find . -name "*.md" > "$_tmp"
while IFS= read -r line; do
  process "$line"
done < "$_tmp"
rm -f "$_tmp"

AP-002: Non-Portable sed -i

Using sed -i.bak creates junk .bak files on macOS. Always use a portable sed_i helper function. See the Coding Conventions section above.

AP-003: Missing Double-Sourcing Guards

Library files without [ -n "${_LIBNAME_SOURCED:-}" ] && return 0 cause re-initialization when sourced transitively by multiple scripts. Every library in scripts/lib/ MUST have this guard.

Inline Loops in Emitted Commands

When a script or command file emits bash commands for an agent to execute, do not embed for-loops, if-chains, or compound statements inline. The harness AST parser blocks these patterns. Instead, write a wrapper script and invoke it.

Silent Failures

A script that exits 0 without emitting a RESULT: line is a silent failure. The engine cannot distinguish "succeeded" from "forgot to report." Always call emit_result before exiting, even on error paths.

Hardcoded Policy in Scripts

Embedding tier thresholds, model IDs, or routing rules directly in script logic violates Principle X. These belong in YAML configuration files (routing.yaml, context-recipe.yaml, hooks.yaml). Scripts implement mechanics; templates declare policy.

Cached State Variables

Reading state once and caching it in a variable for later use violates Principle XI. Always re-derive state from disk via derive-phase.sh when you need the current state. Stale cached state is the source of subtle bugs.


Cross-References

  • ../ANTIPATTERNS.md -- full antipattern register with evidence
  • ../.orchestrator/memory/constitution.md -- constitution v2.0 (13 principles)
  • ../references/constitution-walkthrough.md -- principle-by-principle walkthrough with codebase examples
  • ../tests/ -- test suites and fixtures
  • ../references/architecture.md -- system architecture, engine pipeline, subsystem map
  • ../references/file-formats.md -- structured output format definitions
  • lib/errors.sh -- emit_result implementation and error taxonomy
  • lib/events.sh -- emit_event implementation and event type registry

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.