Skip to content

[rig-tasks] Add 10 rig samples — 2026-07-28 - #242

Merged
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-07-28-a3a2271bf1572835
Jul 28, 2026
Merged

[rig-tasks] Add 10 rig samples — 2026-07-28#242
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-07-28-a3a2271bf1572835

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Summary

Added 10 new rig sample files to skills/rig/samples/.

# File Description Typecheck
1 250-git-commit-annotator.md Nano subagent delegation per commit with p.writeOutput pass
2 251-npm-script-dep-mapper.md defineTool pre/post hook detection; s.record output with repair() pass
3 252-markdown-link-checker.md Async defineTool curl URL checker; s.optional(s.int) for HTTP code pass
4 253-package-version-drift.md p.bash npm outdated + p.writeOutput; drift level s.enum pass
5 254-git-author-stats.md Dual p.bash intents; s.record(s.object(...)) author map pass
6 255-ts-path-alias-validator.md defineTool with node:fs existsSync; s.enum(valid/broken/unused) pass
7 256-gh-actions-timing-analyzer.md p.glob + defineTool YAML parsing; suggestions s.array(s.string) pass
8 257-binary-file-detector.md Async defineTool buffer sampling; steering() addon for large sets pass
9 258-ts-interface-inheritance-graph.md Regex defineTool for extends chains; depth s.enum(root/derived/leaf) pass
10 259-dep-license-matrix.md SPDX classification defineTool; hasConflicts s.boolean summary pass

Typecheck failures

No typecheck failures. All 10 programs passed on the first attempt.

Tasks run

  • (reused) Git commit annotator with nano subagent
  • (reused) NPM script dependency mapper
  • (reused) Markdown link checker
  • (reused) Package version drift reporter
  • (reused) Git author stats aggregator
  • (reused) TypeScript path alias validator
  • (new) GitHub Actions workflow timing analyzer
  • (new) Binary file detector
  • (new) TypeScript interface inheritance graph builder
  • (new) Dependency license compatibility matrix

Generated by Daily Rig Task Generator · sonnet46 94.1 AIC · ⌖ 6.59 AIC · ⊞ 6.7K ·

10 new sample files covering subagent delegation, defineTool patterns,
s.record output, s.enum classification, repair/steering addons, and new
agentic patterns (binary detection, interface inheritance, license matrix,
workflow timing).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review July 28, 2026 04:16
@pelikhan
pelikhan merged commit e3b5d1a into main Jul 28, 2026
1 check passed
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /grill-with-docs — no blocking issues, but 6 actionable findings across the new samples worth addressing.

📋 Key Themes & Highlights

Key Themes

  • Shell injection (252): url interpolated directly into a curl shell command — any URL from an untrusted source can execute arbitrary code.
  • Memory over-allocation (257): full file read before 8 KB slice; harmless for text repos but could OOM on large binary assets.
  • Fragile data pipeline (259): paste - - approach for pairing name/license lines silently misaligns when a package.json has multiple matches or a missing field.
  • False schema fields (253): reportWritten: s.boolean cannot be reliably populated by the LLM since p.writeOutput has no return path back into the output shape.
  • Regex over-counting (256): job-count regex matches all 2-space-indented YAML keys, not just those under jobs:, inflating the count and triggering incorrect suggestions.
  • SPDX naming mismatch (259): spdxId is the raw input string, not a normalised SPDX identifier — potentially misleading for callers doing SPDX lookups.

Positive Highlights

  • ✅ Consistent use of s.enum for all classification fields — strong pattern for downstream consumers.
  • repair() addon correctly applied on agents that produce complex nested output.
  • steering() on binaryFileDetector is the right addon choice for large unbounded tool-call loops.
  • ✅ All 10 samples passed typecheck on first attempt — clean generation quality.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 61.3 AIC · ⌖ 4.88 AIC · ⊞ 6.3K
Comment /matt to run again

