Skip to content

perf(ci): parallelize repo audits, guard env-dependent tests, and fix the docs generator - #6358

Merged
waleedlatif1 merged 10 commits into
stagingfrom
ci-parallel-audits
Aug 7, 2026
Merged

perf(ci): parallelize repo audits, guard env-dependent tests, and fix the docs generator#6358
waleedlatif1 merged 10 commits into
stagingfrom
ci-parallel-audits

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Three related pieces of CI/docs work. All three came out of chasing why type-checking was slow.

1. Parallelize the repo audits — and actually make them faster

21 independent read-only tree walks ran as 21 sequential CI steps. scripts/run-audits.ts runs them concurrently and replays output only for failures, so a green run stays quiet and a red one still names the audit.

The first version of this made CI slower, not faster — 31s serial → 39.2s wall. check:desktop-bridge went from 1s to 39.2s and became the entire wall clock while the other 20 finished in 9s. It's the only audit that shells out through bunx, which re-resolves the package against the shared install cache — a network-backed sticky-disk mount on CI. Cheap alone, serialized behind the others when run together. Spawning the resolved compiler entry directly removes that layer.

2. Guard tests that depend on tools the repo doesn't vendor

5 tests fail for every macOS dev and are invisible in CI. They shell out to python3 using PEP 701 f-strings (3.12+); stock macOS ships 3.9.6, so bun run test produced raw Python SyntaxErrors with nothing tying them to a missing tool. One also needs ripgrep.

These suites deliberately run the real helper rather than a mock — the cloud-review path/read-size bounds and the placeholder compiler's generated Python are only observable that way. So @sim/testing/environment skips them with a reason locally via ctx.skip(), but throws under CI: a missing tool on the runner means a security boundary silently stopped being covered.

Reviewers correctly caught that the floor should be 3.12, not 3.10. Verified on a real 3.11 interpreter — the match-guard test passes, but the two f-string tests fail with f-string: unmatched '(' and f-string expression part cannot include '#'.

Also drops the Codecov upload, which the workflow itself documented as a dead path.

3. Unbreak the docs build + clean up the generator

The docs build has been failing on staging since the Smartlead merge: Expected a closing tag for ``. Tool descriptions are emitted as prose, and that path escaped only braces while every table-cell path already escaped angle brackets.

Trigger config now comes from the evaluated registry, not regex over source. Static parsing silently dropped every field whose builder assembled its array imperatively or took a description as a parameter — all ten Jira triggers lost webhookSecret/jqlFilter, Monday lost its config too. Regenerating the docs was destructive. Deletes 232 lines of parsing.

Tool headings show the tool's name, not its id### \a2a_send_message`### A2A Send Message`, across 241 pages. These headings feed each page's table of contents.

Generator: 4,306 → 4,069 lines.

Type of Change

  • Bug fix + improvement

Testing

The generator refactor was verified against a golden manifest of all 289 generated files, itself validated both ways — deterministic across runs, and proven to catch a one-character change. Only intended output changed.

Check Result
local, python 3.9.6 5 skipped, 0 failed, one actionable warning
local, python 3.11 skips cleanly (was 2 failures)
CI=true, python 3.9.6 hard error, refuses to skip
CI=true, python 3.13 49 run, 0 skipped — doesn't over-skip
full apps/sim suite 20,408 passed, 0 failed
escape-chain dedup / comment trim byte-identical, 289 files
post-rebase regen zero drift

