Skip to content

ci: lint Markdown JS/TS code samples#66

Merged
flyingrobots merged 14 commits intomainfrom
feature/b87-markdown-code-lint
Mar 13, 2026
Merged

ci: lint Markdown JS/TS code samples#66
flyingrobots merged 14 commits intomainfrom
feature/b87-markdown-code-lint

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Mar 12, 2026

Summary

  • add scripts/lint-markdown-code-samples.js to extract fenced js/ts blocks from Markdown and syntax-check them with line-accurate diagnostics
  • wire npm run lint:md:code into the CI fast firewall after markdownlint
  • keep pseudocode-only snippets tagged as text while restoring valid executable examples to code-fence coverage

Test plan

  • npm run lint:md
  • npm run lint:md:code
  • npx vitest run test/unit/scripts/lint-markdown-code-samples.test.js
  • branch pre-push firewall passed (282 test files, 4981 tests)

ADR checks

  • This PR does not implement ADR 2 without satisfying ADR 3
  • If this PR touches persisted op formats, I linked the ADR 3 readiness issue
  • If this PR touches wire compatibility, I confirmed canonical-only ops are still rejected on the wire pre-cutover
  • If this PR touches schema constants, I confirmed patch and checkpoint namespaces remain distinct

Summary by CodeRabbit

  • New Features

    • Added a Markdown JS/TS code-sample linter with file/line-accurate diagnostics and npm run lint:md:code; CI now runs this linter before the runtime security audit.
  • Tests

    • Added comprehensive unit tests covering extraction, parsing, and diagnostics for Markdown code samples.
  • Documentation

    • Updated code-block languages, roadmap entries, and marked the code-sample linter milestone as completed.
  • Chores

    • Adjusted CI/test container behavior and updated pre-push gate ordering, messaging, and quick-mode behavior.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff537993-6afe-4d7e-afb4-da870e542b1d

📥 Commits

Reviewing files that changed from the base of the PR and between 764e4b7 and a3ee371.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • scripts/hooks/pre-push
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Adds a Markdown JS/TS code-sample linter CLI with tests, wires it into CI and local pre-push gates as npm run lint:md:code, renumbers CI/pre-push gates so the runtime security audit runs after the new lint gate, and updates docs/roadmap to mark the linter work completed.

Changes

Cohort / File(s) Summary
CI Workflow & Compose
/.github/workflows/ci.yml, docker-compose.test.yml
Inserted a CI gate to run npm run lint:md:code before the runtime security audit and adjusted gate numbering; changed Deno test flag to --node-modules-dir=manual.
Pre-push Hook
scripts/hooks/pre-push
Added lint:md and lint:md:code into parallel gates, renumbered gates (Gates 1–7 parallel, Gate 8 = unit tests), and updated quick-mode skip messaging.
NPM Scripts
package.json
Added lint:md:code script calling node scripts/lint-markdown-code-samples.js.
Linter Implementation
scripts/lint-markdown-code-samples.js
New Node CLI: discovers Markdown files, extracts JS/TS fenced blocks, parses them with TypeScript APIs, reports line-accurate diagnostics, emits extraction/formatting issues, and exports utilities.
Tests
test/unit/scripts/lint-markdown-code-samples.test.js
New comprehensive unit tests for fence parsing, sample extraction, file collection, per-sample linting, aggregation, and isolation.
Docs: Status & Roadmap
CHANGELOG.md, ROADMAP.md, docs/ROADMAP/COMPLETED.md
Documented B87 CODE SAMPLE LINTER as completed/merged on feature branch; updated backlog/wave ordering and narratives.
Docs: Code-Fence Language Tags
docs/ADR-001-Folds.md, docs/GUIDE.md, docs/design/...
Replaced several javascript fences with text (or adjusted languages) and made small formatting/code-sample tweaks to support the new linter.
Docker: Deno Image
docker/Dockerfile.deno
Added Node22 tooling stage, switched to npm ci workflow and removed prior Deno bootstrap; updated final test invocation.
Other docs/manifest tweaks
CHANGELOG.md (additional lines), various docs
Expanded CI/docs narrative about the Markdown linter, fence-edge-case handling, and gate ordering.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer
  participant FS as Filesystem
  participant CI as CI Workflow
  participant Lint as Markdown Linter (CLI)
  participant TS as TypeScript Parser

  Dev->>FS: push repo changes (linter + docs)
  CI->>FS: checkout repository
  CI->>Lint: run `npm run lint:md:code`
  Lint->>FS: collect Markdown files
  Lint->>TS: parse/extract fenced JS/TS blocks & request diagnostics
  TS-->>Lint: return diagnostics
  Lint-->>CI: report results (exit code)
  CI->>CI: if lint passed -> continue to security audit gate
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I nibble fences, sniffing JS and TS,

