Skip to content

[rig-tasks] Add 10 rig samples — 2026-08-10 - #395

Merged
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-08-10-b19941e1e0260c9b
Aug 10, 2026
Merged

[rig-tasks] Add 10 rig samples — 2026-08-10#395
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-08-10-b19941e1e0260c9b

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Summary

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

# File Description Kind Typecheck
1 391-ts-barrel-re-exporter.md TypeScript barrel file generator with regex export extraction agent ✅ pass
2 392-gh-actions-step-duration-estimator.md GitHub Actions step duration estimator agent ✅ pass
3 393-ssh-config-host-parser.md SSH config Host block parser using p.readOptional agent ✅ pass
4 394-vitest-test-file-classifier.md Vitest test file classifier by type/mocks agent ✅ pass
5 395-git-file-at-revision.md Git file content at a given revision agent ✅ pass
6 396-json-schema-validator.md JSON schema structure validator agent ✅ pass
7 397-zlib-compression-analyzer.md Zlib deflate compression ratio analyzer (node:zlib) agent ✅ pass
8 398-file-crypto-hash-reporter.md Cryptographic hash reporter using node:crypto agent ✅ pass
9 399-pkg-scripts-trio-workflow.md Three-subagent workflow for package.json scripts analysis workflow ✅ pass
10 400-ts-const-enum-extractor.md TypeScript const enum member extractor agent ✅ pass

Typecheck failures

Two tasks required fixes before passing:

Task 6 (396-json-schema-validator): Initial code used Record<string, unknown> for parsed JSON schema, triggering TS4111: Property 'required' comes from an index signature errors. Fixed by declaring a typed interface for the schema shape.

Task 9 (399-pkg-scripts-trio-workflow): Initial code used parallel([() => call(agentA, ...), () => call(agentB, ...)]) with heterogeneous agent output types. parallel<Result> infers a single homogeneous type, so TypeScript could not reconcile the differing outputs. Fixed by using sequential call() statements instead.

Tasks run

  • (reused) TypeScript barrel re-exporter
  • (reused) GitHub Actions workflow step duration estimator
  • (reused) SSH config host parser
  • (reused) Vitest test file classifier
  • (reused) Git file at revision extractor
  • (reused) JSON schema structure validator
  • (new) Zlib compression analyzer — node:zlib deflateSync compression ratio measurement
  • (new) File crypto hash reporter — node:crypto createHash per-file hashing
  • (new) Package scripts trio workflow — sequential 3-subagent workflow
  • (new) TypeScript const enum extractor — regex-based const enum member extraction

Generated by Daily Rig Task Generator · sonnet46 100.6 AIC · ⌖ 12.9 AIC · ⊞ 6.8K ·

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review August 10, 2026 10:37
@pelikhan
pelikhan merged commit 170e3b3 into main Aug 10, 2026
1 check passed
@github-actions

github-actions Bot commented Aug 10, 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 /grill-with-docs — requesting changes on 3 correctness issues where samples encode misleading or incorrect rig API usage patterns.

📋 Key Themes & Highlights

Issues (correctness — readers will copy these patterns)

  • p.writeOutput vs p.writeInput (391): outputFile is a caller-supplied input field but the sample writes to a hardcoded static path, silently ignoring it. Should use p.writeInput.
  • Hardcoded p.bash in 395: Reads HEAD:README.md at definition time regardless of the agent's own filePath/revision input fields — a self-contradicting sample.
  • require() in ESM handler (395): Only instance across all 10 samples; breaks the consistent import convention.
  • p.glob("**/*") ignores targetDir input (398): Files are enumerated across the entire workspace, not scoped to the input directory.
  • Optional algorithm mismatch (398): Declared optional in agent input, required in tool parameters — forces the LLM to guess a value silently.

Positive Highlights

  • ✅ Clean use of repair() vs steering() addons chosen appropriately by task type
  • p.readOptional used correctly in 393 for the potentially-absent SSH config
  • p.readInput(field) used correctly in 396 for caller-supplied file paths
  • node: prefix consistently used on Node.js builtins (except the require in 395)
  • ✅ 399 correctly falls back to sequential call() after the parallel type-inference issue

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

List .ts files (excluding index.ts): ${p.bash("find . -name '*.ts' -not -name 'index.ts' -not -name '*.d.ts' 2>/dev/null | head -30")}
For each relevant file in sourceDir, call extractExportedSymbols.
Then call p.write equivalent: use the write intent below to persist the barrel.
${p.writeOutput("barrelContent", "barrel-out.ts")}

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] p.writeOutput hardcodes the destination path (barrel-out.ts), silently ignoring the caller-supplied outputFile input field. Readers who copy this pattern will lose dynamic path support.

💡 Fix: use p.writeInput for caller-supplied paths

p.writeOutput(field, path) requires a static path. Since outputFile comes from the agent's input, use:

${p.writeInput("outputFile", "barrelContent")}

See references/prompt-intents.mdp.writeOutput vs p.writeInput is the exact distinction documented there.

description: "Run git log to get commit hash and message for a revision",
parameters: s.object({ revision: s.string }),
handler({ revision }) {
const { execSync } = require("node:child_process");

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] require() is used inside an ES module handler. All other samples use top-level import — this is the sole exception and it will confuse readers about the expected module style.

💡 Fix: top-level import

Move the import to the top of the file alongside the other imports:

import { execSync } from "node:child_process";

Then remove the inline require. This is consistent with the node: prefix convention used throughout the samples.

model: "small",
input: s.object({ filePath: s.string, revision: s.string }),
instructions: p`Fetch the file content and commit metadata for the given revision.
File at revision: ${p.bash("git show HEAD:README.md 2>/dev/null | head -5 || echo '(use input revision and filePath)'")}

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 p.bash intent reads HEAD:README.md unconditionally at definition time, not the actual filePath/revision from input. This leaks repository-specific content into the sample's prompt and won't generalize — readers see a misleading pattern for reading input-based git revisions.

💡 Suggested replacement

The correct intent for fetching a file at a caller-supplied revision is either a p.bash using the runtime input fields, or better, a defineTool that runs git show. The current hint comment (use input revision and filePath) acknowledges this but leaves the wrong intent in place. Consider:

instructions: p`Fetch file content and commit metadata.
Call extractRevisionMetadata with the revision from input, then call getFileAtRevision.
Return the declared output.`,

And add a getFileAtRevision tool alongside extractRevisionMetadata using execSync (or execa) with git show ${revision}:${filePath}.

model: "small",
input: s.object({ targetDir: s.string, algorithm: s.optional(s.string) }),
instructions: p`Compute file hashes for all files in targetDir.
Files found: ${p.glob("**/*")}

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] p.glob("**/*") lists all files in the entire workspace, not the caller-supplied targetDir. This ignores the agent's own input contract and is a common misuse pattern readers will repeat.

💡 Fix: scope the glob to targetDir

Since targetDir is a runtime input value, you cannot embed it directly in a p.glob call (which is static). The right approach is to use p.bash with the directory from input, or enumerate files inside computeFileHash's caller loop using a listDir tool:

instructions: p`Compute file hashes for all files in targetDir from input.
List files: ${p.bash("find . -type f | head -50")}
For each file in targetDir call computeFileHash.`,

Alternatively, add a listFilesInDir tool that receives targetDir from the LLM and uses readdir with full paths.

tools: [
defineTool("computeFileHash", {
description: "Compute the cryptographic hash and size of a file",
parameters: s.object({ filePath: s.path, algorithm: s.string }),

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 algorithm parameter is declared s.optional(s.string) in the agent input but required (s.string, no optional) in the tool's parameters. If the caller omits algorithm, the LLM must invent a value — a silent default-guessing trap.

💡 Fix: resolve algorithm before passing to tool

Either default it in the agent instructions (use 'sha256' if no algorithm supplied) and document that in the instructions, or make the tool parameter optional and default inside the handler:

parameters: s.object({ filePath: s.path, algorithm: s.optional(s.string) }),
async handler({ filePath, algorithm = "sha256" }) { ... }

const listed = await call(scriptsLister, "list scripts");
const health = await call(scriptsHealthChecker, "check health");
const scripts = listed?.scripts ?? [];
const categorized = await call(scriptsCategorizer, { scripts });

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] health result is used with optional chaining (health?.dependencyHealth) but listed is also accessed with optional chaining (listed?.scripts ?? []). Both subagents have fully-typed, required output schemas, so ? is unnecessary and misleads readers into thinking nullable results are normal here.

💡 Minor but patterns matter in samples

If call() returns null on failure it signals an error path that isn't handled — in that case explicit error handling is better than silent fallback. Samples should reflect correct usage:

const listed = await call(scriptsLister, "list scripts");
const scripts = listed.scripts; // typed, no fallback needed if error is surfaced

If resilient fallback is desired, add a comment explaining why.

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