349 lines
9.0 KiB
Bash
Executable File
349 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
PROJECT_ROOT="${1:-$ROOT_DIR/example_projects}"
|
|
SPEC_ROOT="${2:-$ROOT_DIR/datasets/example_run_specs}"
|
|
VARIANTS="${VARIANTS:-12}"
|
|
|
|
mkdir -p "$PROJECT_ROOT" "$SPEC_ROOT"
|
|
|
|
cat > "$PROJECT_ROOT/README.md" <<'DOC'
|
|
# Example Projects Corpus
|
|
|
|
Curated multi-stack fixtures for MCP training data generation.
|
|
|
|
- Designed for tool-use diversity, not production deployment.
|
|
- Each project has a small, realistic file tree and a validator command.
|
|
- Matching run specs live in `datasets/example_run_specs`.
|
|
DOC
|
|
|
|
create_project() {
|
|
local name="$1"
|
|
local stack="$2"
|
|
local validator="$3"
|
|
local files="$4"
|
|
local failures="$5"
|
|
|
|
local dir="$PROJECT_ROOT/$name"
|
|
mkdir -p "$dir"
|
|
cat > "$dir/PROJECT_META.md" <<META
|
|
# ${name}
|
|
|
|
- stack: ${stack}
|
|
- validator: ${validator}
|
|
- common_failure_modes: ${failures}
|
|
|
|
## Scope
|
|
|
|
Small representative project for training data generation.
|
|
META
|
|
|
|
case "$name" in
|
|
py_cli_todo)
|
|
mkdir -p "$dir/src" "$dir/tests"
|
|
cat > "$dir/src/todo.py" <<'PY'
|
|
def add_task(tasks, title):
|
|
title = title.strip()
|
|
if not title:
|
|
raise ValueError("title is required")
|
|
tasks.append({"title": title, "done": False})
|
|
return tasks
|
|
|
|
|
|
def complete_task(tasks, index):
|
|
tasks[index]["done"] = True
|
|
return tasks
|
|
PY
|
|
cat > "$dir/tests/test_todo.py" <<'PY'
|
|
from src.todo import add_task, complete_task
|
|
|
|
|
|
def test_add_and_complete():
|
|
tasks = []
|
|
add_task(tasks, "pay bills")
|
|
complete_task(tasks, 0)
|
|
assert tasks[0]["done"] is True
|
|
PY
|
|
;;
|
|
node_api_gateway)
|
|
mkdir -p "$dir/src" "$dir/test"
|
|
cat > "$dir/src/server.js" <<'JS'
|
|
const http = require("http");
|
|
|
|
function health() {
|
|
return { ok: true, service: "gateway" };
|
|
}
|
|
|
|
const server = http.createServer((_req, res) => {
|
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
res.end(JSON.stringify(health()));
|
|
});
|
|
|
|
if (require.main === module) {
|
|
server.listen(8080);
|
|
}
|
|
|
|
module.exports = { health };
|
|
JS
|
|
cat > "$dir/test/server.test.js" <<'JS'
|
|
const assert = require("assert");
|
|
const { health } = require("../src/server");
|
|
assert.equal(health().ok, true);
|
|
JS
|
|
;;
|
|
go_worker_pool)
|
|
mkdir -p "$dir/cmd/worker" "$dir/internal/queue"
|
|
cat > "$dir/internal/queue/queue.go" <<'GO'
|
|
package queue
|
|
|
|
type Job struct {
|
|
ID string
|
|
}
|
|
|
|
func Enqueue(q []Job, j Job) []Job {
|
|
return append(q, j)
|
|
}
|
|
GO
|
|
cat > "$dir/cmd/worker/main.go" <<'GO'
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"example/internal/queue"
|
|
)
|
|
|
|
func main() {
|
|
jobs := []queue.Job{}
|
|
jobs = queue.Enqueue(jobs, queue.Job{ID: "job-1"})
|
|
fmt.Println(len(jobs))
|
|
}
|
|
GO
|
|
;;
|
|
bash_log_shipper)
|
|
mkdir -p "$dir/bin" "$dir/etc"
|
|
cat > "$dir/bin/ship_logs.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
SRC="${1:-/var/log/app.log}"
|
|
DST="${2:-/tmp/app.log.gz}"
|
|
gzip -c "$SRC" > "$DST"
|
|
echo "$DST"
|
|
SH
|
|
chmod +x "$dir/bin/ship_logs.sh"
|
|
cat > "$dir/etc/shipper.conf" <<'CONF'
|
|
retention_days=7
|
|
max_size_mb=50
|
|
CONF
|
|
;;
|
|
cpp_cache_indexer)
|
|
mkdir -p "$dir/src" "$dir/include"
|
|
cat > "$dir/include/indexer.h" <<'H'
|
|
#pragma once
|
|
#include <string>
|
|
#include <vector>
|
|
std::vector<std::string> tokenize(const std::string& line);
|
|
H
|
|
cat > "$dir/src/indexer.cpp" <<'CPP'
|
|
#include "indexer.h"
|
|
#include <sstream>
|
|
|
|
std::vector<std::string> tokenize(const std::string& line) {
|
|
std::istringstream in(line);
|
|
std::vector<std::string> out;
|
|
std::string tok;
|
|
while (in >> tok) out.push_back(tok);
|
|
return out;
|
|
}
|
|
CPP
|
|
;;
|
|
rust_metrics_agent)
|
|
mkdir -p "$dir/src"
|
|
cat > "$dir/src/main.rs" <<'RS'
|
|
fn parse_metric(line: &str) -> Option<(&str, i64)> {
|
|
let parts: Vec<&str> = line.split('=').collect();
|
|
if parts.len() != 2 {
|
|
return None;
|
|
}
|
|
let value = parts[1].parse::<i64>().ok()?;
|
|
Some((parts[0], value))
|
|
}
|
|
|
|
fn main() {
|
|
let _ = parse_metric("requests=10");
|
|
}
|
|
RS
|
|
;;
|
|
java_order_service)
|
|
mkdir -p "$dir/src/main/java/app" "$dir/src/test/java/app"
|
|
cat > "$dir/src/main/java/app/Order.java" <<'JAVA'
|
|
package app;
|
|
public record Order(String id, int quantity) {}
|
|
JAVA
|
|
cat > "$dir/src/main/java/app/OrderValidator.java" <<'JAVA'
|
|
package app;
|
|
public class OrderValidator {
|
|
public static boolean valid(Order order) {
|
|
return order != null && order.quantity() > 0;
|
|
}
|
|
}
|
|
JAVA
|
|
;;
|
|
terraform_vpc_stack)
|
|
mkdir -p "$dir"
|
|
cat > "$dir/main.tf" <<'TF'
|
|
terraform {
|
|
required_version = ">= 1.5.0"
|
|
}
|
|
|
|
resource "null_resource" "example" {}
|
|
TF
|
|
;;
|
|
react_admin_panel)
|
|
mkdir -p "$dir/src/components"
|
|
cat > "$dir/src/App.jsx" <<'JSX'
|
|
import React from "react";
|
|
export default function App() {
|
|
return <main><h1>Admin Panel</h1></main>;
|
|
}
|
|
JSX
|
|
cat > "$dir/src/components/UserTable.jsx" <<'JSX'
|
|
import React from "react";
|
|
export function UserTable() {
|
|
return <table><tbody></tbody></table>;
|
|
}
|
|
JSX
|
|
;;
|
|
sql_etl_pipeline)
|
|
mkdir -p "$dir/sql"
|
|
cat > "$dir/sql/001_create_tables.sql" <<'SQL'
|
|
CREATE TABLE IF NOT EXISTS events (
|
|
id TEXT PRIMARY KEY,
|
|
ts TEXT NOT NULL,
|
|
kind TEXT NOT NULL
|
|
);
|
|
SQL
|
|
cat > "$dir/sql/010_transform.sql" <<'SQL'
|
|
INSERT INTO events (id, ts, kind)
|
|
SELECT id, ts, kind FROM staging_events;
|
|
SQL
|
|
;;
|
|
esac
|
|
|
|
cat > "$dir/TASKS.md" <<TASKS
|
|
# Task Seeds: ${name}
|
|
|
|
${files}
|
|
|
|
## Validator
|
|
|
|
\`${validator}\`
|
|
TASKS
|
|
}
|
|
|
|
create_project "py_cli_todo" "python" "pytest -q" "src/todo.py, tests/test_todo.py" "arg validation, off-by-one index, missing tests"
|
|
create_project "node_api_gateway" "node" "node test/server.test.js" "src/server.js, test/server.test.js" "route mismatch, status/body drift, invalid json"
|
|
create_project "go_worker_pool" "go" "go test ./..." "cmd/worker/main.go, internal/queue/queue.go" "dependency order, wrong package path, nil handling"
|
|
create_project "bash_log_shipper" "bash" "bash -n bin/ship_logs.sh" "bin/ship_logs.sh, etc/shipper.conf" "path misuse, quoting, retention config mismatch"
|
|
create_project "cpp_cache_indexer" "cpp" "cmake --build . && ctest" "include/indexer.h, src/indexer.cpp" "header/impl mismatch, parser edge cases"
|
|
create_project "rust_metrics_agent" "rust" "cargo test" "src/main.rs" "parse failures, error propagation, type mismatch"
|
|
create_project "java_order_service" "java" "mvn -q test" "src/main/java/app/*.java" "null checks, quantity rules, test gaps"
|
|
create_project "terraform_vpc_stack" "terraform" "terraform validate" "main.tf" "resource drift, provider/schema mismatch"
|
|
create_project "react_admin_panel" "react" "npm test -- --watch=false" "src/App.jsx, src/components/UserTable.jsx" "component state misuse, prop mismatch"
|
|
create_project "sql_etl_pipeline" "sql" "sqllogictest tests/*.slt" "sql/001_create_tables.sql, sql/010_transform.sql" "schema drift, non-idempotent transforms"
|
|
|
|
cat > "$SPEC_ROOT/README.md" <<'DOC'
|
|
# Example Project Run Specs
|
|
|
|
Cross-project run specs for broad training distribution.
|
|
|
|
- `index.jsonl` stores metadata for each spec.
|
|
- `runspec_*.md` are directly consumable by `run_spec_batch_with_capture.sh`.
|
|
DOC
|
|
|
|
INDEX_FILE="$SPEC_ROOT/index.jsonl"
|
|
: > "$INDEX_FILE"
|
|
|
|
families=(
|
|
wrong_tool_selection
|
|
missing_required_args
|
|
dependency_order_violation
|
|
premature_finalize
|
|
overfetch_context
|
|
)
|
|
|
|
projects=(
|
|
py_cli_todo
|
|
node_api_gateway
|
|
go_worker_pool
|
|
bash_log_shipper
|
|
cpp_cache_indexer
|
|
rust_metrics_agent
|
|
java_order_service
|
|
terraform_vpc_stack
|
|
react_admin_panel
|
|
sql_etl_pipeline
|
|
)
|
|
|
|
for project in "${projects[@]}"; do
|
|
for family in "${families[@]}"; do
|
|
for i in $(seq 1 "$VARIANTS"); do
|
|
id="${project}_${family}_$(printf '%02d' "$i")"
|
|
file="$SPEC_ROOT/runspec_${id}.md"
|
|
|
|
cat > "$file" <<SPEC
|
|
# Run Spec: ${id}
|
|
|
|
## Context
|
|
|
|
project = ${project}
|
|
family = ${family}
|
|
variant = ${i}
|
|
project_root = example_projects/${project}
|
|
|
|
## Objective
|
|
|
|
Solve a bounded engineering task in ${project} while producing deterministic, stage-labeled traces.
|
|
|
|
## Target Behavior
|
|
|
|
- Prefer minimal-context file reads in the project root.
|
|
- Use phase-correct flow: intake -> generate -> queue -> validate.
|
|
- Stop once acceptance checks are met.
|
|
|
|
## Anti-Pattern To Detect
|
|
|
|
- ${family}
|
|
- Non-terminal tool loops.
|
|
- Schema-invalid tool args.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- Uses project-local files under \`example_projects/${project}\`.
|
|
- Includes at least one concrete validator/test command in task items.
|
|
- No malformed payloads in MCP stages.
|
|
- Explicit finalization condition and bounded retries.
|
|
|
|
## Data Capture Tags
|
|
|
|
- corpus: example_projects
|
|
- project: ${project}
|
|
- family: ${family}
|
|
- variant: ${i}
|
|
SPEC
|
|
|
|
jq -nc \
|
|
--arg id "$id" \
|
|
--arg file "$(basename "$file")" \
|
|
--arg project "$project" \
|
|
--arg family "$family" \
|
|
--argjson variant "$i" \
|
|
'{id:$id,file:$file,project:$project,family:$family,variant:$variant}' >> "$INDEX_FILE"
|
|
done
|
|
done
|
|
done
|
|
|
|
echo "Example projects generated: $(find "$PROJECT_ROOT" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')"
|
|
echo "Example run specs generated: $(find "$SPEC_ROOT" -maxdepth 1 -type f -name 'runspec_*.md' | wc -l | tr -d ' ')"
|
|
echo "Spec index: $INDEX_FILE"
|