I parse each block with tidy paws,
I flag the gaps and mixed-marker mess,
So CI gates can hum without a pause,
A hopping linter, proud and small.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'ci: lint Markdown JS/TS code samples' clearly and concisely describes the main change: adding Markdown code-sample linting to the CI pipeline.
Description check ✅ Passed The PR description is well-structured with clear Summary, Test plan, and ADR checks sections that follow the template requirements and provide comprehensive details about the changes and validation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/b87-markdown-code-lint
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
docs/ADR-001-Folds.md (1)

122-128: Keep the View API example linted.

This block is valid JS/TS syntax, so changing it to text drops the new Markdown sample-lint coverage for what still reads like an executable example. Consider keeping this fence as ts (or javascript) and reserving text for the arrow-notation pseudocode block below.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ADR-001-Folds.md` around lines 122 - 128, The fenced code block showing
usage of graph.view and View methods (view.traverse, view.query,
view.renderAscii) should be marked as executable TS/JS so linters treat it as
code: change the fence language from "text" to "ts" or "javascript" for the
block containing const view = graph.view({ fold: { mode: "shallow", maxDepth: 1
} }); await view.traverse(...); await view.query(...); await
view.renderAscii(...); and keep the later arrow-notation pseudocode block as
"text".
scripts/lint-markdown-code-samples.js (1)

136-139: Accessing TypeScript internal parseDiagnostics property.

The parseDiagnostics property is not part of TypeScript's public API surface — it's an internal property on the SourceFile object. While this is a common pattern for accessing parse-only diagnostics without a full program, be aware it could break with future TypeScript major versions.

The fallback to || [] is a good defensive measure. Consider adding a brief comment noting the internal API usage for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/lint-markdown-code-samples.js` around lines 136 - 139, The code
currently reads parse-only diagnostics from an internal TypeScript SourceFile
property (parseDiagnostics) when building diagnostics for the diagnostics
variable; add a concise inline comment above this expression mentioning that
parseDiagnostics is an internal/non-public TS API and may change across
TypeScript major versions, keep the existing defensive fallback (|| []) and note
why we're using this internal access so future maintainers understand the risk
and can revisit if TS evolves; reference the parseDiagnostics property and the
sourceFile/diagnostics usage in your comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/ADR-001-Folds.md`:
- Around line 122-128: The fenced code block showing usage of graph.view and
View methods (view.traverse, view.query, view.renderAscii) should be marked as
executable TS/JS so linters treat it as code: change the fence language from
"text" to "ts" or "javascript" for the block containing const view =
graph.view({ fold: { mode: "shallow", maxDepth: 1 } }); await
view.traverse(...); await view.query(...); await view.renderAscii(...); and keep
the later arrow-notation pseudocode block as "text".

In `@scripts/lint-markdown-code-samples.js`:
- Around line 136-139: The code currently reads parse-only diagnostics from an
internal TypeScript SourceFile property (parseDiagnostics) when building
diagnostics for the diagnostics variable; add a concise inline comment above
this expression mentioning that parseDiagnostics is an internal/non-public TS
API and may change across TypeScript major versions, keep the existing defensive
fallback (|| []) and note why we're using this internal access so future
maintainers understand the risk and can revisit if TS evolves; reference the
parseDiagnostics property and the sourceFile/diagnostics usage in your comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 160df22b-251c-4e60-b44a-03b55b35c753

📥 Commits

Reviewing files that changed from the base of the PR and between c6ff228 and cb6a662.

📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • ROADMAP.md
  • docs/ADR-001-Folds.md
  • docs/GUIDE.md
  • docs/ROADMAP/COMPLETED.md
  • docs/design/MIGRATION_PROBLEM.md
  • docs/design/joinreducer-split.md
  • docs/design/warpgraph-decomposition.md
  • package.json
  • scripts/lint-markdown-code-samples.js
  • test/unit/scripts/lint-markdown-code-samples.test.js

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@flyingrobots
Copy link
Member Author

✅ Addressed in commits cb6a662 and 067186c.

  • restored the ADR folds view example to executable ts coverage by replacing the placeholder calls with valid syntax
  • added a unit test covering the documentation-style top-level await snippet pattern
  • documented the non-public parseDiagnostics access in the Markdown code-sample linter so the maintenance risk is explicit
  • updated the PR description to the repository template so the description check warning is cleared

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@flyingrobots: On it! 🥕 Kicking off the review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
docs/ADR-001-Folds.md (1)

122-128: Add a clarifying note that the View API methods are proposed and not yet implemented.

The code sample mixes implemented APIs (query(), match(), run()) with unimplemented ones (graph.view(), view.traverse(), view.renderAscii()). While the document status is marked "Proposed (Deferred)" in the header and section 3 is titled "API Surface (Proposed)", the code block itself lacks direct indication that these methods don't currently exist.

Consider adding a brief comment before the code block:

#### 3.1 View API

+<!-- Note: The following APIs are proposed and not yet implemented -->
 ```ts
 const view = graph.view({ fold: { mode: 'shallow', maxDepth: 1 } });

Alternatively, change the language tag from ts to text to avoid implying these are currently valid TypeScript APIs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ADR-001-Folds.md` around lines 122 - 128, The code sample mixes
proposed/unimplemented APIs (graph.view, view.traverse, view.renderAscii) with
implemented ones (query, match, run); update the documentation by adding a brief
inline note above the snippet stating that graph.view, view.traverse, and
view.renderAscii are proposed and not yet implemented, or alternatively change
the code block language tag from "ts" to "text" so it doesn't imply valid
TypeScript usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/lint-markdown-code-samples.js`:
- Around line 57-73: The fence detection only matches fences at column 1; update
the regex used when computing fenceMatch and the closePattern to allow up to
three optional leading spaces so indented fenced blocks are recognized.
Concretely, change the opening match (used to set activeFence with marker,
markerLength, language via parseFenceLanguage) to permit up to three leading
spaces before the backtick/tilde group, and build closePattern to also accept up
to three leading spaces before the marker repeat (preserving use of
activeFence.marker and activeFence.markerLength). Ensure the captured marker
(fenceMatch[1][0]) and language parsing still work with the adjusted regex.
- Around line 129-135: Replace the hardcoded ts.ScriptTarget.Latest when
creating sourceFile with the repository's configured target by loading the repo
tsconfig (tsconfig.base.json) compilerOptions.target, converting that target
string/number into the corresponding ts.ScriptTarget, and passing that value to
ts.createSourceFile; update the code around sourceFile / ts.createSourceFile
(which currently uses sample.language and scriptKind) to first read and parse
tsconfig.base.json (or use
ts.parseJsonConfigFileContent/ts.convertCompilerOptionsFromJson) to obtain
compilerOptions.target and map it to the ts.ScriptTarget enum before calling
ts.createSourceFile.

---

Nitpick comments:
In `@docs/ADR-001-Folds.md`:
- Around line 122-128: The code sample mixes proposed/unimplemented APIs
(graph.view, view.traverse, view.renderAscii) with implemented ones (query,
match, run); update the documentation by adding a brief inline note above the
snippet stating that graph.view, view.traverse, and view.renderAscii are
proposed and not yet implemented, or alternatively change the code block
language tag from "ts" to "text" so it doesn't imply valid TypeScript usage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ddeb8668-2ceb-4aed-a340-e9434894fc96

📥 Commits

Reviewing files that changed from the base of the PR and between cb6a662 and 067186c.

📒 Files selected for processing (3)
  • docs/ADR-001-Folds.md
  • scripts/lint-markdown-code-samples.js
  • test/unit/scripts/lint-markdown-code-samples.test.js

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@flyingrobots
Copy link
Member Author

✅ Addressed the failing test-deno check in commits 6e69a83 and eac13f6.

  • reproduced the failure locally with npm run test:deno
  • root cause was Deno re-resolving cbor-extract optional platform packages at runtime and tripping over a missing @cbor-extract/cbor-extract-win32-x64@2.2.2
  • fixed the Deno image to preinstall lockfile-backed node_modules with npm install
  • switched the Deno test command to --node-modules-dir=manual so runtime uses the preinstalled tree instead of drifting against npm registry metadata
  • re-ran npm run test:deno successfully after the fix

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@flyingrobots: On it! 🥕 Kicking off another review pass now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

@flyingrobots
Copy link
Member Author

✅ Addressed the remaining CodeRabbit review comments in commits e0bd918, 98839ad, and c6afcc5.

  • scripts/lint-markdown-code-samples.js now recognizes fenced code blocks indented by up to three spaces, matching Markdown fence rules more closely
  • the linter now derives its ts.ScriptTarget from tsconfig.base.json instead of hardcoding Latest
  • docs/ADR-001-Folds.md now explicitly marks the graph.view() snippet as proposed / not yet implemented
  • added regression coverage for indented fences and configured script-target resolution
  • verification: npx vitest run test/unit/scripts/lint-markdown-code-samples.test.js, npm run lint:md:code, npx markdownlint docs/ADR-001-Folds.md CHANGELOG.md, plus the full pre-push firewall on branch push

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/hooks/pre-push (1)

21-21: ⚠️ Potential issue | 🟡 Minor

Minor inconsistency in quick mode message.

Line 21 mentions "Gate 6 (unit tests)" but unit tests are now Gate 8 per lines 64-68. The message should reference Gate 8.

