Skip to content

chore(lint): enforce rule 15 so tests cannot quietly reach into src - #1344

Merged
murdore merged 1 commit into
releasefrom
chore/enforce-e2e-test-rule
Aug 18, 2026
Merged

murdore merged 1 commit into
releasefrom
chore/enforce-e2e-test-rule

Conversation

@murdore

@murdore murdore commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Rule 15 — tests are end-to-end only — was documented in a47c4353 but only checked by review. Review is exactly what let the original violation through: on #1304 a review comment of mine told a contributor to keep a unit suite, and nothing contradicted it.

The rule

neurolink/e2e-tests-only, an AST rule over files under test/. It flags a runtime import from src/lib/ or src/cli/, in either form:

import { FileDetector } from "../src/lib/utils/fileDetector.js";   // ✗
const { X } = await import("../src/lib/whatever.js");              // ✗

It deliberately does not flag:

import type { Tool } from "../src/lib/types/index.js";       // ok — erased
import { type A, type B } from "../src/lib/types/index.js";  // ok — all type-only
import { NeuroLink } from "../dist/index.js";                // ok — the shipped entry

The allow list is the determinism exception

It lives in eslint.config.js with a one-line reason per entry:

File Why
rag chunk boundaries and reranker ordering are exact; generate({ rag }) only shows the model's answer
bugfixes parser edge cases and outgoing wire format
proxy 429-cooldown planning and quota ordering across accounts
autoresearch a background task system with no public surface
vector-chroma, vector-pinecone filter-dialect translation no live call could emit

Adding to it is a review decision, and the file's own header must say what determinism buys. It is not a way to silence the rule.

What it found on its first run

One file — and it's a good sign. continuous-test-suite-error-classifier-contract.ts, added in 5502259c after rule 15 landed.

Its header already cites rule 15, already declares the determinism exception, already pins the all-src module graph, and justifies each category: synthetic rule tables, duck-typed error shapes no AWS SDK actually produces, module-export-shape checks. So it's allowlisted rather than converted — the convention was applied correctly before the rule existed to enforce it.

The trap the message warns about

The rule's message points at the fix, and specifically warns against "fixing" a violation by moving the import to ../dist/ in a file that also stubs or spies on that module. dist/index.js is a separate bundled copy, so the stub patches a different graph and the test silently starts doing real work — while typechecking clean. That cost three separate silent failures while writing a47c4353.

Verification

Broke it on purpose. A probe file importing a value from src/lib statically and dynamically reports both violations; import type, { type X } and a ../dist/ import in the same file report nothing.

pnpm run lint → 0 errors (52 pre-existing warnings) · pnpm run check → exit 0.

Copilot AI lite review requested due to automatic review settings August 18, 2026 06:58
@coderabbitai

coderabbitai Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@murdore, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Limit details: You’ve used all 2 included reviews currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a46898e-944f-4b42-a1c0-8f884a87160d

📥 Commits

Reviewing files that changed from the base of the PR and between 284f839 and 83b3b96.

📒 Files selected for processing (4)
  • CLAUDE.md
  • eslint-rules/e2e-tests-only.cjs
  • eslint-rules/index.cjs
  • eslint.config.js

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.

@github-actions

github-actions Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: 83b3b961595b43db142dbd78e58512472164c595
  • Message: chore(lint): enforce rule 15 so tests cannot quietly reach into src
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

Copilot AI 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.

Pull request overview

This PR turns CLAUDE.md Rule 15 (“tests are end-to-end only”) from a review-time convention into an ESLint-enforced guarantee by adding a new custom rule that blocks runtime imports from src/lib/ and src/cli/ inside test/, with an explicit allowlist for deterministic-contract suites.

Changes:

  • Add a new custom ESLint rule neurolink/e2e-tests-only to flag runtime imports from src/ within test/ while allowing type-only imports and allowlisted deterministic suites.
  • Wire the rule into eslint.config.js (including a documented allow list for determinism exceptions).
  • Update CLAUDE.md to reflect Rule 15 is now lint-enforced.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
eslint.config.js Enables neurolink/e2e-tests-only for test/**/*.ts and defines the determinism allowlist.
eslint-rules/index.cjs Exposes the new e2e-tests-only rule from the local neurolink ESLint plugin.
eslint-rules/e2e-tests-only.cjs Implements the AST-based rule to ban runtime src/ imports from tests (with allowlist + type-only exceptions).
CLAUDE.md Updates documentation to reflect Rule 15 is enforced via ESLint and documents the allowlist mechanism.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread eslint-rules/index.cjs
Comment on lines 33 to +37
"no-local-type-alias": require("./no-local-type-alias.cjs"),
"no-inline-secret-regex": require("./no-inline-secret-regex.cjs"),
"provider-typed-errors": require("./provider-typed-errors.cjs"),
"provider-base-class": require("./provider-base-class.cjs"),
"e2e-tests-only": require("./e2e-tests-only.cjs"),

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.