const { execSync } = await import("node:child_process");
try {
const code = execSync(
`curl --head --silent --max-time 5 -o /dev/null -w "%{http_code}" "${url}" 2>/dev/null`,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] Shell injection risk: url is interpolated directly into a shell string. A URL with embedded $(...) or backticks would execute arbitrary code when curl is invoked via execSync.

💡 Suggested fix

Shell-escape the URL or pass it as an argument after --:

const safeUrl = url.replace(/"/g, '');
const code = execSync(
  `curl --head --silent --max-time 5 -o /dev/null -w "%{http_code}" -- "${safeUrl}"`,
  { encoding: "utf8", timeout: 8000 }
).trim();

Or avoid the shell entirely with node:https / fetch.

const stats = await stat(filePath);
const sizeBytes = stats.size;
const buf = await readFile(filePath);
const sample = buf.slice(0, 8192);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] buf.slice() returns a Buffer view sharing the same memory as the parent, not a copy — this is fine for reading, but for very large files the full file is still read into memory before slicing. Consider readFile with a byte range or use open + read to limit I/O to the 8KB sample.

💡 Suggested alternative
const fh = await import('node:fs/promises').then(m => m.open(filePath, 'r'));
try {
  const buf = Buffer.alloc(8192);
  const { bytesRead } = await fh.read(buf, 0, 8192, 0);
  const sample = buf.subarray(0, bytesRead);
  // ... null byte check
} finally {
  await fh.close();
}

This avoids loading a 500 MB binary into memory just to sample its first 8 KB.

} else {
compatibility = "unknown";
}
return JSON.stringify({ packageName, spdxId: licenseField, compatibility });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/grill-with-docs] The tool returns spdxId: licenseField (the raw input string), but the output schema also declares spdxId: s.string — the naming implies a normalised SPDX identifier, not the raw field. If licenseField is e.g. "Apache 2.0" (non-standard casing) the caller gets back a non-SPDX string labelled spdxId, which is misleading and could break downstream comparisons.

💡 Suggestion

Normalise to a canonical SPDX id before returning, or rename the field to licenseRaw to signal it's unprocessed. If normalisation isn't in scope, at least document the caveat in the tool description.


package.json: ${p.read("package.json")}

Installed package licenses: ${p.bash("cat node_modules/*/package.json 2>/dev/null | grep -E '\"name\"|\"license\"' | paste - - | head -60 || echo 'Run npm install first'")}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] cat node_modules/*/package.json | grep ... | paste - - is fragile: paste - - pairs consecutive lines assuming each package.json contributes exactly one "name" and one "license" line — which breaks for packages with multiple matches or missing fields. This can silently produce misaligned name/license pairs.

💡 More robust alternative

Use node -e to dump the data as newline-delimited JSON instead:

node -e "const fs=require('fs'),g=require('path').join; require('fs').readdirSync('node_modules').forEach(p=>{try{const m=JSON.parse(fs.readFileSync(g('node_modules',p,'package.json')));console.log(JSON.stringify({name:m.name,license:m.license||''}));}catch{}})"

This is deterministic and safe even when license is an object.

description: "Parse a GitHub Actions workflow YAML to extract job names, step counts, and detect optimization patterns",
parameters: s.object({ content: s.string, filename: s.string }),
handler: ({ content, filename }) => {
const jobMatches = content.match(/^\s{2}[\w-]+:/gm) ?? [];

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The job-count regex /^\s{2}[\w-]+:/gm will match any top-level YAML key indented exactly two spaces — including on:, env:, defaults:, concurrency:, and permissions:, not only jobs.<id>. This overstates jobs and triggers the wrong suggestions.

💡 Suggested fix

Anchor the match to the jobs: block. A simple heuristic is to count keys two levels deep under a jobs: line:

const jobsSection = content.match(/^jobs:\n([\s\S]*?)(?=^\S|$)/m)?.[1] ?? '';
const jobMatches = jobsSection.match(/^  [\w-]+:/gm) ?? [];

Or just document the known over-counting so sample readers aren't misled.

})
),
report: s.string,
reportWritten: s.boolean,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/grill-with-docs] reportWritten: s.boolean is declared in the output schema but there is no mechanism for the agent to know whether p.writeOutput succeeded — it's a prompt instruction, not a tool call with a return value. The field will be whatever the LLM guesses (likely true always), making it a false signal.

💡 Suggestion

Remove reportWritten from the output schema, or if confirming file writes matters, use a defineTool that does the write in-process and returns a real success/error value.

parameters: s.object({ content: s.string }),
handler: ({ content }) => {
const results: Array<{ name: string; extends: string[] }> = [];
const re = /interface\s+(\w+)(?:<[^>]+>)?\s*(?:extends\s+([\w<>, ]+))?\s*\{/g;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The regex /interface\s+(\w+)(?:<[^>]+>)?\s*(?:extends\s+([\w<>, ]+))?\s*\{/g can false-positive on string literals, comments, or @interface JSDoc annotations. Additionally, the [\w<>, ]+ extends capture is greedy and will include leading/trailing spaces and multi-line extends that cross a newline — m[2] will be undefined in that case, silently dropping parents.

💡 Note

For a sample this is acceptable, but the description should note the regex-based limitation so readers don't copy it into production tooling. A comment like // NOTE: heuristic regex; does not handle multiline extends or string literals would set correct expectations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant