From 232fe7dbaa5bff1926af3ca4e469e31536e5a4e9 Mon Sep 17 00:00:00 2001 From: dakshcodez Date: Sat, 15 Aug 2026 18:41:55 +0530 Subject: [PATCH 1/2] Always post a PR comment, even when the Action finds nothing to do Root-caused a real user report ("workflow ran successfully but the README didn't change") against a real PR: dakshcodez/gofiber- starter-project#1. That repo is 100% Go, and docmend only parses TypeScript/TSX/JavaScript/Python - parseCodebase() correctly found zero chunks, so the pipeline correctly found nothing stale, but never said so anywhere the PR author could see. Two early-return paths in index.ts's run() only logged via core.info (visible in the Action's own run logs, not on the PR) and exited silently - from the PR's perspective: green checkmark, total silence, indistinguishable from "didn't run at all." This also directly matches the original design brief, which called for a comment on every triggering PR, even the all-clear case ("3 sections verified accurate...") - not just when something needed fixing. Added postStatusComment (comment.ts) for the simple early-exit cases, and restructured run() to always post something: - Zero parseable code in the repo at all -> names which languages are actually supported, rather than looking identical to "your docs are fine." - No relevant file changes (all excluded via .docmendignore) -> says so. - Changes detected but nothing stale -> distinguishes "nothing here is linked to any doc" from "N sections checked, all still accurate" - meaningfully different situations that both used to produce identical silence. Verified against the actual reported scenario, not a synthetic stand-in: rebuilt a Go-only fixture matching the real PR's repo composition, confirmed parseCodebase() genuinely returns zero chunks for it (the real trigger condition), and confirmed postStatusComment() posts the correct, specific message through a mocked octokit. Separately: Go support itself (or any language beyond the current four) is a real feature gap, not something this commit addresses - would need a new tree-sitter grammar, WASM build, and extraction queries, comparable in scope to how TS/JS/Python were originally added. Flagged for the user to prioritize separately. --- packages/action/dist/index.js | 25 +++++++++++++++++++++++++ packages/action/src/comment.ts | 13 +++++++++++++ packages/action/src/index.ts | 22 +++++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/action/dist/index.js b/packages/action/dist/index.js index a052b4e..538165c 100644 --- a/packages/action/dist/index.js +++ b/packages/action/dist/index.js @@ -204546,6 +204546,16 @@ async function applyCorrectionToRepo(cwd, graph, sectionId, correctedContent) { } // src/comment.ts +async function postStatusComment(octokit, ctx, message) { + await octokit.rest.issues.createComment({ + owner: ctx.owner, + repo: ctx.repo, + issue_number: ctx.prNumber, + body: `## Doc Check Results + +${message}` + }); +} async function postSummaryComment(octokit, ctx, summary2) { const verifiedAccurate = summary2.verdicts.filter((verdict) => !verdict.stale).length; const autoFixed = summary2.fixPr?.appliedSectionIds.length ?? 0; @@ -204678,6 +204688,7 @@ function loadPrContext() { } // src/index.ts +var SUPPORTED_LANGUAGES = "TypeScript, TSX, JavaScript, and Python"; async function run2() { const config = loadConfig(); const ctx = loadPrContext(); @@ -204691,6 +204702,17 @@ async function run2() { llm, embeddingIndexPath: join4(cwd, ".docmend", "vectra-index") }); + if (chunks.length === 0) { + info2("docmend: no parseable code found in this repository."); + setOutput("stale-sections-found", 0); + setOutput("corrections-generated", 0); + await postStatusComment( + octokit, + ctx, + `No parseable code found in this repository - docmend currently supports ${SUPPORTED_LANGUAGES}. Nothing to check.` + ); + return; + } const changedFiles = await getDiffBetweenRefs(cwd, ctx.baseSha, ctx.headSha); const ignorePatterns = await loadIgnorePatterns(cwd); const relevantFiles = changedFiles.filter((file) => !isIgnored(file.path, ignorePatterns)); @@ -204698,6 +204720,7 @@ async function run2() { info2("docmend: no relevant changes in this PR."); setOutput("stale-sections-found", 0); setOutput("corrections-generated", 0); + await postStatusComment(octokit, ctx, "No relevant file changes in this PR. Nothing to check."); return; } const fileChanges = await resolveFileChanges( @@ -204711,6 +204734,8 @@ async function run2() { if (staleVerdicts.length === 0) { info2("docmend: docs look accurate for this PR."); setOutput("corrections-generated", 0); + const message = suspects.length === 0 ? `None of the changed files in this PR are referenced by any documentation section (or none are in a supported language - docmend currently supports ${SUPPORTED_LANGUAGES}). Nothing to check.` : `${suspects.length} linked documentation section(s) checked - all still accurate, nothing to fix.`; + await postStatusComment(octokit, ctx, message); return; } const repairResults = await repairStaleDocs(suspects, verdicts, llm, { diff --git a/packages/action/src/comment.ts b/packages/action/src/comment.ts index fd0acbd..e6b5895 100644 --- a/packages/action/src/comment.ts +++ b/packages/action/src/comment.ts @@ -11,6 +11,19 @@ export interface CommentSummary { fixPrError?: string; } +export async function postStatusComment( + octokit: InstanceType, + ctx: PrContext, + message: string, +): Promise { + await octokit.rest.issues.createComment({ + owner: ctx.owner, + repo: ctx.repo, + issue_number: ctx.prNumber, + body: `## Doc Check Results\n\n${message}`, + }); +} + export async function postSummaryComment( octokit: InstanceType, ctx: PrContext, diff --git a/packages/action/src/index.ts b/packages/action/src/index.ts index 046c1e8..ab236a3 100644 --- a/packages/action/src/index.ts +++ b/packages/action/src/index.ts @@ -14,11 +14,13 @@ import { repairStaleDocs, resolveFileChanges, } from '@docmend/core'; -import { postSummaryComment } from './comment.js'; +import { postStatusComment, postSummaryComment } from './comment.js'; import { loadConfig } from './config.js'; import { createFixPr } from './fix-pr.js'; import { loadPrContext } from './pr-context.js'; +const SUPPORTED_LANGUAGES = 'TypeScript, TSX, JavaScript, and Python'; + async function run(): Promise { const config = loadConfig(); const ctx = loadPrContext(); @@ -35,6 +37,18 @@ async function run(): Promise { embeddingIndexPath: join(cwd, '.docmend', 'vectra-index'), }); + if (chunks.length === 0) { + core.info('docmend: no parseable code found in this repository.'); + core.setOutput('stale-sections-found', 0); + core.setOutput('corrections-generated', 0); + await postStatusComment( + octokit, + ctx, + `No parseable code found in this repository - docmend currently supports ${SUPPORTED_LANGUAGES}. Nothing to check.`, + ); + return; + } + const changedFiles = await getDiffBetweenRefs(cwd, ctx.baseSha, ctx.headSha); const ignorePatterns = await loadIgnorePatterns(cwd); const relevantFiles = changedFiles.filter((file) => !isIgnored(file.path, ignorePatterns)); @@ -43,6 +57,7 @@ async function run(): Promise { core.info('docmend: no relevant changes in this PR.'); core.setOutput('stale-sections-found', 0); core.setOutput('corrections-generated', 0); + await postStatusComment(octokit, ctx, 'No relevant file changes in this PR. Nothing to check.'); return; } @@ -60,6 +75,11 @@ async function run(): Promise { if (staleVerdicts.length === 0) { core.info('docmend: docs look accurate for this PR.'); core.setOutput('corrections-generated', 0); + const message = + suspects.length === 0 + ? `None of the changed files in this PR are referenced by any documentation section (or none are in a supported language - docmend currently supports ${SUPPORTED_LANGUAGES}). Nothing to check.` + : `${suspects.length} linked documentation section(s) checked - all still accurate, nothing to fix.`; + await postStatusComment(octokit, ctx, message); return; } From 2b1ddad47ff5494f3cf96f700f0c04bd41821e77 Mon Sep 17 00:00:00 2001 From: dakshcodez Date: Sat, 15 Aug 2026 18:54:19 +0530 Subject: [PATCH 2/2] Fix issues found by subagent review of the silent-success PR - index.ts: moved the chunks.length === 0 check to right after parseCodebase(), before parseDocs()/buildLinkGraph() run at all. buildEmbeddingLinks() still calls llm.embed() once per doc section regardless of how many code chunks exist to link against - so the previous ordering meant every run of the exact scenario this PR targets (a repo docmend can't parse) burned N Gemini embedding calls before reporting "nothing to check." Checking first avoids that entirely. - index.ts: reworded both "nothing to check" messages that implied a confident "your language isn't supported" diagnosis. Real gap found during review: packages/core/src/parsing/queries.ts only captures function_declaration/class_declaration/method_definition - not arrow functions. A fully-supported TS/JS/React repo written idiomatically (`const Foo = () => {...}`, extremely common for components and hooks) can legitimately produce zero chunks despite being a supported language, which would have made the original wording actively wrong, not just unhelpfully silent. Messages now acknowledge the ambiguity (unsupported language vs. a code pattern not yet recognized) instead of confidently blaming the language. Found by a fresh subagent review spawned after PR #16 was opened. Verified the reordering doesn't change any other behavior (full lint/typecheck/build still pass; the early-return still fires on the same chunks.length === 0 condition, just before the now-skipped parseDocs/buildLinkGraph calls instead of after). Not fixed here, flagged as a separate, likely higher-priority gap than adding a new language: the underlying arrow-function parsing gap itself. Any TS/JS/React codebase built primarily on arrow functions is currently under-covered by docmend's own "supported" languages, not just genuinely-unsupported ones like Go. --- packages/action/dist/index.js | 17 +++++++++-------- packages/action/src/index.ts | 22 ++++++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/action/dist/index.js b/packages/action/dist/index.js index 538165c..5b6a255 100644 --- a/packages/action/dist/index.js +++ b/packages/action/dist/index.js @@ -204695,13 +204695,8 @@ async function run2() { const cwd = process.cwd(); const octokit = getOctokit(config.githubToken); const llm = new GeminiClient({ apiKey: config.apiKey }); - info2("docmend: building code-to-docs link graph..."); + info2("docmend: parsing codebase..."); const chunks = await parseCodebase(cwd); - const sections = await parseDocs(cwd); - const graph = await buildLinkGraph(chunks, sections, { - llm, - embeddingIndexPath: join4(cwd, ".docmend", "vectra-index") - }); if (chunks.length === 0) { info2("docmend: no parseable code found in this repository."); setOutput("stale-sections-found", 0); @@ -204709,10 +204704,16 @@ async function run2() { await postStatusComment( octokit, ctx, - `No parseable code found in this repository - docmend currently supports ${SUPPORTED_LANGUAGES}. Nothing to check.` + `No functions, classes, or methods were detected in this repository. docmend currently parses ${SUPPORTED_LANGUAGES}, but doesn't yet recognize every code pattern (e.g. arrow-function-only declarations) - this could mean an unsupported language, or supported code docmend doesn't fully cover yet. Nothing to check.` ); return; } + info2("docmend: building code-to-docs link graph..."); + const sections = await parseDocs(cwd); + const graph = await buildLinkGraph(chunks, sections, { + llm, + embeddingIndexPath: join4(cwd, ".docmend", "vectra-index") + }); const changedFiles = await getDiffBetweenRefs(cwd, ctx.baseSha, ctx.headSha); const ignorePatterns = await loadIgnorePatterns(cwd); const relevantFiles = changedFiles.filter((file) => !isIgnored(file.path, ignorePatterns)); @@ -204734,7 +204735,7 @@ async function run2() { if (staleVerdicts.length === 0) { info2("docmend: docs look accurate for this PR."); setOutput("corrections-generated", 0); - const message = suspects.length === 0 ? `None of the changed files in this PR are referenced by any documentation section (or none are in a supported language - docmend currently supports ${SUPPORTED_LANGUAGES}). Nothing to check.` : `${suspects.length} linked documentation section(s) checked - all still accurate, nothing to fix.`; + const message = suspects.length === 0 ? "None of the changed code in this PR is referenced by any documentation section (or is in a language/pattern docmend doesn't recognize yet). Nothing to check." : `${suspects.length} linked documentation section(s) checked - all still accurate, nothing to fix.`; await postStatusComment(octokit, ctx, message); return; } diff --git a/packages/action/src/index.ts b/packages/action/src/index.ts index ab236a3..23d1f50 100644 --- a/packages/action/src/index.ts +++ b/packages/action/src/index.ts @@ -29,26 +29,32 @@ async function run(): Promise { const octokit = getOctokit(config.githubToken); const llm = new GeminiClient({ apiKey: config.apiKey }); - core.info('docmend: building code-to-docs link graph...'); + core.info('docmend: parsing codebase...'); const chunks = await parseCodebase(cwd); - const sections = await parseDocs(cwd); - const graph = await buildLinkGraph(chunks, sections, { - llm, - embeddingIndexPath: join(cwd, '.docmend', 'vectra-index'), - }); if (chunks.length === 0) { + // Checked before parseDocs/buildLinkGraph specifically to avoid burning + // an embed() call per doc section (buildEmbeddingLinks still embeds + // every section even with zero chunks to link against) on a run that's + // about to report there's nothing to do anyway. core.info('docmend: no parseable code found in this repository.'); core.setOutput('stale-sections-found', 0); core.setOutput('corrections-generated', 0); await postStatusComment( octokit, ctx, - `No parseable code found in this repository - docmend currently supports ${SUPPORTED_LANGUAGES}. Nothing to check.`, + `No functions, classes, or methods were detected in this repository. docmend currently parses ${SUPPORTED_LANGUAGES}, but doesn't yet recognize every code pattern (e.g. arrow-function-only declarations) - this could mean an unsupported language, or supported code docmend doesn't fully cover yet. Nothing to check.`, ); return; } + core.info('docmend: building code-to-docs link graph...'); + const sections = await parseDocs(cwd); + const graph = await buildLinkGraph(chunks, sections, { + llm, + embeddingIndexPath: join(cwd, '.docmend', 'vectra-index'), + }); + const changedFiles = await getDiffBetweenRefs(cwd, ctx.baseSha, ctx.headSha); const ignorePatterns = await loadIgnorePatterns(cwd); const relevantFiles = changedFiles.filter((file) => !isIgnored(file.path, ignorePatterns)); @@ -77,7 +83,7 @@ async function run(): Promise { core.setOutput('corrections-generated', 0); const message = suspects.length === 0 - ? `None of the changed files in this PR are referenced by any documentation section (or none are in a supported language - docmend currently supports ${SUPPORTED_LANGUAGES}). Nothing to check.` + ? 'None of the changed code in this PR is referenced by any documentation section (or is in a language/pattern docmend doesn\'t recognize yet). Nothing to check.' : `${suspects.length} linked documentation section(s) checked - all still accurate, nothing to fix.`; await postStatusComment(octokit, ctx, message); return;