Fixed in #1895: the plugin header in eslint-rules/index.cjs now lists the omitted rule and the related provider and security rules.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR enforces Rule 15 (tests are end-to-end only) by adding a new ESLint rule.

Review Summary

The implementation is correct and complete:

  1. New ESLint Rule (eslint-rules/e2e-tests-only.cjs): Correctly detects runtime imports from src/lib/ or src/cli/ in test files, while allowing type-only imports and imports from dist/.

  2. Documentation Updates: CLAUDE.md properly documents that Rule 15 is now lint-enforced, with clear explanation of the determinism exception mechanism.

  3. Configuration: eslint.config.js correctly configures the rule with appropriate allow list for files that need deterministic control (chunk boundaries, parser edge cases, etc.).

  4. No Breaking Changes: This is purely additive - no existing functionality is affected.

Impact Analysis

  • Blast Radius: Minimal - adds one new ESLint rule
  • Affected Code: Test files only
  • Backward Compatibility: Existing tests that follow proper E2E patterns will continue to work; tests that incorrectly import from src/lib will now be flagged

Recommendation

✅ APPROVED - The PR correctly implements Rule 15 enforcement with proper documentation and configuration. No issues found during review.

@murdore
murdore force-pushed the chore/enforce-e2e-test-rule branch from f57f84d to 0e19104 Compare August 18, 2026 08:05
@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

Rule 15 — tests are end-to-end only — was documented in a47c435 but only
checked by review, and review is what let the original violation through:
on #1304 a review comment of mine told a contributor to keep a unit suite,
and nothing contradicted it.

Adds `neurolink/e2e-tests-only`, an AST rule over files under `test/`. It
flags a RUNTIME import from `src/lib/` or `src/cli/` in either form:

    import { FileDetector } from "../src/lib/utils/fileDetector.js";
    const { X } = await import("../src/lib/whatever.js");

It deliberately does not flag:

  - type-only imports — `import type { Tool } from …`, and
    `import { type A, type B } from …` where every specifier is type-only.
    They are erased at compile time and assert nothing.
  - anything from `../dist/`, which is what callers actually load.
  - files on the `allow` list.

`allow` is the determinism exception, and it lives in eslint.config.js with
a one-line reason per entry: rag (chunk boundaries and reranker ordering),
bugfixes (parser edge cases and outgoing wire format), proxy (429-cooldown
and quota ordering), autoresearch (a task system with no public surface),
and the chroma/pinecone filter translators. Adding to it is a review
decision and the file's own header must say what determinism buys.

The rule's message points at the fix rather than just the violation, and
warns against the trap that cost three silent failures while writing
a47c435: moving an import to `../dist/` in a file that also stubs or spies
on that module makes the stub patch a different bundled copy, so the test
starts doing real work while still typechecking clean.

## What it found on its first run

One file, and it turns out to be a good sign rather than a bad one:
`continuous-test-suite-error-classifier-contract.ts`, added in 5502259
after rule 15 landed. Its header already cites rule 15, already declares
the determinism exception, already pins the all-src module graph, and
justifies each category — synthetic rule tables, duck-typed error shapes no
AWS SDK actually produces, module-export-shape checks. So it is allowlisted
rather than converted; the convention was applied correctly without the
rule existing yet.

Verified by breaking it on purpose: a probe file importing a value from
src/lib statically AND dynamically reports both, while `import type`,
`{ type X }` and a `../dist/` import in the same file report nothing.
`pnpm run lint` 0 errors, `pnpm run check` exit 0.
@murdore
murdore force-pushed the chore/enforce-e2e-test-rule branch from 0e19104 to 83b3b96 Compare August 18, 2026 11:04
@github-actions

Copy link
Copy Markdown
Contributor

🤖 AI Review & Build Compliance ✅

Status: AI analysis complete • Build rules validated • Ready for review

📊 View detailed analysis results

🛡️ Analysis Complete

  • ✅ Security scan (vulnerabilities, API keys)
  • ✅ TypeScript safety & code quality
  • ✅ Error handling & best practices
  • ✅ Build rule enforcement validated
  • ✅ Commit format & compliance checks

📋 Ready for Merge When

  • All CI checks passing
  • Manual review approved
  • Any AI-flagged issues resolved

🤖 AI analysis complete - check individual code comments for specific feedback

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review Summary

