@blundergoat/goat-flow
Version:
AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.
93 lines • 8.87 kB
JavaScript
import { QUALITY_REPORT_KIND } from "../quality/schema.js";
import { getPackageVersion } from "../paths.js";
import { inferQualityScope, jsonString, qualityModeLabel, shellSingleQuote, toShellProjectPath, } from "./compose-quality-common.js";
export function appendAgentReportContract(lines, input) {
lines.push("### Write the JSON report");
lines.push("");
lines.push("Do **not** emit the JSON as a fenced block in your reply. Write it as a file to `.goat-flow/logs/quality/` - that path is gitignored and expected. No tracked-file writes or implementation edits are permitted.");
lines.push("");
lines.push("**CRITICAL:** After writing the file, verify it was saved by running `ls -la .goat-flow/logs/quality/` and confirming the file appears with non-zero size. If missing, retry the write. A quality report that exists only in conversation history is invisible to `goat-flow quality history` and `goat-flow quality diff`.");
lines.push("");
lines.push("**Filename format:** `YYYY-MM-DD-HHMM-<agent>-<rand5>.json`");
lines.push("");
lines.push("Where:");
lines.push("- `YYYY-MM-DD-HHMM` is the current local date and 24-hour time (e.g. `2026-04-19-1430`)");
lines.push(`- \`<agent>\` is the literal string \`${input.agent}\``);
lines.push("- `<rand5>` is 5 lowercase alphanumeric characters (a-z, 0-9) that you generate fresh to avoid collisions with other parallel runs");
lines.push("");
lines.push("**Derive the date/time/random parts via your shell** (so the filename reflects when the report was actually written, not when this prompt was generated). On Linux/macOS:");
lines.push("");
lines.push("```bash");
lines.push('STAMP="$(date +"%Y-%m-%d-%H%M")" # e.g. 2026-04-19-1430');
lines.push("RAND=\"$(LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 5)\"");
lines.push(`QUALITY_DIR=${shellSingleQuote(toShellProjectPath(input.projectPath, ".goat-flow/logs/quality"))}`);
lines.push(`FILE="\${QUALITY_DIR}/\${STAMP}-${input.agent}-\${RAND}.json"`);
lines.push('mkdir -p "$QUALITY_DIR"');
lines.push("# (then write the JSON below to $FILE)");
lines.push("```");
lines.push("");
lines.push("**JSON body shape:**");
lines.push("");
lines.push("```json");
lines.push("{");
lines.push(` "report_kind": ${jsonString(QUALITY_REPORT_KIND)},`);
lines.push(` "goat_flow_version": ${jsonString(getPackageVersion())},`);
lines.push(` "agent": ${jsonString(input.agent)},`);
lines.push(` "project_path": ${jsonString(input.projectPath)},`);
lines.push(` "run_date": ${jsonString(input.runDate)},`);
lines.push(` "audit_status": ${jsonString(input.auditStatus)},`);
lines.push(` "scope": ${jsonString(inferQualityScope(input.projectPath))},`);
lines.push(` "rubric_version": ${jsonString(getPackageVersion())},`);
lines.push(` "quality_mode": ${jsonString(input.qualityMode)},`);
lines.push(` "prior_report_id": ${input.priorReport ? jsonString(input.priorReport.id) : "null"},`);
lines.push(' "scores": {');
lines.push(' "setup": { "total": 0, "accuracy": 0, "relevance": 0, "completeness": 0, "friction": 0 },');
lines.push(' "system": { "total": 0, "usefulness": 0, "signal_to_noise": 0, "adaptability": 0, "learnability": 0 }');
lines.push(" },");
lines.push(' "findings": [');
lines.push(" {");
lines.push(' "type": "setup_quality", "severity": "MAJOR", "file": ".goat-flow/architecture.md", "line": null,');
lines.push(` "summary": "One-line finding summary", "detail": "Why it matters; include a semantic anchor when the evidence should survive as a durable learning-loop artifact.", "evidence_quality": "OBSERVED", "evidence_method": "static-analysis", "delta_tag": ${input.priorReport ? '"new"' : "null"}`);
lines.push(" }");
lines.push(" ]");
lines.push("}");
lines.push("```");
lines.push("");
lines.push("JSON rules:");
lines.push("- `scores.*` axis values must use exact `0 | 5 | 10 | 15 | 20 | 25` increments and each axis sum must equal its `total` exactly.");
lines.push("- Allowed `type` values: `setup_quality`, `skill_flaw`, `contradiction`, `false_path`, `content_quality`, `framework_flaw`.");
lines.push("- Allowed `severity` values: `BLOCKER`, `MAJOR`, `MINOR`.");
lines.push("- `evidence_quality` is REQUIRED on every finding. Allowed values: `OBSERVED` (verified in code/output), `INFERRED` (state what's missing). Omitting this field causes the report to be rejected.");
lines.push("- `evidence_method` is REQUIRED on every finding (schema v2, 2026-04-19+). Allowed values: `runtime-probe` (you invoked commands/tools to verify - e.g. `npx eslint`, `bash <hook>`), `static-analysis` (you read files only), `mixed` (both methods for this specific finding). A finding labelled `OBSERVED` via `static-analysis` can still miss runtime-only defects; labelling the method honestly lets cross-report triangulation flag methodology gaps.");
lines.push("- Runtime-backed findings SHOULD include compact evidence fields when useful: `evidence_command` (the command), `evidence_exit_code` (integer), `evidence_summary` (literal pass/fail or warning summary), `evidence_warning_count` (integer), and `evidence_excerpt` (short single-line excerpt). Do not paste raw terminal blocks into JSON.");
lines.push('- `scope` is REQUIRED at top level. Set `framework-self` if you detect this is the goat-flow repo itself (heuristic: `package.json` contains `"name": "@blundergoat/goat-flow"`). Otherwise set `consumer`.');
lines.push(`- \`rubric_version\` is REQUIRED at top level; copy the template value (\`"${getPackageVersion()}"\`). The Rating bands section above is the rubric - future readers use this version tag to trace which band anchors produced your scores.`);
lines.push(`- \`quality_mode\` is REQUIRED for new reports generated from this prompt. Use \`${jsonString(input.qualityMode)}\` for this ${qualityModeLabel(input.qualityMode)} assessment.`);
lines.push(`- \`prior_report_id\` must be ${input.priorReport ? `\`${input.priorReport.id}\`` : "`null`"} for this run. This makes \`delta_tag\` traceable to the same-agent baseline and prevents readers from treating \`new\` as newly introduced without a diff.`);
lines.push("- `line` must be a positive integer OR `null`. Never `0`. For file-wide findings with no specific line, use `null`.");
lines.push("- Live review findings should cite `file` + semantic anchor after re-reading the cited file and anchor. Durable footguns, lessons, patterns, and decisions must use file paths plus semantic anchors rather than line numbers.");
if (input.priorReport) {
lines.push('- `delta_tag` is REQUIRED on every current finding and must be either `"new"` or `"persisted"`.');
}
else {
lines.push("- `delta_tag` must be `null` or omitted when no prior report context exists.");
}
lines.push("- Do NOT include an `id` field. The CLI attaches positional finding ids deterministically when the report is loaded.");
lines.push("- Do NOT include extra top-level keys or extra finding keys outside this contract. Unknown keys are rejected.");
lines.push("- `summary` and `detail` MUST be single-line strings. No literal newlines, tabs, or other control characters. If you need to reference multi-line command output, summarise the outcome in prose - do NOT paste raw terminal blocks into JSON string fields. Pasted multi-line content produces unparseable JSON and the report is lost.");
lines.push("- If you write the file via a bash heredoc, QUOTE the delimiter (`<<'EOF'`, not `<<EOF`). Unquoted delimiters make the shell interpret `` `backticks` `` as command substitution, which silently eats your inline code references.");
lines.push("");
lines.push("**Validate before confirming.** After writing the file, run:");
lines.push("");
lines.push("```bash");
lines.push('goat-flow quality validate "$FILE" # or: node --import tsx src/cli/cli.ts quality validate "$FILE"');
lines.push("```");
lines.push("");
lines.push("If validate exits non-zero, read the reported error, fix the JSON, and re-write the file. Do NOT emit the confirmation below until validate passes.");
lines.push("");
lines.push("If command execution is unavailable, do not claim validation passed. Confirm instead with: `Wrote unvalidated quality report to .goat-flow/logs/quality/<your-filename>.json; validation unavailable: <exact reason>`.");
lines.push("");
lines.push("**End of response:** After validate passes, confirm in prose with a single line: `Wrote quality report to .goat-flow/logs/quality/<your-filename>.json`. Do not include the JSON inline in your reply.");
lines.push("");
}
//# sourceMappingURL=compose-quality-agent-report.js.map