🐛 Proposed fix
-  echo "WARP_QUICK_PUSH: quick mode active — Gate 6 (unit tests) will be skipped"
+  echo "WARP_QUICK_PUSH: quick mode active — Gate 8 (unit tests) will be skipped"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-push` at line 21, The quick-mode status message in the
pre-push hook is outdated: update the echo string that prints "WARP_QUICK_PUSH:
quick mode active — Gate 6 (unit tests) will be skipped" so it references Gate 8
instead; locate the echo invocation in scripts/hooks/pre-push (the line emitting
WARP_QUICK_PUSH) and change "Gate 6" to "Gate 8" so the message matches the
actual gate numbering used later.
🧹 Nitpick comments (1)
scripts/lint-markdown-code-samples.js (1)

175-197: Consider adding symlink cycle protection.

The recursive traversal follows into directories without checking for symlinks, which could cause an infinite loop if the repo contains circular symlinks. This is an edge case, but since this script runs in pre-push hooks blocking developer workflow, a defensive guard would be prudent.

♻️ Optional fix using a visited set
 export function collectMarkdownFiles(startPath = root) {
+  const visited = new Set();
+  return collectMarkdownFilesRecursive(resolve(startPath), visited);
+}
+
+function collectMarkdownFilesRecursive(resolved, visited) {
-  const resolved = resolve(startPath);
+  const realPath = realpathSync(resolved);
+  if (visited.has(realPath)) {
+    return [];
+  }
+  visited.add(realPath);
   const stats = statSync(resolved);

This requires importing realpathSync from node:fs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/lint-markdown-code-samples.js` around lines 175 - 197, The recursive
collector collectMarkdownFiles currently follows directories without guarding
against symlink cycles; add a visited-paths set (use realpathSync to get
canonical paths) passed through recursion to skip directories whose realpath is
already seen, and check Dirent.isSymbolicLink() or compare realpaths before
recursing from the loop that calls collectMarkdownFiles(join(resolved,
entry.name)), ensuring you import realpathSync and thread the visited set
through initial and recursive calls so cycles are avoided.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/ADR-001-Folds.md`:
- Around line 122-129: The disclaimer currently calls out only graph.view(),
view.traverse(), and view.renderAscii() as not implemented; update the note to
state that the entire proposed API sketch is not implemented yet so readers
don't assume any part exists—explicitly mention graph.view, view.traverse,
view.renderAscii, and view.query (and any other sketched methods) in the
reworded sentence to make the scope clear.

In `@docs/ROADMAP/COMPLETED.md`:
- Line 399: The completed entry for B87 currently includes a self-expiring note
"pending merge" which will become inaccurate once the PR lands; update the B87
line in COMPLETED.md to use a stable status or remove the branch-state wording —
e.g., either omit "pending merge" entirely or replace it with a neutral phrase
like "awaiting merge" or "merged after review" depending on truth today; refer
to the B87 entry, branch name feature/b87-markdown-code-lint, and artifacts
scripts/lint-markdown-code-samples.js / npm run lint:md:code to ensure the
description remains correct and not time-sensitive.

---

Outside diff comments:
In `@scripts/hooks/pre-push`:
- Line 21: The quick-mode status message in the pre-push hook is outdated:
update the echo string that prints "WARP_QUICK_PUSH: quick mode active — Gate 6
(unit tests) will be skipped" so it references Gate 8 instead; locate the echo
invocation in scripts/hooks/pre-push (the line emitting WARP_QUICK_PUSH) and
change "Gate 6" to "Gate 8" so the message matches the actual gate numbering
used later.

---

Nitpick comments:
In `@scripts/lint-markdown-code-samples.js`:
- Around line 175-197: The recursive collector collectMarkdownFiles currently
follows directories without guarding against symlink cycles; add a visited-paths
set (use realpathSync to get canonical paths) passed through recursion to skip
directories whose realpath is already seen, and check Dirent.isSymbolicLink() or
compare realpaths before recursing from the loop that calls
collectMarkdownFiles(join(resolved, entry.name)), ensuring you import
realpathSync and thread the visited set through initial and recursive calls so
cycles are avoided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ac58aa5-beef-4c58-bc22-8ad2323ee25b

📥 Commits

Reviewing files that changed from the base of the PR and between 067186c and 218e821.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • ROADMAP.md
  • docker-compose.test.yml
  • docker/Dockerfile.deno
  • docs/ADR-001-Folds.md
  • docs/ROADMAP/COMPLETED.md
  • scripts/hooks/pre-push
  • scripts/lint-markdown-code-samples.js
  • test/unit/scripts/lint-markdown-code-samples.test.js
✅ Files skipped from review due to trivial changes (1)
  • docker-compose.test.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/unit/scripts/lint-markdown-code-samples.test.js

@flyingrobots
Copy link
Member Author

✅ Addressed in commits a66f783, 62c4fcf, and 764e4b7.

  • corrected the pre-push quick-mode message so it now references Gate 8, matching the actual unit-test gate numbering
  • broadened the ADR folds disclaimer so the entire graph.view() sketch, including view.query().match().run(), is clearly marked as proposed / not implemented yet
  • removed the self-expiring pending merge wording from the archived B87 entry in docs/ROADMAP/COMPLETED.md

@coderabbitai Thanks! One note on the symlink-cycle suggestion: this looks like a mix-up in Node's Dirent behavior. collectMarkdownFiles() only recurses when entry.isDirectory() is true. On a local repro with a symlinked directory, Node reported {"name":"linkdir","isDirectory":false,"isSymbolicLink":true}, so the walker does not follow directory symlinks and cannot recurse into a symlink loop via that path. Happy to revisit if you have a concrete reproducer.

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@flyingrobots: On it! 🥕 Kicking off the review now.

Thanks for the detailed explanation on the symlink behavior — that makes sense. Since entry.isDirectory() returns false for symlinked directories on Node's Dirent, the walker won't recurse into them, so no cycle can form via that path. I'll withdraw that suggestion.


✏️ Learnings added
Learnt from: flyingrobots
URL: https://github.com/git-stunts/git-warp/pull/66

Timestamp: 2026-03-12T21:07:34.621Z
Learning: In `scripts/lint-markdown-code-samples.js` (`collectMarkdownFiles()`), the directory walker recurses only when `entry.isDirectory()` is `true`. Node's `fs.Dirent` reports symlinked directories as `{ isDirectory: false, isSymbolicLink: true }`, so the walker does not follow directory symlinks and cannot recurse into a symlink cycle. The symlink-cycle concern is therefore not applicable to this implementation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@flyingrobots: Thanks for the thorough clarification on Dirent behavior — you're right. When fs.readdirSync is called with { withFileTypes: true }, the returned Dirent objects report the type of the directory entry itself, not its target. A symlinked directory yields isDirectory() === false and isSymbolicLink() === true, so collectMarkdownFiles() will not recurse into it and the symlink-cycle concern doesn't apply. I'll retract that suggestion and note this for future reviews.

Kicking off the review pass now! 🥕


✏️ Learnings added
Learnt from: flyingrobots
URL: https://github.com/git-stunts/git-warp/pull/66

Timestamp: 2026-03-12T21:07:44.393Z
Learning: In `scripts/lint-markdown-code-samples.js`, the `collectMarkdownFiles()` function uses `fs.readdirSync` with `{ withFileTypes: true }` and only recurses when `entry.isDirectory()` is true. Node.js `Dirent.isDirectory()` returns `false` for symbolic links even when they point to directories, so the walker does not follow directory symlinks and is not susceptible to symlink cycles. Do not flag symlink-cycle risk for this function.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: flyingrobots
Repo: git-stunts/git-warp PR: 55
File: src/domain/warp/checkpoint.methods.js:170-191
Timestamp: 2026-02-28T10:10:56.572Z
Learning: In `src/domain/warp/checkpoint.methods.js`, the `_loadLatestCheckpoint()` function uses four specific substring checks to distinguish "not found" errors from decode/corruption errors:
- `"missing"` — matches git cat-file errors on pruned/unreachable objects
- `"not found"` — matches readTree entry lookup failures
- `"ENOENT"` — matches filesystem-level missing path errors
- `"non-empty string"` — matches readRef/getNodeInfo validation errors with empty SHA
These patterns are documented in commit 9e48052 and are appropriate because the persistence adapter is internal with stable error strings.

Learnt from: flyingrobots
Repo: git-stunts/git-warp PR: 65
File: src/infrastructure/adapters/InMemoryGraphAdapter.js:427-437
Timestamp: 2026-03-08T12:36:24.197Z
Learning: In `src/infrastructure/adapters/InMemoryGraphAdapter.js`, the `logNodesStream()` method intentionally uses a dynamic `import('node:stream')` without a browser-safe fallback. This is by design because: (1) it is an infrastructure adapter method, not domain code; (2) materialization never calls it — it walks commits via `getNodeInfo()`/`readBlob()` directly; (3) in browser builds (Vite), `node:stream` is stubbed and the method is never invoked; (4) its only consumer is the CLI's `git log`-style output. Do not flag this as a browser-compatibility issue.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/hooks/pre-push (1)

54-60: Gate numbering diverges from CI workflow.

The pre-push hook gate numbers don't align with .github/workflows/ci.yml:

Check Pre-push CI
ESLint Gate 1 Gate 4
TypeScript Gate 2 Gate 1
IRONCLAD policy Gate 3 Gate 2
Consumer type surface Gate 4 Gate 3
Declaration surface Gate 5 Gate 5
Markdown lint Gate 6 Gate 6
Markdown code-sample Gate 7 Gate 7

When CI fails "Gate 1" (TypeScript), developers might incorrectly reference local "Gate 1" (ESLint). Consider aligning the numbering for consistent debugging experience.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/hooks/pre-push` around lines 54 - 60, The gate numbers in the
pre-push hook messages are misaligned with CI; update the wait error messages to
match the CI gate mapping by changing the reported gate numbers for the PID
variables so they correspond to .github/workflows/ci.yml (set $TC_PID to "Gate
1", $POLICY_PID to "Gate 2", $CONSUMER_PID to "Gate 3", $LINT_PID to "Gate 4",
keep $SURFACE_PID as "Gate 5", $MD_PID as "Gate 6", and $MD_CODE_PID as "Gate
7"); locate the wait lines referencing $LINT_PID, $TC_PID, $POLICY_PID,
$CONSUMER_PID, $SURFACE_PID, $MD_PID, and $MD_CODE_PID and only change the
human-readable "Gate N" numbers in the echo strings to the CI-consistent values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/hooks/pre-push`:
- Line 5: Update the inaccurate header comment in the pre-push hook that
currently reads "Eight gates in parallel, then unit tests." to correctly state
"Seven gates in parallel, then unit tests." so it accurately reflects that Gates
1–7 run in parallel and Gate 8 is the sequential unit tests; edit the comment
string in the scripts/hooks/pre-push file accordingly.

---

Nitpick comments:
In `@scripts/hooks/pre-push`:
- Around line 54-60: The gate numbers in the pre-push hook messages are
misaligned with CI; update the wait error messages to match the CI gate mapping
by changing the reported gate numbers for the PID variables so they correspond
to .github/workflows/ci.yml (set $TC_PID to "Gate 1", $POLICY_PID to "Gate 2",
$CONSUMER_PID to "Gate 3", $LINT_PID to "Gate 4", keep $SURFACE_PID as "Gate 5",
$MD_PID as "Gate 6", and $MD_CODE_PID as "Gate 7"); locate the wait lines
referencing $LINT_PID, $TC_PID, $POLICY_PID, $CONSUMER_PID, $SURFACE_PID,
$MD_PID, and $MD_CODE_PID and only change the human-readable "Gate N" numbers in
the echo strings to the CI-consistent values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f332ca8-7445-45db-98c8-6abf90da1e39

📥 Commits

Reviewing files that changed from the base of the PR and between 218e821 and 764e4b7.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • docs/ADR-001-Folds.md
  • docs/ROADMAP/COMPLETED.md
  • scripts/hooks/pre-push
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/ADR-001-Folds.md

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@flyingrobots: On it! 🥕 Kicking off another review pass now.


🧠 Learnings used
Learnt from: flyingrobots
Repo: git-stunts/git-warp PR: 55
File: src/domain/warp/checkpoint.methods.js:170-191
Timestamp: 2026-02-28T10:10:56.572Z
Learning: In `src/domain/warp/checkpoint.methods.js`, the `_loadLatestCheckpoint()` function uses four specific substring checks to distinguish "not found" errors from decode/corruption errors:
- `"missing"` — matches git cat-file errors on pruned/unreachable objects
- `"not found"` — matches readTree entry lookup failures
- `"ENOENT"` — matches filesystem-level missing path errors
- `"non-empty string"` — matches readRef/getNodeInfo validation errors with empty SHA
These patterns are documented in commit 9e48052 and are appropriate because the persistence adapter is internal with stable error strings.

Learnt from: flyingrobots
Repo: git-stunts/git-warp PR: 65
File: src/infrastructure/adapters/InMemoryGraphAdapter.js:427-437
Timestamp: 2026-03-08T12:36:24.197Z
Learning: In `src/infrastructure/adapters/InMemoryGraphAdapter.js`, the `logNodesStream()` method intentionally uses a dynamic `import('node:stream')` without a browser-safe fallback. This is by design because: (1) it is an infrastructure adapter method, not domain code; (2) materialization never calls it — it walks commits via `getNodeInfo()`/`readBlob()` directly; (3) in browser builds (Vite), `node:stream` is stubbed and the method is never invoked; (4) its only consumer is the CLI's `git log`-style output. Do not flag this as a browser-compatibility issue.

Learnt from: flyingrobots
Repo: git-stunts/git-warp PR: 0
File: :0-0
Timestamp: 2026-03-12T21:07:44.393Z
Learning: In `scripts/lint-markdown-code-samples.js`, the `collectMarkdownFiles()` function uses `fs.readdirSync` with `{ withFileTypes: true }` and only recurses when `entry.isDirectory()` is true. Node.js `Dirent.isDirectory()` returns `false` for symbolic links even when they point to directories, so the walker does not follow directory symlinks and is not susceptible to symlink cycles. Do not flag symlink-cycle risk for this function.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link

Release Preflight

  • package version: 14.0.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v14.0.0, release workflow will publish.

@flyingrobots flyingrobots merged commit 731627a into main Mar 13, 2026
7 checks passed
@flyingrobots flyingrobots deleted the feature/b87-markdown-code-lint branch March 13, 2026 01:30
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