Verified the failure paths too: broke an audit (runner named it, replayed output, exit 1), and broke the bridge contract (audit still fails after the bunx change — it isn't passing faster by doing less).

Things I built, measured, and threw away rather than ship: a regex fix for the imperative-array pattern (invented fieldFilters on 9 triggers that don't declare it); sourcing trigger outputs from the registry (deleted 10,298 lines across 53 files); isolate: false in vitest (1.94x faster, 429 test failures).

Known gaps — please don't read this as "all clear"

  • extractTriggerOutputs still parses source and has the same blind spot as the config parser. It already drops one Jira #### Output section on main — verified as pre-existing. Regenerating is now safe for trigger config, still lossy for trigger outputs.
  • openai.mdx is a stale orphan with no backing block since feat(embeddings): multi-provider Embeddings block on a shared core #6317. The generator doesn't rewrite it and the stale-doc cleanup doesn't delete it. Probably just wants deleting.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Added since the first review round

Trigger outputs — the known gap above is now closed. extractTriggerOutputs also parsed source, so jira_webhook had no output section at all. The registry wasn't a drop-in: a TriggerOutput marks a nested group by omitting type and holding children as sibling keys, while the renderer walks a {type, description, properties} shape the parser used to synthesize. Handing it the raw value collapsed every group to one untyped row — that's the 10,298-line deletion an earlier attempt produced. A converter between the two encodings is the real fix. 20 output sections and 1,698 rows recovered; independently verified zero lost across all 289 files.

The 96 deletions are corrections: 70 are confluence fields the parser flattened out of comment: { ...buildContentEntityFields(), parent: {…} } and published as top-level trigger outputs — they reappear nested under their parent, and the source confirms the nesting.

Devin tool names. All 11 Devin tools had their id as their name, so docs rendered ### list_session_messages. Audited all 4,427 tools — Devin was the only one. Fixed at the source; zero id-shaped names remain.

ship skill → check:audits. It had a third hand-maintained copy of the audit list, five behind package.json (cron-parity, import-specifiers, sql-date-binding, trigger-block-cycle, native-typecheck never ran when shipping).

Vite 8 deprecation warnings. Five warnings on every test run, all from @vitejs/plugin-react@4 being pre-rolldown on Vite 8 — it sets deprecated options unconditionally, so no config change suppresses them. ⚠️ Requires @vitejs/plugin-react ^4.3.4 → ^6.0.5 (v6 is plugin-react-oxc merged back; plugin-react-oxc itself doesn't support Vite 8). v6 drops Babel support — verified the repo calls react() with no options and has no Babel plugins. Full suite before/after: identical counts (20,415 passed / 30 skipped), 128.6s vs 129.8s — no speedup, and I'm not claiming one. plugin-react does little in an environment: 'node' run.

Generator: 4,306 → 3,870 lines, every step verified byte-identical against the 289-file manifest.

Measured CI result

Repo audits step
21 sequential steps (before) ~31s
first parallel attempt 39.2s (a regression — one audit's bunx call serialized the batch)
now 8s

Lint and Test job: 373s → 220s (−41%).

@waleedlatif1
waleedlatif1 requested a review from a team as a code owner August 7, 2026 01:01
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Aug 7, 2026 2:12am

Request Review

@cursor

cursor Bot commented Aug 7, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Large CI and docs-regeneration surface area; behavior is intentionally stricter in CI for tool-dependent tests. No auth or payment logic, but a wrong audit list or skipped security tests on runners would weaken gates.

Overview
CI and ship workflow replace ~20 sequential audit steps with a single bun run check:audits driven by scripts/run-audits.ts, which derives the audit list from package.json (closing drift with ship skills and CI). Audits run concurrently with failure replay; spawning scripts directly avoids nested bun run overhead. The dead Codecov upload step is removed.

Tests add @sim/testing/environment guards so suites that shell out to real python3 (≥3.12 for PEP 701 f-strings) and ripgrep skip locally with a clear reason but fail in CI if tools are missing.

Docs generation fixes MDX breakage from unescaped angle brackets in tool prose, reads trigger config and outputs from the evaluated registry (with a converter for nested trigger output shapes), and renders action headings from tool display names (e.g. ### A2A Send Message instead of ids)—regenerating hundreds of integration MDX pages accordingly. Devin tool metadata is corrected so names are not raw ids.

Reviewed by Cursor Bugbot for commit 19cd7ea. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR parallelizes repository audits, makes tool-dependent tests fail safely in CI while skipping unsupported local environments, and refactors documentation generation to use evaluated registry metadata.

  • Consolidates independent repository audits into a concurrent runner and removes redundant workflow steps.
  • Requires Python 3.12 for tests exercising PEP 701 syntax and checks for ripgrep where needed.
  • Sources trigger configuration and outputs from registry data, escapes generated prose, and regenerates integration documentation.
  • Corrects Devin display names and updates the Vite React plugin.

Confidence Score: 5/5

The PR appears safe to merge.

The prior Python-version issue is resolved: the guard now requires Python 3.12, all tests implicated by that thread invoke it before executing the affected syntax, and unsupported CI environments fail rather than silently skipping coverage.

Important Files Changed

Filename Overview
packages/testing/src/environment/index.ts Introduces memoized Python 3.12 and ripgrep availability guards, with explicit local skips and CI failures; the previously reported Python-version floor is fixed.
scripts/run-audits.ts Runs independent zero-argument repository audits concurrently while preserving failure output and status.
scripts/generate-docs.ts Refactors documentation generation around evaluated registry metadata and normalizes trigger-output nesting.
.github/workflows/test-build.yml Replaces sequential audit steps with the consolidated audit runner and removes the inactive coverage upload.
apps/sim/lib/execution/code-placeholders/compiler.test.ts Guards tests that execute Python 3.12-only generated syntax using the corrected environment check.

Reviews (2): Last reviewed commit: "refactor(ci): read package.json once in ..." | Re-trigger Greptile

Comment thread packages/testing/src/environment/index.ts Outdated
Comment thread packages/testing/src/environment/index.ts Outdated
The 21 independent audits ran as 21 sequential CI steps, each a single-threaded
read-only walk of the tree. scripts/run-audits.ts runs them concurrently:
28s serial -> 5.0s wall locally at 13-way. It buffers each audit's output and
replays only failures, so a green run stays quiet and a red one still names the
audit and shows why. Audits needing a git base ref (block registry, migration
safety) or that write files (drizzle generate) stay as their own steps.

Also fixes 5 tests that fail for every macOS dev and are invisible in CI. They
shell out to python3 using `match` statements and 3.12 f-string nesting, which
need >= 3.10; stock macOS ships 3.9.6, so `bun run test` produced raw Python
SyntaxErrors with no guard and nothing tying them to a missing tool. One also
needs ripgrep, which CI installs and a Mac usually does not.

@sim/testing/environment detects both and the tests skip with a reason via
vitest's ctx.skip(). Under CI it throws instead: these suites deliberately run
the real helper rather than a mock -- the cloud-review path/read-size bounds and
the placeholder compiler's generated Python are only observable that way -- so a
missing tool in CI means a security boundary silently stopped being covered,
which is worse than a red build.

Drops the Codecov upload. The workflow already documented it as a dead path:
nothing generates apps/sim/coverage, vitest runs without --coverage, and
fail_ci_if_error hides it, so it reported green having uploaded nothing.
…ializing the batch

Two review findings, both real.

MIN_PYTHON was 3.10, chosen for the `match` statements the compiler suite
generates. But two of the three guarded tests also use PEP 701 f-strings --
reusing the outer quote, and embedding `#` -- which are 3.12. Verified on a real
3.11 interpreter: the match-guard test passes, the other two fail with
`f-string: unmatched '('` and `f-string expression part cannot include '#'`,
which is exactly the raw SyntaxError the guard exists to prevent. A 3.10 floor
let them through and failed anyway.

The audit parallelization did not speed CI up -- it slowed it down. Serially the
21 audits took ~31s; concurrently the batch took 39.2s wall, because
check:desktop-bridge went from 1s to 39.2s and became the entire wall clock while
the other 20 finished in 9s. It is the only audit that shells out through `bunx`,
which re-resolves the package against the shared install cache -- a network-backed
sticky-disk mount on CI. Cheap when it runs alone, serialized behind the others
when they run together. Spawning the resolved compiler entry point directly
removes that layer.

Verified the audit still fails on a breaking bridge change rather than passing
faster by doing less.
…istry

The docs build has been failing on staging since the Smartlead merge:

  ./apps/docs/content/docs/en/integrations/smartlead.mdx
  Expected a closing tag for `<original>` before the end of `paragraph`

Tool descriptions are emitted as prose, and that path escaped only braces --
every table-cell path already escaped angle brackets. MDX reads `<` as the start
of a JSX tag, so a description like 'The copy is named "<original> - copy"' fails
the build outright. escapeMdxProse handles the MDX-hostile characters and leaves
pipes, parens and brackets alone, which are legal in prose and whose escaping
would mangle markdown links.

Trigger configuration now comes from the evaluated registry instead of regex over
source. Static parsing silently dropped every field whose builder assembled its
array imperatively or took a description as a parameter -- all ten Jira triggers
lost `webhookSecret` and `jqlFilter` that way, and Monday lost its config too, so
regenerating the docs was destructive. Reading real objects also deletes 232 lines
of parsing. Note `required` may be a condition object rather than `true`; only an
unconditional `true` renders as Required, matching the previous behavior.

Tool headings now show the tool's name ("A2A Send Message") rather than its id
(`a2a_send_message`), unformatted, across 241 generated pages. Names come from
tools/generated/tool-metadata.ts, which CI keeps in sync. These headings feed each
page's table of contents. a2a.mdx is hand-written, so its headings were updated
directly.

Also consolidates five hand-inlined copies of the escape chain into the
escapeMdxCell that already existed, and drops 44 comments that restated the line
below them. Generator: 4306 -> 4069 lines.

Every refactor step was verified against a golden manifest of all 289 generated
files -- proven deterministic across runs and proven to catch a one-character
change -- so the only output differences are the intended ones.

KNOWN GAP: extractTriggerOutputs still parses source and has the same blind spot;
it already drops one Jira output section on main. Regenerating is now safe for
trigger config but still lossy for trigger outputs.
@waleedlatif1 waleedlatif1 changed the title perf(ci): parallelize the repo audits and guard env-dependent tests perf(ci): parallelize repo audits, guard env-dependent tests, and fix the docs generator Aug 7, 2026
Review pass over the audit runner and the tool guards.

The audit list was hand-maintained alongside package.json with nothing linking
them, and it had already drifted: check:cron-parity exists, passes, and ran in no
CI step at all. The list is now derived from the check:* scripts with an explicit
exclusion map, so a new audit is opted out deliberately rather than forgotten.
That picks up cron-parity — 22 audits now, not 21.

check-realtime-prune-graph.ts still shelled out through `bunx turbo`, the same
pattern that took the bridge audit from 1s to 39s once the audits ran
concurrently. Both now go through scripts/local-bin.ts, which resolves
node_modules/.bin — the same path check:native-typecheck asserts is the native
TypeScript 7 compiler, so the one guarded path is the one that runs.

Audits are spawned as their script rather than `bun run <name>`, which started a
bun process only to read package.json and start a second one.

Tool detection is memoized per process; it was re-spawning python3 on each of the
5 call sites, in every vitest worker. The CI throw is deliberately NOT memoized —
memoizing it would turn every call after the first into a silent skip, which is
the failure mode the guard exists to prevent. Verified it still throws for all
three guarded tests, not just the first.

Also: dropped the environment module from the @sim/testing barrel so
node:child_process stays out of unrelated consumers' module graphs, restored the
per-audit reporting the 21 separate steps used to give (collapsible groups, error
annotations, and a timing table they never had), and trimmed comments that
restated their code or duplicated the runner's own docs.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Pushed a review pass (21a80bd1b) after running the diff through reuse / simplification / efficiency / altitude review. Three of the findings were real problems, not style:

The audit list had already drifted. It was hand-maintained alongside package.json with nothing linking them — and check:cron-parity exists, passes, and ran in no CI step at all. The list is now derived from the check:* scripts with an explicit exclusion map, so a new audit is opted out deliberately instead of forgotten. That picks up cron-parity: 22 audits now, not 21.

The bunx cliff wasn't fully fixed. check-realtime-prune-graph.ts still shelled out through bunx turbo — the same pattern that took the bridge audit from 1s to 39s once these started running concurrently, and it's in the same batch. Both now go through scripts/local-bin.ts, which resolves node_modules/.bin — the same path check:native-typecheck asserts is the native TS7 compiler, so the guarded path is the one that runs.

Memoizing the tool probe could have weakened the CI gate. Detection is now cached per process (it was re-spawning python3 at each of 5 call sites, in every vitest worker), but the CI throw is deliberately not memoized — that would turn every call after the first into a silent skip, exactly the failure mode the guard exists to prevent. Verified it still throws for all three guarded tests.

Also restored the per-audit reporting the 21 separate steps used to give: collapsible ::group:: output, ::error:: annotations, and a per-audit timing table in the job summary that the separate steps never had.

Skipped two findings: resolving the native tsc binary directly to skip its JS shim (couples to typescript's internal layout for ~50ms), and longest-first audit ordering (bakes in timing assumptions that drift, and the tail is much smaller now that the bunx calls are gone).

Also folded in the docs work from #6360 — the MDX build fix and the generator cleanup — so it all lands together.

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This is a clean review pass — all three substantive findings are addressed correctly, and the design decisions hold up well.

Derived audit list (run-audits.ts): The exclusion map with documented reasons is the right pattern. check:api-validation being excluded with an explicit note that its :strict sibling does run is exactly the kind of context that prevents someone from removing the exclusion thinking it's an oversight. Picked up cron-parity as advertised.

bunx cliff fix: Both check-desktop-bridge-contract.ts and check-realtime-prune-graph.ts now go through localBin, and the localBin comment tying it to check:native-typecheck's invariant is a good self-enforcing constraint — the one guarded path is the one both audits actually use.

Memoization design: The split between memoizing detect() but not the throw is correct and the code clearly expresses the intent:

result ??= detect()   // spawn once
if (result.ok) return true
if (process.env.CI) {
  throw new Error(...)  // re-throws on every call — NOT memoized
}

One minor thing worth noting in run-audits.ts: package.json is parsed twice — once inside auditScripts() and again at the outer scope for commands. Not a correctness issue and the wall-clock impact is negligible, but if you want to tighten it, auditScripts() could accept the already-parsed scripts map rather than re-reading the file.

Everything else looks good — the ::group:: / ::error:: / GITHUB_STEP_SUMMARY reporting is a nice net improvement over the 21 separate steps, and dropping the environment module from the barrel export is the right call for keeping node:child_process scoped.

Every Devin tool had its id as its `name` (`list_session_messages`), so the
generated docs rendered `### list_session_messages` where every other integration
renders a human name. It was the only integration doing this -- 11 of 4427 tools.

Names take the service prefix, matching the majority convention (3200 of 4416
names start with their service).

Also points the ship skill at check:audits instead of hand-listing the audits.
That copy had drifted five behind package.json: cron-parity, import-specifiers,
sql-date-binding, trigger-block-cycle and native-typecheck were all missing, so
shipping never ran them. It was the third copy of that list; there is now one.
Closes the gap left by the config fix: extractTriggerOutputs still parsed source,
so triggers whose outputs come from a builder call lost their tables. jira_webhook
had no output section at all.

The registry was not a drop-in, which is why the naive swap deleted 10,298 lines
earlier. The two sides encode nesting differently. A TriggerOutput marks a group
by OMITTING type and holding children as sibling keys:

  issue: { id: { type: 'number' }, title: { type: 'string' } }

while the renderer walks the JSON-Schema-ish shape the parser used to synthesize:

  issue: { type: 'object', properties: { id: …, title: … } }

formatOutputStructure only descends into .properties, so handing it the raw
registry value collapsed every nested group to one untyped row and dropped its
children. normalizeTriggerOutputs converts between the two, preserving leaves
that already declare properties/items and merging the 13 hybrid nodes that carry
both a type and inline children.

Measured across all 368 triggers before changing anything: 155 identical, 213
divergent, and the divergence was purely the nesting encoding — no node has a
non-string type, and a group never carries its own string description, so
leaf-vs-group classification is unambiguous. That is what makes a nested property
literally named 'description' (42 of them) survive.

Deletes the static path: extractTriggerOutputs, resolveTriggerBuilderFunction,
resolveTriggerOutputsConstant, readTriggerSiblingModules,
getWebhookProviderConstants, plus resolveConstStringValue and matchQuotedProperty
which the config fix had already stranded.

20 output sections recovered (linear 79->93, tiktok 6->11, jira 44->45) and 1698
rows. Verified independently: zero sections lost across all 289 generated files,
no file lost rows, output deterministic across regeneration.

The 96 deletions are all corrections, not losses. 70 are confluence fields the
parser flattened out of `comment: { ...buildContentEntityFields(), parent: {…} }`
and rendered as top-level trigger outputs; they reappear nested under their
parent in the same hunk. 8 are greenhouse key ordering, 6 are intercom
descriptions the parser had dropped, 1 is a vercel row moving position.

Generator: 4069 -> 3903 lines.
…nfig

@vitejs/plugin-react v4 targets pre-rolldown Vite: it sets `esbuild.jsx`
and `optimizeDeps.rollupOptions`, both deprecated under Vite 8's oxc
pipeline, and self-reports that plugin-react-oxc should be used instead.
v6 is that plugin merged back under the original name — it requires Vite
^8, drops Babel entirely, and emits none of those options.

Vite 8 also resolves tsconfig paths natively, so vite-tsconfig-paths is
replaced by `resolve.tsconfigPaths`.

Full apps/sim suite unchanged: 1483 passed / 2 skipped files,
20415 passed / 30 skipped tests.
Second pass over the generator, e.g. `// Copy icons from sim app to docs app`
above `copyIconsFile()`. Kept the multi-line runs (those carry reasoning), the
ones with concrete examples, and the one marking a deliberate empty catch.

Verified byte-identical output across all 289 generated files.
Generator: 3903 -> 3870 lines, 4306 at the start of this branch.
auditScripts() re-read the manifest the module body had already loaded.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

Pushed four more commits. Two close gaps I'd flagged as open, two came from review.

The trigger-outputs gap is closed (1bce1a46c). The registry wasn't a drop-in — it and the old parser encode nesting differently, which is why the naive swap deleted 10,298 lines earlier. A TriggerOutput marks a group by omitting type; the renderer walks a synthesized {type, properties} shape. A converter between the two is the actual fix. 20 output sections and 1,698 rows recovered, independently verified zero lost across all 289 generated files. The 96 deletions are corrections — 70 are confluence fields the old parser flattened out of a nested group and published as top-level outputs.

Devin tool names (3520afe2f). All 11 had their id as their name. Audited all 4,427 tools; Devin was the only integration doing it.

Vite 8 warnings (7b5b70f32) — flagging the one risky bit: this needs @vitejs/plugin-react ^4 → ^6, because v4 sets deprecated options unconditionally on Vite 8 and no config change suppresses them. v6 drops Babel support; verified the repo calls react() with no options and has no Babel plugins. Full suite before/after is identical (20,415 passed / 30 skipped) and timing is flat — no speedup, not claiming one. Also worth knowing: 6.0.5 cleared the repo's 7-day minimumReleaseAge gate by 0.7 days.

And the thing I got wrong earlier is now measured right. I reported the audit parallelization as a 5.8x win from my laptop (13-way, warm cache). On CI's 8 vCPU with cold I/O it was actually a regression — 31s serial → 39.2s — because one audit's bunx call serialized the whole batch. Fixed, and CI now says 8s. Lint and Test overall: 373s → 220s.

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread .github/workflows/test-build.yml

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 21dbe2f. Configure here.

People Data Labs declared `pdl_*` tool ids under `tools/peopledatalabs/`. Every
other integration names the directory after its id prefix -- 259 of 260 before
this, and PDL was the only exception.

The docs generator locates a tool's definition by deriving the directory from the
id prefix, so it looked in `tools/pdl/`, found nothing, and returned null for all
11 tools. peopledatalabs.mdx rendered eleven bare `###` headings with no
description, no Input table and no Output table.

Renaming the directory rather than the ids: tool ids are persisted in saved
workflows, so renaming those would break existing users. The directory is
internal -- 15 files' imports.

Fixed at the source rather than teaching the generator a fallback. A special case
would have left the invariant broken and the next integration free to break it
again; now 260 of 260 hold, and the generator needs no exception.

peopledatalabs.mdx: 11 empty headings -> 456 lines. Repo-wide: zero pages with an
empty action body.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

One more, from a real gap you'd never see in the diff: peopledatalabs.mdx was rendering eleven bare ### headings with no description, no Input table and no Output table.

Root cause: PDL declared pdl_* tool ids under tools/peopledatalabs/. The generator locates a tool's definition by deriving the directory from the id prefix, so it looked in tools/pdl/, found nothing, and returned null for all 11.

I fixed the naming rather than the generator. My first instinct was a fallback that indexes what each file actually declares — but that would have left the invariant broken and let the next integration break it again. Measured it first: 259 of 260 tool directories already name themselves after their id prefix. PDL was the sole exception, so it's a violation to correct, not a rule to soften. Now 260 of 260, and the generator needs no special case.

Renamed the directory, not the ids — tool ids are persisted in saved workflows, so renaming those would break existing users. The directory is internal: 15 files' imports.

peopledatalabs.mdx: 11 empty headings → 456 lines. Repo-wide: zero pages with an empty action body. Verified after the rename: type-check green across all 23 workspaces, 22 audits pass, and the 2,824 tool/block tests pass.

Worth a reviewer's eye: PDL Person Identify has two params (email, school) rendering as "No description" — that's a genuine gap in the tool definition, not the generator. Left alone as out of scope.

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
waleedlatif1 merged commit 1c0e82a into staging Aug 7, 2026
22 of 23 checks passed
@waleedlatif1
waleedlatif1 deleted the ci-parallel-audits branch August 7, 2026 02:08
@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The fix is correct and the reasoning is sound. Renaming the directory to match the pdl_* id prefix restores the invariant cleanly — the generator's derivation logic is a contract, and tools/peopledatalabs/ was the only implementation violating it. A fallback would have been the wrong tradeoff: it makes the contract implicit, which means the next integration can silently break it again with no audit catching it.

The rename-not-the-ids call is the right one too. Tool ids are external state (persisted in saved workflows); directory paths are internal. 15 import sites is an acceptable churn for a correctness fix.

The PDL Person Identify missing descriptions for email and school — worth a follow-up ticket since email is a primary lookup key and its description is the first thing a user reads in the docs, but agree it's out of scope here.

LGTM on this piece.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 19cd7ea. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant