This file is the primary operating guide for Codex in this repository, both in interactive sessions and when Codex runs headlessly as a NEEDLE worker. Inspect the current repository state and the relevant source before relying on older examples or planning documents.
NEEDLE (Navigates Every Enqueued Deliverable, Logs Effort) is a Rust worker binary. It selects and claims work through the repository's configured bead backend, dispatches a coding agent, records telemetry, and routes every outcome through an explicit state machine.
The primary backend is bead-rs (bead); bead-forge (bf) remains supported
for explicitly bound legacy workspaces. Check .needle.yaml and the .beads/
layout before running either CLI. Do not infer the backend from which binaries
happen to be installed, and never use one backend's repair or import command on
the other backend's database. br is retired and must not be introduced in
source, prompts, scripts, tests, or documentation.
- Inspect
git status --shortbefore editing. Preserve unrelated user and worker changes in a dirty worktree. - Multiple NEEDLE workers may share the same repository. Re-check files before modifying them and avoid broad rewrites that can overwrite concurrent work.
- Do not create per-worker git worktrees to isolate concurrent workers —
a shared checkout is the intended model, not a bug to route around.
Worktrees add disk and build-cache duplication without addressing the
actual failure mode, which happens at the bead level: the same bead getting
claimed and worked twice (confirmed 2026-08-09, commitgraph
cg-l0v0kc— two byte-identical commits at the same second from two concurrent workers). The supported backends provide atomic claim operations, so avoid this by decomposing and dependency-ordering beads so the same unit of work is never independently claimable twice — see the target repo's ownAGENTS.mdfor repo-specific guidance. - Do not use destructive Git operations (
reset --hard, forced checkout, forced push) to resolve unrelated changes. - Forgejo (
git.ardenone.com) is the authoritative remote. GitHub is a mirror. Push only to the configuredorigin, and never use--forceor--force-with-lease. - Keep commands suitable for unattended execution: avoid prompts, pagers, and commands that require an interactive terminal.
- Treat
docs/plan/plan.mdas architecture and design history. Verify current behavior in source and against the installed CLI before depending on old command examples.
The declared MSRV is Rust 1.75 (Cargo.toml). Do not add language features or
dependencies that require a newer compiler without intentionally updating the
MSRV and associated toolchain and CI configuration.
- Prefer
Resultand?for fallible operations. Do not hide operational failures withunwrap()orexpect(). Reserve those calls for tests or a clearly documented invariant/unrecoverable initialization condition. - Fallible public operations should return
Result; infallible constructors, accessors, and pure transformations may return ordinary values. - Match state and outcome enums exhaustively. Avoid catch-all
_arms where a new variant should force an explicit decision. - Emit telemetry for every state transition and terminal outcome.
- Preserve error context at process, filesystem, database, and parsing boundaries.
- Unit tests belong in
#[cfg(test)]modules near their implementation. - Integration and end-to-end tests belong under
tests/. - Use
#[tokio::test]for asynchronous tests; do not introducetokio_test::block_on. - Prefer testing public behavior over implementation details.
Any test that spawns the compiled needle binary as a real subprocess (for
example, Command::new(CARGO_BIN_EXE_needle)) must isolate both HOME and the
Explore strand's scan root.
Explore is enabled by default and otherwise scans under the real home directory. An unisolated test can discover and mutate production bead stores. This previously created hundreds of phantom beads across real repositories.
At minimum, give the subprocess a temporary home:
cmd.env("HOME", temp_dir.path());Prefer also configuring workspace_root to a temporary fixture directory, or
disable Explore when the behavior under test does not require it. Never point a
test worker at /home/coding or another directory containing real projects.
- Run
cargo fmt --checkfor Rust changes. - Run targeted tests for the modules or behavior changed.
- Run
cargo clippy --all-targets -- -D warningswhen practical. Existing unrelated warnings must be reported distinctly rather than silently fixed as part of an unrelated task. - On this host, the
cargowrapper may offload a clean repository to iad-ci and use a resource-limited local fallback for a dirty repository. Do not assume a command ran remotely; report what actually ran and its result. - The authoritative full verification is the
needle-ciworkflow on iad-ci after a push tomain. Do not claim full-suite success from targeted tests. - If required CI fails, record the failure on the bead, fix it, and do not close the bead as successfully completed.
Each bead supplies its own deliverables and acceptance criteria. Complete and verify the requested repository work before closing it.
This repository is bead-rs-backed. Use:
bead close BEAD_ID --reason "Summary of what was done"The close flag is --reason, not --body. When no code change is appropriate,
record the reason with a bead comment or another supported public bead
operation rather than creating an empty commit.
SQLite (.beads/beads.db) is the live store. .beads/checkpoint/ is the
git-tracked durable checkpoint, and mutations do not flush it implicitly.
Flush explicitly before committing bead state:
bead sync flush-only
./scripts/checkpoint-publish.sh stageThe staging helper resolves and verifies the generation objects named by both
checkpoint pointers, stages those objects atomically with the pointers, and
removes superseded objects/gen-*.jsonl files from the working tree. Install
the tracked pre-commit check once per clone with
./scripts/install-git-hooks.sh. Do not use git add -A for checkpoint
publication.
bead doctor is read-only by default. If a database is missing, wrong-schema,
or corrupt, confirm the backend first and restore an empty native store from
the committed forensic checkpoint with the documented bead init plus bead sync import-only --restore-into-empty procedure. Never delete or rebuild a
bead database without explicitly accounting for unflushed work.
Use the bead identifier when the work is bead-driven:
feat(needle-XYZ): short description
fix(needle-XYZ): short description
test(needle-XYZ): short description
Do not mix unrelated cleanup into the task commit. State which checks were run and any remaining limitations in the final handoff or bead notes.
NEEDLE emits OpenTelemetry-compatible telemetry via OTLP. When modifying code that interacts with the telemetry system, maintain these semantic conventions:
The agent.dispatch span uses OpenTelemetry's gen_ai.* semantic conventions:
| Attribute | Description |
|---|---|
gen_ai.system |
AI provider (e.g., anthropic, openai) |
gen_ai.request.model |
Model identifier (e.g., claude-sonnet-4-6) |
gen_ai.usage.input_tokens |
Input token count |
gen_ai.usage.output_tokens |
Output token count |
These attributes enable NEEDLE telemetry to integrate with GenAI-focused dashboards (Grafana GenAI app, Langfuse, Honeycomb AI, etc.).
Every exported signal carries these resource attributes:
| Attribute | Value |
|---|---|
service.name |
"needle" |
service.version |
Build version from CARGO_PKG_VERSION |
service.instance.id |
Worker ID (e.g., needle-claude-anthropic-sonnet-alpha) |
needle.session_id |
Per-boot random session ID |
host.name |
Hostname |
process.pid |
Worker PID |
For the complete semantic mapping of NEEDLE events to OpenTelemetry signals, see docs/plan/plan.md.