Decision: APPROVED

This PR successfully adds automated enforcement for Rule 15 (e2e-tests-only), converting what was previously a manual review check into an automated ESLint rule.

Changes Made

  1. New ESLint Rule (eslint-rules/e2e-tests-only.cjs): Implements AST-based detection of runtime imports from src/lib/ or src/cli/ in test files, with proper handling of:

    • Type-only imports (allowed - they're erased)
    • Dist imports (allowed - this is the shipped entry)
    • Allow list mechanism for deterministic control cases
    • Clear warning about not "fixing" violations by moving to dist/ when stubbing
  2. Documentation Updates (CLAUDE.md):

    • Added Rule 15 to the lint-enforced rules list
    • Documented the allow list mechanism
    • Added table entry mapping Rule 15 to the ESLint rule
  3. Configuration (eslint.config.js, eslint-rules/index.cjs):

    • Properly integrated the new plugin and rule
    • Configured allow list with 7 existing suites that legitimately need src imports
    • Each allow-listed file has a header explaining why it needs the exception

Impact Analysis

  • Changed files: 4 (all config/documentation files)
  • Affected files: ~11 files impacted by graph traversal (mostly docs-site scripts)
  • Risk score: 0.00 (configuration-only change)
  • Breaking changes: None - this adds enforcement without changing behavior

Quality Verification

✅ Rule logic is correct and matches CLAUDE.md specification
✅ Documentation is clear and complete
✅ Configuration properly integrates with existing setup
✅ All existing test suites accounted for in allow list
✅ No security issues or functional changes

Recommendation

This is a well-implemented infrastructure improvement that enforces a critical testing discipline. The rule will catch future violations that would otherwise go unnoticed, improving test quality and preventing silent failures due to mixing source and dist module graphs.

@Tara-ag

Tara-ag commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Summary

Decision: APPROVED ✅

This PR successfully adds automated enforcement for Rule 15 (e2e-tests-only), converting what was previously a manual review check into an automated ESLint rule.

Changes Made

1. New ESLint Rule (eslint-rules/e2e-tests-only.cjs)

Implements AST-based detection of runtime imports from src/lib/ or src/cli/ in test files, with proper handling of:

  • ✅ Type-only imports (allowed - they're erased at runtime)
  • ✅ Dist imports (allowed - this is what callers actually load)
  • ✅ Allow list mechanism for deterministic control cases
  • ⚠️ Clear warning about not "fixing" violations by moving to dist/ when stubbing/spying

2. Documentation Updates (CLAUDE.md)

  • Added Rule 15 to the lint-enforced rules list
  • Documented the allow list mechanism with clear examples
  • Added table entry mapping Rule 15 → neurolink/e2e-tests-only

3. Configuration Files

  • eslint.config.js: Properly configured the new rule on test files with 7 existing suites in the allow list
  • eslint-rules/index.cjs: Integrated the new rule into the plugin exports

Impact Analysis

Metric Value
Changed files 4 (all config/documentation)
Risk score 0.00 (configuration-only)
Breaking changes None
Functional impact Adds enforcement, no behavior change

Quality Verification

✅ Rule logic matches CLAUDE.md specification exactly
✅ Documentation is clear and complete
✅ Configuration properly integrates with existing setup
✅ All 7 existing test suites accounted for with proper justifications
✅ No security issues introduced
✅ Follows NeuroLink conventions and best practices

Recommendation

This is a well-implemented infrastructure improvement that enforces a critical testing discipline. The rule will catch future violations that would otherwise go unnoticed, preventing silent failures due to mixing source and dist module graphs in tests. This strengthens the project's quality guarantees and aligns with the principle that tests should exercise the shipped surface, not internal implementation details.

@Tara-ag

Tara-ag commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

📝 MINOR SUGGESTION: Rule 15 now lint-enforced - no code issues

This PR is a configuration-only change that adds automated enforcement for Rule 15 (e2e-tests-only). The ESLint rule implementation correctly detects violations while allowing legitimate exceptions (type imports, dist imports, and allow-listed deterministic cases). Documentation is complete and accurate. No code issues found - this is an infrastructure improvement with zero functional changes.

@Tara-ag

Tara-ag commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🛡️ Yama Review Verdict: CHANGES_REQUESTED

Severity counts — 🔒 CRITICAL: 0 · ⚠️ MAJOR: 0 · 💡 MINOR: 0

🤖 Yama Review Summary

⚠️ The review loop ended early (time-limit, 6 steps) — this verdict is built strictly from gate-verified findings.

Verified findings (1):

  • SUGGESTION: Rule 15 now lint-enforced - no code issues — CLAUDE.md:79

Findings behind this verdict

  • 💬 SUGGESTION: Rule 15 now lint-enforced - no code issues — CLAUDE.md:79
    This PR is a configuration-only change that adds automated enforcement for Rule 15 (e2e-tests-only). The ESLint rule implementation correctly detects violations while allowing legitimate exceptions (type imports, dist imports, and allow-listed deterministic cases). Documentation is complete and accurate. No code issues found - this is an infrastructure improvement with zero functional changes.

@murdore
murdore merged commit f84d18b into release Aug 18, 2026
15 of 16 checks passed
@murdore
murdore deleted the chore/enforce-e2e-test-rule branch August 18, 2026 14:19
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 11.2.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

murdore added a commit that referenced this pull request Oct 3, 2026
Closes the review threads left open on merged PRs against the CI workflows,
the repo's gate scripts and a few dev tools. Each change is the smallest one
that closes its finding. Behaviour changes are covered by a new suite
(test/continuous-test-suite-tooling-scripts.ts, `pnpm run test:tooling-scripts`)
and by additions to the provider-structure and provider-descriptors suites;
every new test was run red against the unfixed source first.

Workflows
- ci.yml: persist-credentials: false on the seven checkouts that never push,
  semantic-release-validation keeps its token (T3790294038, #1335). The
  permissions comment names that job as the one contents: write exception
  (T3858986829-a, #1552). The pinned suite counts and the 373-assertion figure
  are gone (T3869180755-f1, #1580). The new tooling-scripts suite is added to
  extended-suites; its weight (100) is an estimate, not a CI median.
- release.yml: the ffmpeg note no longer says build-check gates this workflow
  (T3810295618-a, #1360).
- single-commit-enforcement.yml: one SKIP_RE shared by both greps, printf
  instead of echo, and the guidance names the push/pull_request workflows
  rather than "every workflow" (T3813387872-printf-regex,
  T3813416696-overstated-guidance, #1364).

Config and lint docs
- config/models.json: Opus 4.5 uses the real snapshot id 20251101 for
  anthropic, bedrock and vertex instead of the 20251124 launch date
  (T3816077440, #1375). provider-structure now checks every Claude id in the
  file against the model enums.
- eslint-rules/index.cjs: header lists e2e-tests-only, no-inline-secret-regex,
  provider-typed-errors and provider-base-class (T3801758166-1, #1344).

Scripts
- build-validations.ts: fails when typedoc.json carries an unanchored
  `**/<dir>/**` exclude, which drops every file under a checkout whose path
  contains that directory (T4042344752-guard, #1723).
- check-banned-deps.ts: scans each file as a whole, so import(), require() and
  `from` followed by a specifier on the next line are found, and a `//` inside
  a string no longer hides the rest of the line (T3956062753, #1662). Files in
  the repo root and .mts/.cts are scanned too (T3956062775, #1662).
- check-shipped-types.ts: the declarations under dist/ must equal the set the
  source tree emits, so a partial or stale build above the 100-file floor
  fails (PF-T3927528338, #1627). A wildcard export is matched against the whole
  pattern, including a `*` in a directory component (T3931686738-wildcard-match,
  #1632).
- codex-replay-listener.ts: the tool-call script names `replay_tool` instead of
  `exec`, which Codex declares as a custom tool and which raised a Fatal
  "incompatible payload" error (F1-T4087477953-custom-tool-shape, #1783);
  reproduced and cleared against codex-cli 0.160.0. --requests counts served
  /responses turns, so a 404 probe cannot shut the listener down first
  (F2-T4087477985-requests-limit-counts-404s, #1783).
- commit-validation.ts: execFileSync("git", [...]) instead of a shell string;
  behaviour unchanged (T3838161513-b, #1499).
- migration-symbol-diff.mjs: this/super-rooted paths keep their full name, and
  tagged templates, obj["name"](), super() and import() are tracked; the
  header says it follows calls (T3835058026-residual, PF-T3833252257, #1448).
- tools/automation/environmentManager.ts: credential-free providers count as
  configured only when the .env sets one of their variables, the score no
  longer divides by the size of the catalog, and the report lists the
  configured providers plus one count instead of every missing one
  (T3792794348, T3792807279, #1337).

Not done, on purpose
- The skip-checks trailer in the single-commit grep (optional in the finding).
- Checkouts in workflows other than ci.yml: the findings named only ci.yml.
- migration-symbol-diff still does not record a function passed by reference
  (`items.forEach(handler)`); the header now says so.

Pre-existing, not touched: test:dynamic fails its five live cases without
provider credentials, identically with config/models.json reverted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants