nasde-benchmark-creator — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited nasde-benchmark-creator (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Create and configure coding agent benchmarks for evaluation with nasde. A benchmark is a set of coding tasks that AI agents solve inside isolated Docker containers, scored both by functional tests (pass/fail) and by an LLM-as-a-Judge architecture assessment.
Benchmark scripts execute inside Linux sandboxes (Docker, Daytona). If tests/test.sh, solution/solve.sh, or environment/Dockerfile are checked out with CRLF line endings (the Windows git default when core.autocrlf=true and there is no .gitattributes), every trial fails immediately with:
bash: line 1: /tests/test.sh: cannot execute: required file not found…because the kernel reads the shebang as #!/bin/bash\r and tries to execute a non-existent /bin/bash\r. The agent finishes its work, but the verifier never runs and Harbor reports RewardFileNotFoundError.
Mitigation (always do this for a new benchmark — `nasde init` does it for you, but verify):
.gitattributes file enforcing LF for shell scripts and Dockerfiles. The minimum content: * text=auto eol=lf
*.sh text eol=lf
*.bash text eol=lf
Dockerfile text eol=lf
*.dockerfile text eol=lf
docker-compose.yaml text eol=lf
docker-compose.yml text eol=lf
*.ps1 text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlfnasde init writes this automatically. If you are adding a benchmark to an existing repo without .gitattributes, create one before adding any task.
.sh or Dockerfile content programmatically on Windows, write with explicit LF — not path.write_text(content) (which translates \n→\r\n on Windows), but path.write_text(content, encoding="utf-8", newline="") or open the file in binary mode. git add --renormalize .
git commit -m "normalize line endings"to fix any files that landed before .gitattributes was in place.
file tasks/<task>/tests/test.sh
# MUST say "with LF line terminators" or omit line-terminator info entirely.
# If it says "with CRLF line terminators" — fix it (`sed -i 's/\r$//' file`).This applies equally when you're adding tasks to a benchmark someone else created — if their repo has no .gitattributes and you're on Windows, your contribution will silently break for them on Linux CI and vice versa.
Before creating files, clarify with the user:
For a new benchmark, run:
nasde init my-benchmark --name my-benchmarkThis creates the base structure. Then customize the generated files.
For adding tasks to an existing benchmark, skip to Step 4.
Edit assessment_dimensions.json. Each benchmark has its OWN dimensions — design them for what matters in this benchmark's domain.
Examples by domain:
code_clarity, test_preservation, api_compatibility, performance_impacterror_handling, api_usage_correctness, test_coverage, documentationvulnerability_detection, fix_correctness, regression_safety, explanation_qualitydomain_modeling, architecture_compliance, extensibility, test_qualityRules:
max_score (any positive integer). Scales are independent — a coarse pass/fail-ish dimension can be 0–3 while a richly graded one can be 0–50 in the same rubric. There is no requirement for the total to sum to 100. normalized_score is computed automatically from the actual sum of max_score values. See ADR-008.name, title, max_score, descriptionEach task lives in tasks/<task-name>/ and needs these files:
Single config file per task, shared with Harbor. nasde-specific fields live under [nasde.*].
version = "1.0"
[task]
name = "<benchmark-name>/<task-name>" # Harbor requires org/name format
description = "Brief description"
[metadata]
difficulty = "intermediate"
language = "C#"
framework = ".NET 8"
domain = "E-Commerce"
[agent]
timeout_sec = 1800 # Primary agent timeout. Rule of thumb: estimated_time_minutes × 60.
[environment]
memory_mb = 4096 # Container memory limit. Claude Code needs 4096+, default 2048 is too low.
[verifier]
timeout_sec = 300 # Timeout for tests/test.sh.
[nasde.source] # Only needed when task has no environment/Dockerfile (nasde auto-generates one).
git = "https://github.com/org/repo.git"
ref = "main"Timeout priority: --timeout CLI flag > task.toml [agent] timeout_sec > Harbor default. Timeouts are per-task — there is no project-wide default in nasde.toml.
Agent-facing task description. Structure it as:
# Task: <Name>
## Context
Working environment, codebase location (/app), technology stack.
## Requirement
What the agent must implement/fix/change. Concrete examples with inputs and expected outputs.
## Scope
What's in scope, what's not.
## Quality Expectations
Architecture and code quality expectations.
## Success Criteria
Numbered list matching what test.sh verifies.
## Constraints
What the agent must NOT do (e.g., don't modify existing tests).Reminder for Windows authors: the Dockerfile and any helper scripts itCOPYs in must have LF line endings — Docker tolerates CRLF in some commands but not inRUNshell snippets, and any shell script copied with CRLF will hit the same shebang failure astest.sh.
FROM <base-image>
RUN apt-get update && apt-get install -y git curl wget ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN git clone <repository-url> .
# Pre-install dependencies so the agent doesn't waste time
RUN <dependency-install-command>
# Verify the environment works
RUN <build-or-compile-command>
CMD ["/bin/bash"]The Dockerfile MUST be self-contained — the agent starts working immediately.
Reminder for Windows authors: this file MUST be saved with LF line endings. See "Critical: line endings on Windows" at the top of this skill. CRLF here = bash: required file not found and a wasted trial.#!/bin/bash
cd /app
echo "Step 1: Verifying build..."
if <build-command>; then
echo "✓ Build succeeded"
else
echo "✗ Build failed"
echo 0 > /logs/verifier/reward.txt
exit 1
fi
echo "Step 2: Running tests..."
if <test-command>; then
echo "✓ Tests pass"
else
echo "✗ Tests failed"
echo 0 > /logs/verifier/reward.txt
exit 1
fi
echo "EVALUATION PASSED ✓"
echo 1 > /logs/verifier/reward.txt
exit 0Rules:
echo 0 > /logs/verifier/reward.txt + exit 1echo 1 > /logs/verifier/reward.txt + exit 0Per-task rubric. Structure:
# Assessment Criteria: <Task Name>
Evaluate across N dimensions. Each dimension uses its own scale (0–`max_score`),
defined in `assessment_dimensions.json`. The ladder below shows what each score
means for one specific dimension — repeat for each dimension.
## 1. <Dimension Name> (0–<max_score>)
| Score | Criteria |
|-------|----------|
| 0 | <worst case> |
| <middle> | <middle case> |
| <max_score> | <best case> |
**Key checks:**
- Specific things to look forPick a max_score that matches the granularity you can actually distinguish. A coarse pass/fail-ish dimension might be 0–3; a richly graded one might be 0–50. Choose the resolution per dimension, independently.
Reference solution for verifying test.sh works. Not executed by Harbor.
Each variant is a directory under variants/<variant-name>/ with a required variant.toml declaring the agent type.
agent = "claude" # or "codex" or "gemini"For Codex variants, always set the model explicitly to avoid inheriting the Claude model from nasde.toml:
agent = "codex"
model = "gpt-5.3-codex" # Required for Codex — use an OpenAI model IDCodex models (recommended first, as of 2026-03):
gpt-5.4 — flagship frontier model, best overall for professional workgpt-5.4-mini — fast, efficient mini model for responsive coding and subagentsgpt-5.3-codex — industry-leading coding model for complex software engineeringgpt-5.3-codex-spark — near-instant real-time coding iteration (ChatGPT Pro only)gpt-5.2-codex, gpt-5.1-codex, gpt-5-codex, gpt-5-codex-miniWithout model in variant.toml, Codex inherits nasde.toml's default (e.g. claude-sonnet-4-6), which silently produces garbage results.
For Gemini CLI variants, always set the model with the google/ prefix:
agent = "gemini"
model = "google/gemini-3-flash-preview" # Required format: google/<model-name>Gemini models (recommended first, as of 2026-03):
google/gemini-3.1-pro-preview — advanced thinking model, best for deep reasoninggoogle/gemini-3-flash-preview — best quality/speed ratio, daily coding tasksgoogle/gemini-3.1-flash-lite-preview — fastest, simple and repetitive tasksIf a variant only makes sense for certain tasks — e.g. a skill whose examples are tuned to one repo's conventions — declare a tasks list. It restricts the variant to those tasks so --all-variants never runs it against the wrong codebase:
agent = "claude"
model = "claude-sonnet-4-6"
tasks = ["my-benchmark/task-a"] # only runs against these tasksOmit tasks for a general-purpose variant (the default — runs against all tasks). The scope wins even over an explicit --tasks filter.
variants/vanilla/
variant.toml # agent = "claude"
CLAUDE.md # Instructions (injected to /app/CLAUDE.md)
skills/ # Optional: skill snapshots (injected to /app/.claude/skills/)variants/codex-baseline/
variant.toml # agent = "codex"
AGENTS.md # Instructions (injected to /app/AGENTS.md)
agents_skills/ # Optional: skill snapshots (native injection -> $HOME/.agents/skills/)
my-skill/
SKILL.md # MUST start with --- YAML frontmatter (name + description)variants/gemini-baseline/
variant.toml # agent = "gemini"
GEMINI.md # Instructions (injected to /app/GEMINI.md)
gemini_skills/ # Optional: skill snapshots (native injection -> ~/.gemini/skills/)
my-skill/
SKILL.md # MUST start with --- YAML frontmatter (name + description)Codex/Gemini skills are registered natively (Harborconfig.agent.skills), not viasandbox_files— these CLIs auto-discover skills only from a HOME-scoped dir, never from a/appcwd dir. This applies to all ways a skill is supplied to a Codex/Gemini variant: theagents_skills//gemini_skills/snapshot above, a[[skill]]by-reference entry invariant.toml, and a[nasde.plugin]'s ownskills/. EachSKILL.mdmust start with a `---` YAML frontmatter line: Codex's loader rejects a file that opens with anything else (missing YAML frontmatter delimited by ---) and silently skips the skill. Put any provenance comment below the closing---. See ADR-012.
If no harbor_config.json exists, nasde auto-generates one from variant.toml. To customize (e.g., add MCP servers), create it explicitly:
{
"agents": [
{
"import_path": "nasde_toolkit.agents.configurable_claude:ConfigurableClaude",
"name": "<variant-name>",
"kwargs": {
"sandbox_files": {
"/app/CLAUDE.md": "/absolute/path/to/variants/<variant>/CLAUDE.md",
"/logs/agent/sessions/.claude.json": "/absolute/path/to/variants/<variant>/claude_config.json"
}
}
}
]
}Critical: "name" field is REQUIRED — without it, Opik tagging breaks.
Design variants to test specific hypotheses:
Every benchmark needs at least one variant (typically vanilla or baseline).
Before running with a real agent:
docker build -t benchmark-test -f tasks/<task>/environment/Dockerfile . docker run --rm -it benchmark-test bash
# Inside container:
bash /path/to/solution/solve.sh
bash /path/to/tests/test.sh
cat /logs/verifier/reward.txt # Should be 1 nasde run --variant vanilla --tasks <task-name> --without-eval -C . find tasks -name '*.sh' -exec sh -c 'file "$1" | grep -q CRLF && echo "BAD: $1"' _ {} \;
find tasks -name 'Dockerfile' -exec sh -c 'file "$1" | grep -q CRLF && echo "BAD: $1"' _ {} \;
# Both should print nothing.If anything prints, fix with sed -i 's/\r$//' <file> and re-commit.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.