Skip to content

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

Merged
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-08-08-45a0908956e54fdd
Aug 8, 2026
Merged

[rig-tasks] Add 10 rig samples — 2026-08-08#370
pelikhan merged 1 commit into
mainfrom
rig-tasks/2026-08-08-45a0908956e54fdd

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

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

# File Description Kind Typecheck
1 371-ts-dead-export-finder.md TypeScript dead export finder agent ✅ pass
2 372-package-scripts-documenter.md Package scripts documenter agent ✅ pass
3 373-git-diff-stats-summarizer.md Git diff stats summarizer agent ✅ pass
4 374-dotenv-template-generator.md Dotenv template generator agent ✅ pass
5 375-ts-class-hierarchy-extractor.md TypeScript class hierarchy extractor agent ✅ pass
6 376-git-bisect-helper.md Git bisect helper agent ✅ pass
7 377-yaml-key-presence-validator.md YAML key presence validator agent ✅ pass
8 378-git-tag-release-aggregator.md Git tag release aggregator agent ✅ pass
9 379-npm-outdated-reporter.md NPM outdated reporter agent ✅ pass
10 380-ts-string-literal-unions.md TypeScript string literal unions agent ✅ pass

Typecheck failures

No failures — all 10 tasks passed typecheck on first attempt.

Tasks run

  • (reused) TypeScript dead export finder: p.glob + p.bash + async defineTool + steering()+repair()
  • (reused) Package scripts documenter: p.read + defineTool classification + p.write + repair()
  • (reused) Git diff stats summarizer: p.bash git diff --numstat + defineTool s.enum + repair()
  • (reused) Dotenv template generator: p.readOptional + p.glob + async defineTool + p.write + repair()
  • (reused) TypeScript class hierarchy extractor: p.bash find + async defineTool + steering()
  • (reused) Git bisect helper: p.bash git log + defineTool selectMidpoint + steering() maxTurns:8
  • (new) YAML key presence validator: input s.object + p.readInput + defineTool regex + repair()
  • (new) Git tag release aggregator: p.bash git tag + defineTool fetchTagCommits + steering()
  • (new) NPM outdated reporter: p.bash npm outdated --json + defineTool s.enum severity + repair()
  • (new) TypeScript string literal union extractor: p.glob + async defineTool regex + steering()

Generated by Daily Rig Task Generator · sonnet46 119.3 AIC · ⌖ 10.3 AIC · ⊞ 6.8K ·

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

github-actions Bot commented Aug 8, 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 /codebase-design — requesting changes for two correctness issues (371, 378) and two logic bugs (375, 377) before merging.

📋 Key Themes & Highlights

Issues to Fix

  • 371 – Self-reference bug: combinedContent includes the exporting file itself, so symbols used only within their own file are never flagged as dead. Fix: scan only other files' content when checking each file's exports.
  • 378 – require() in handler: Uses CommonJS require("node:child_process") inside a tool handler, violating the project's ES-import-only style and node: prefix rule. Fix: top-level import { execSync } from "node:child_process".
  • 377 – Fragile YAML key traversal: The dot-path check narrows remaining content but ignores indentation, so server.port can false-positive if port appears in any section after server:. Needs either a comment documenting the heuristic limit or a proper YAML parse.
  • 375 – isAbstract off-by-offset: The -10 char slice before the match index is unsafe. The regex already captures the abstract keyword — use m[0].startsWith("abstract") instead.

Positive Highlights

  • ✅ All 10 samples pass typecheck on first attempt
  • ✅ Consistent use of repair() and steering() addons appropriate to each agent's needs
  • ✅ Good coverage of new patterns: p.readInput, s.object input schema, s.optional, s.path
  • ✅ 379 and 380 are clean, well-structured examples

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

});
if (unused.length > 0) unusedExports[file] = unused;
}
return JSON.stringify(unusedExports);

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.

[/codebase-design] Self-reference bug: all file contents are joined into one combinedContent string, so if a symbol appears in an import statement within the same file, it passes the check and is never flagged as unused.

💡 Suggested fix

When testing whether a symbol is imported, scan only the other files' content, not the exporting file itself:

for (const [file, symbols] of Object.entries(exportedSymbols)) {
  const otherContent = files
    .filter((f) => f !== file)
    .map((f) => allContent[files.indexOf(f)])
    .join('\n');
  const unused = symbols.filter((sym) => {
    const importPattern = new RegExp(`import[^;]+\\b${sym}\\b`);
    return !importPattern.test(otherContent);
  });
  if (unused.length > 0) unusedExports[file] = unused;
}

A symbol that is only referenced in its own file is still dead from the outside.

const { execSync } = require("node:child_process");
try {
const log = execSync(
`git log ${fromTag}..${toTag} --oneline --format="%H %ai" 2>/dev/null | head -100`,

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.

[/codebase-design] require("node:child_process") inside a tool handler breaks the project style rule: Node built-ins must use top-level ES import with the node: prefix. Using require inside a handler also means the module is re-resolved on every call.

💡 Suggested fix

Move the import to the top of the file:

import { execSync } from "node:child_process";

Then remove the const { execSync } = require(...) line inside the handler. Every other sample in this PR already follows this pattern.

let present = false;
// Check if all parts of the key path appear in order as indented keys
let remaining = content;
for (const part of parts) {

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.

[/codebase-design] The key-path traversal is structurally unsound: it searches for each dot-notation segment as a regex anywhere in the remaining content, not within the correct indentation scope. For example, server.port would match if port appears in a completely different section that happens to follow the server: line.

💡 Suggested approach

Either use a YAML parser (e.g., js-yaml) for correctness, or document the limitation clearly so sample users know the tool is a heuristic approximation. The current code is a valid teaching example of progressive narrowing, but the comment (line 17) should warn:

// Heuristic: searches for each key segment in remaining content.
// Does not track indentation scope — may produce false positives
// for identical key names in sibling sections.

If the sample is meant to demonstrate a real-world pattern, prefer a proper YAML parse.

current: s.string("current installed version"),
latest: s.string("latest available version"),
}),
handler({ current, latest }) {

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.

[/codebase-design] Minor: parse(current) is called twice via two separate destructuring assignments ([curMajor] and [, curMinor]). This doubles the work and is easy to misread. Combine into one call.

💡 Suggested fix
handler({ current, latest }) {
  const parse = (v: string) => v.replace(/^[^0-9]*/, "").split(".").map(Number);
  const [curMajor, curMinor] = parse(current);
  const [latMajor, latMinor] = parse(latest);
  if (latMajor > curMajor) return "major" as const;
  if (latMinor > curMinor) return "minor" as const;
  return "patch" as const;
},

Also note: if current or latest is a pre-release string like 1.0.0-beta.1, map(Number) produces NaN for the pre-release segment. Adding a || 0 guard covers this edge case.

parent: m[2] ?? null,
interfaces: m[3] ? m[3].split(",").map((s: string) => s.trim()).filter(Boolean) : [],
isAbstract: src.slice(Math.max(0, m.index! - 10), m.index!).includes("abstract"),
});

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.

[/codebase-design] The isAbstract detection is redundant and fragile. The regex already optionally captures abstract\s+ as part of the class pattern, so m[0] (the full match) reliably includes it. The current approach slices raw source backwards by a fixed 10-character offset, which can misfire when there's whitespace or a comment between abstract and class.

💡 Suggested fix

Use the match itself:

results.push({
  name: m[1],
  parent: m[2] ?? null,
  interfaces: m[3] ? m[3].split(",").map((s: string) => s.trim()).filter(Boolean) : [],
  isAbstract: m[0].startsWith("abstract"),
});

This is unambiguous and does not require the unsafe index arithmetic.

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