Skip to content

[rig-claude] Improve Claude dynamic-workflow compatibility for rig - #344

Merged
pelikhan merged 1 commit into
mainfrom
rig-claude-compat/2026-08-03-be4609dc3871b594
Aug 4, 2026
Merged

[rig-claude] Improve Claude dynamic-workflow compatibility for rig#344
pelikhan merged 1 commit into
mainfrom
rig-claude-compat/2026-08-03-be4609dc3871b594

Conversation

@github-actions

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

Copy link
Copy Markdown
Contributor

Compatibility gap addressed

Sample 360-parallel-branch-analysis-workflow.md used Promise.all instead of parallel(thunks), which is the idiomatic primitive in both Claude dynamic workflows and rig. This breaks knowledge transfer in two ways:

  1. Wrong concurrency semantics: Promise.all bypasses the shared concurrency limiter; parallel(thunks) respects it.
  2. Wrong failure semantics: Promise.all rejects the entire batch on any failure; parallel(thunks) converts individual failures to null holes — matching Claude's behavior.

A user porting a Claude dynamic workflow who searches for parallel would find a sample using the wrong primitive and learn incorrect patterns.

Additionally, the original sample was already failing the 30-line validation test at baseline (60 lines vs the 30-line limit), causing npm run sample -- --testNamePattern="skill markdown samples typecheck" to fail.

Why this improves Claude→Rig transfer

The claude-workflow-conversion.md reference explicitly maps parallel(thunks)parallel(thunks) with identical semantics. Sample 360 is the most prominent heterogeneous-fan-out example but showed Promise.all. Fixing it makes the mapping obvious and keeps the sample runnable.

Files changed

  • skills/rig/samples/360-parallel-branch-analysis-workflow.md — rewrote to use parallel(thunks), simplified to a shared metric output schema (avoids TypeScript union-type inference issues with heterogeneous thunks), added prose explaining why parallel is preferred over Promise.all when porting Claude workflows, and reduced to exactly 30 lines to pass the test.

Validation run

npx vitest run scripts/run-sample.test.ts --testNamePattern="360"
# Tests: 1 passed

npx vitest run scripts/run-sample.test.ts --testNamePattern="typechecks"
# Tests: 1 passed

Remaining intentional differences

None introduced by this change. All existing documented differences between Claude dynamic workflows and rig (no effort option, no agentType: "Explore", no resume journal) remain as documented in references/claude-workflow-conversion.md.

Generated by Daily Rig Claude Dynamic Workflow Compatibility · sonnet46 151.8 AIC · ⌖ 8.39 AIC · ⊞ 5.4K ·

…e count

Sample 360 used Promise.all instead of parallel(thunks), which bypasses
the shared concurrency limiter and doesn't convert failures to null holes.
This diverges from Claude dynamic-workflow semantics where parallel(thunks)
is the idiomatic primitive. A user porting a Claude workflow would look for
parallel and find a sample using the wrong primitive.

Also: the sample was already failing the 30-line test at baseline (60 lines).
This commit rewrites it to be idiomatic and within the 30-line limit.

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

github-actions Bot commented Aug 4, 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 two issues: an unanchored schema field and a missing null-hole demonstration.

📋 Key Themes & Highlights

Issues

  • Unanchored label field: metric schema requires label: s.string but neither agent instruction describes what value to emit. This is a repair-loop risk every time the sample is run or adapted.
  • Missing null-hole guard: The PR's primary teaching claim — that parallel converts failures to null holes — is asserted in prose but not shown in code. The sample should demonstrate it with a null check so readers understand the practical difference from Promise.all.

Positive Highlights

  • ✅ Correct use of parallel(thunks) over Promise.all — good idiomatic fix
  • ✅ Shared metric schema is a clean simplification that avoids TypeScript union-inference issues
  • ✅ Sample now fits within the 30-line validation limit
  • ✅ Explanatory prose in the intro is a useful addition for Claude→rig porters

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

});
// Agent role: measure branch staleness as a labeled metric.
const branchAgent = agent({ model: "small", output: metric,
instructions: p`Count stale/total branches.\n${p.bash("git branch -a 2>/dev/null || echo ''")}` });

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] metric schema declares label: s.string, but neither agent instruction tells the model what value to put there — only numeric counting is described. This risks repair loops or silently hallucinated label values.

💡 Suggestion

Either drop the label field (the caller already knows which agent produced which slot), or explicitly instruct each agent to set it:

const branchAgent = agent({ model: "small", output: metric,
  instructions: p`Emit { label: "stale-branches", value: <stale count> }.\n${p.bash("git branch -a 2>/dev/null || echo ''")}` });

A schema field with no prompt instruction is a latent repair-loop.

const [branches, commits] = await parallel([
() => call(branchAgent, "measure"),
() => call(commitAgent, "measure"),
]);

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 PR's stated teaching goal is that parallel converts individual failures to null holes, but the sample destructures the result directly without any null guard. Readers learn the API name but not the semantics that justify using it.

💡 Suggestion

Add a brief inline guard to make the null-hole pattern explicit:

const [branches, commits] = await parallel([
  () => call(branchAgent, "measure"),
  () => call(commitAgent, "measure"),
]);
// branches or commits may be null if an agent failed
if (!branches || !commits) return "insufficient-data";

This turns the sample into a real demonstration of the failure-semantic difference versus Promise.all.

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