Skip to content

feat(plumbing): hermetic mock Auth0 CLI + trace-aware judge for tenant-config evals#82

Open
sanchitmehtagit wants to merge 15 commits into
mainfrom
plumbing
Open

feat(plumbing): hermetic mock Auth0 CLI + trace-aware judge for tenant-config evals#82
sanchitmehtagit wants to merge 15 commits into
mainfrom
plumbing

Conversation

@sanchitmehtagit

@sanchitmehtagit sanchitmehtagit commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What this adds

Shared framework infrastructure that lets evals verify tenant configuration hermetically. This PR contains no eval content of its own — it is the base that the MFA Leg 2 and Custom Token Exchange enterprise-leg PRs stack on top of.

Components

  • Mock Auth0 CLI (mocks/auth0) — a version-controlled no-op stub prepended to the agent's PATH (via EVAL_MOCK_BIN_DIR) so tenant commands succeed deterministically — no auth, no network, no live side effects.
    • Declarative engine + per-feature manifests — the generic engine (packages/eval-core/src/mock/) applies verbs (static/create/reflect/handler) from *.routes.json manifests; feature content is data, not code.
    • Per-eval route co-location — an eval ships its own routes/ dir (<surface>.routes.json + fixtures/ + handlers.js) next to its PROMPT.md; run.ts sets EVAL_MOCK_ROUTES_DIRS and the dispatcher merges it with the shared mocks/ dir. So a surface lives with the eval that uses it, not centrally.
    • Stateful read-after-write via EVAL_MOCK_STATE_DIR (a per-run temp dir outside the workspace, so graders never see it): a put guardian/factors/otp is reflected by a later get guardian/factors.
    • Path normalization — collapses bare (guardian/factors/otp), leading-slash (/api/v2/...), and full-URL (https://<tenant>/api/v2/...) forms to one route, so the agent's command works whichever form it emits and never misreads a valid call as a no-op.
    • Unmapped writes echo {"ok":true}; unmapped reads fall through to {}.
  • Shared CLI platform context (src/evals/contexts/cli-platform/AGENTS.md) — feature-agnostic platform-engineer guidance with only the auth0 api <METHOD> <path> passthrough shape. Auto-attached by convention: any eval that ships a routes/ dir (i.e. mocks the CLI → is a CLI tenant-config eval) inherits it via loadSharedContextForEval in the loader — no scaffold:/context: frontmatter. It's context (guidance), not a scaffold (starter code), and being generic it never leaks a grader's expected commands. One file changes the context for every CLI eval.
  • Trace-aware LLM judgejudge() gains an opt-in { includeCommandTrace }. When set, the agent's successful shell commands are appended to the judge input (under a // COMMAND TRACE header), so a file-less CLI eval has a judgeable artifact. Off by default — every file-based judge is unchanged.
  • WiringfilteredEnv() forwards EVAL_MOCK_BIN_DIR / EVAL_MOCK_STATE_DIR / EVAL_MOCK_ROUTES_DIRS; run.ts creates a per-run state dir locally and sets per-eval routes; docker/Dockerfile + entrypoint.sh wire the sandbox.

Testing

npm run build, npm run lint, npm test all pass. New/updated tests: auth0-mock, judge trace (executors), primitives, env, discovery, loader.

Stacked PRs

Two feature PRs build on this branch — both CLI-only tenant-config evals with co-located mock routes:

🤖 Generated with Claude Code

…t-config evals

Shared framework infrastructure for tenant-configuration evals (no eval content
of its own — MFA and Custom Token Exchange feature branches build on this).

- Mock Auth0 CLI (mocks/auth0): version-controlled no-op stub prepended to the
  agent PATH so tenant commands succeed hermetically — no auth, network, or live
  side effects. Stateful read-after-write via EVAL_MOCK_STATE_DIR (a per-run temp
  dir outside the workspace). Path normalization collapses bare, leading-slash,
  /api/v2/, and full-URL forms to one route. Unmapped writes echo {"ok":true}.
- Trace-aware LLM judge: judge() gains { includeCommandTrace } so a file-less
  CLI eval's command trace is judgeable. Off by default — file-based judges are
  unchanged.
- Wiring: EVAL_MOCK_BIN_DIR / EVAL_MOCK_STATE_DIR forwarded via filteredEnv();
  run.ts creates per-run state dir locally; docker Dockerfile/entrypoint for the
  sandbox.
- Tests: auth0-mock, judge trace, primitives, env, discovery, loader, validators.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

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
📝 Walkthrough

Walkthrough

This PR introduces a manifest-driven mock runtime for Auth0 CLI calls in eval-core, wired through Docker and CLI runners via environment variables. It also adds judge command-trace support, a ranCommandsInOrder grader, shared CLI platform context injection for evals, a discovery refactor, and cosmetic formatting cleanups.

Changes

Declarative Auth0 mock runtime

Layer / File(s) Summary
Mock engine core and tests
packages/eval-core/src/mock/*, packages/eval-core/src/index.ts, packages/eval-core/tests/mock/*
Adds MockState, path matcher, manifest loader/validator, verb application, runMockCli engine, barrel exports, and unit tests covering each module.
Auth0 mock entrypoint and route docs
apps/auth0-evals/mocks/auth0, apps/auth0-evals/mocks/README.md, apps/auth0-evals/mocks/routes/README.md, packages/eval-core/tests/auth0-mock.test.ts, packages/eval-core/tests/mock/harness.test.ts, docs/superpowers/specs/*, docs/superpowers/plans/*
Adds the Auth0-specific mock CLI entrypoint wiring manifests/handlers, documents the mock/route system, and adds black-box + harness tests plus design/plan docs.
Mock generator and probe scripts
apps/auth0-evals/scripts/mock-new.mjs, apps/auth0-evals/scripts/mock-check.mjs, apps/auth0-evals/package.json
Adds mock:new scaffold generator and mock:check probe CLI, wired into package scripts.

Mock environment wiring

Layer / File(s) Summary
Runtime image and entrypoint env setup
docker/Dockerfile, docker/entrypoint.sh, packages/eval-core/src/utils/env.ts, packages/eval-core/tests/env.test.ts
Copies mock stubs into the runtime image, exports EVAL_MOCK_BIN_DIR/EVAL_MOCK_STATE_DIR in the entrypoint, and forwards them (plus EVAL_MOCK_ROUTES_DIRS) through filteredEnv().
CLI runner mock env setup
packages/eval/src/cli/run.ts, packages/eval/src/cli/sandbox-runner.ts
Sets mock bin/state/route env vars for local and sandbox runners, and cleans up temporary mock state on completion.

Grader command trace support

Layer / File(s) Summary
Judge trace formatting and exclusions
packages/eval-core/src/graders/executors/llm-judge.ts, packages/eval-core/src/graders/engine.ts, packages/eval-graders/src/types.ts, packages/eval-core/tests/graders/executors.test.ts, packages/eval-graders/tests/primitives.test.ts
Adds formatCommandTrace, includeCommandTrace option, and routes directory exclusions for grading/judge input.
ranCommandsInOrder export and tests
packages/eval-graders/src/index.ts, packages/eval-graders/tests/primitives.test.ts
Re-exports ranCommandsInOrder and JudgeOptions, with predicate tests for ordered command execution.

Discovery refactor, shared context, and formatting cleanup

Layer / File(s) Summary
Discovery refactor and promptFile support
packages/eval-core/src/discovery.ts, packages/eval-core/src/loader.ts
Replaces single-config building with buildEvalConfigs and lets loadEval select an alternate prompt file via promptFile.
Shared CLI platform context injection
packages/eval-core/src/loader.ts, apps/auth0-evals/src/evals/contexts/cli-platform/AGENTS.md, docs/ADDING_EVALS.md, packages/eval-core/tests/loader.test.ts
Adds loadSharedContextForEval to auto-inject shared AGENTS.md for evals with a routes/ directory, documents the convention, and adds injection-precedence tests.
Formatting-only changes
packages/eval/src/runners/claude-code/agent.ts, packages/eval/src/runners/codex/agent.ts, packages/eval/tests/runners/claude-code-agent.test.ts
Reformats token-accounting expressions without behavior changes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Entrypoint as docker/entrypoint.sh
  participant AgentCLI as auth0 mock CLI
  participant Engine as runMockCli
  participant Manifests as loadManifests/applyVerb
  participant State as MockState

  Entrypoint->>Entrypoint: export EVAL_MOCK_BIN_DIR, EVAL_MOCK_STATE_DIR
  AgentCLI->>Engine: runMockCli(argv, config, handlers)
  Engine->>Manifests: loadManifests(manifestDirs)
  Manifests-->>Engine: RouteManifest[]
  Engine->>Engine: routeMatches(pattern, method, path)
  alt route verb is handler
    Engine->>Manifests: handlers[route.handler](context)
  else declarative verb
    Engine->>Manifests: applyVerb(route, state, fixturesDir)
    Manifests->>State: has/set/clear(key)
    State-->>Manifests: marker state
  end
  Manifests-->>Engine: response body
  Engine-->>AgentCLI: JSON stdout
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the two main changes: a hermetic Auth0 mock CLI and trace-aware judging for tenant-config evals.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch plumbing

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.

sanchitmehtagit and others added 2 commits July 6, 2026 14:59
…nery

The standalone-eval design (one eval per tenant-config method) supersedes the
frontmatter-driven fan-out. Nothing uses it: no PROMPT.md declares
tenant_config_methods, every graders.ts calls bare defineGraders(), and
--tenant-config filters on an always-undefined field.

Removes:
- discovery.ts: tenant_config_methods fan-out + scaffold_<method> resolution
- loader.ts: tenantConfigMethod / variantScaffold / {{tenant_config_instruction}}
  substitution + defineGraders(method) arg
- eval-graders: TenantConfigMethod type + TENANT_CONFIG_INSTRUCTIONS map
- CLI: --tenant-config flag, RunConfig.tenantConfig, validateTenantConfig
- the fan-out-only tests (tenant-config-method, validators, discovery/loader cases)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Split the monolithic mocks/auth0 case-statement into a dispatcher that owns only
the shared mechanism and per-feature route files that own their API surface.

- mocks/auth0: parses `api <method> <path>`, normalizes the path, sources every
  mocks/routes/*.sh until one handles the request (sets HANDLED via emit()), then
  applies the fallthrough (writes → {"ok":true}, reads → {}). Knows nothing about
  guardian, token-exchange, or any specific surface.
- mocks/lib.sh: shared helpers — emit / record_state / has_state / clear_state.
- mocks/routes/README.md: the route-file contract.

A feature now adds its endpoints by dropping ONE file in mocks/routes/ — no edit
to the dispatcher, so route files never merge-conflict. Route files are named by
API surface (guardian.sh, token-exchange.sh) and ship with the PR of the feature
that consumes them; guardian/token-exchange routes move out of plumbing into the
MFA and CTE PRs respectively.

Dispatcher tests are now feature-agnostic (use a throwaway fixture route),
exercising normalization, route discovery, read-after-write via helpers, and the
fallthrough.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
packages/eval-core/src/loader.ts (1)

142-159: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scaffold error paths hardcode PROMPT.md, ignoring promptFile.

resolveScaffoldFromMeta builds EvalConfigError paths with a literal join(evalPath, 'PROMPT.md') (Lines 153, 158), but loadEval now allows an alternate prompt filename via evalConfig.promptFile (Line 51). If a config uses a non-default promptFile, a scaffold resolution failure will report a PROMPT.md path that doesn't exist for that eval, misleading debugging.

🐛 Proposed fix to thread promptFileName through
-function resolveScaffoldFromMeta(scaffoldMeta: string | undefined, evalPath: string, frameworkRoot: string): string {
+function resolveScaffoldFromMeta(
+  scaffoldMeta: string | undefined,
+  evalPath: string,
+  frameworkRoot: string,
+  promptFileName: string,
+): string {
   if (!scaffoldMeta) {
     return join(evalPath, 'scaffold');
   }

   let resolvedPath: string;
   try {
     resolvedPath = resolveInside(frameworkRoot, scaffoldMeta);
   } catch {
     throw new EvalConfigError(
       `scaffold path is invalid or escapes project root: ${scaffoldMeta}`,
-      join(evalPath, 'PROMPT.md'),
+      join(evalPath, promptFileName),
     );
   }

   if (!existsSync(resolvedPath)) {
-    throw new EvalConfigError(`scaffold path does not exist: ${resolvedPath}`, join(evalPath, 'PROMPT.md'));
+    throw new EvalConfigError(`scaffold path does not exist: ${resolvedPath}`, join(evalPath, promptFileName));
   }

   return resolvedPath;
 }

And update the call site to pass promptFileName.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/loader.ts` around lines 142 - 159,
`resolveScaffoldFromMeta` is hardcoding `PROMPT.md` in its `EvalConfigError`
paths, which breaks when `loadEval` uses a custom `evalConfig.promptFile`.
Update `resolveScaffoldFromMeta` to accept the prompt filename (or full prompt
path) from `loadEval` and use that value in both error cases instead of the
literal `PROMPT.md`. Make sure the call site in `loadEval` threads the existing
`promptFileName` through so scaffold resolution failures point to the correct
prompt file.
packages/eval/src/cli/run.ts (1)

146-180: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Avoid mutating shared process.env for per-run mock state

Local agent jobs run under the pLimit(workers) pool, so process.env.EVAL_MOCK_STATE_DIR can be overwritten or deleted by a sibling job before filteredEnv() snapshots it for the child process. That can send two evals to the same mock-state directory or a removed one.

Thread the mock state dir through the spawned env instead of mutating global process.env, or serialize local non-sandboxed runs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval/src/cli/run.ts` around lines 146 - 180, The local run path in
run.ts is mutating shared process.env for EVAL_MOCK_STATE_DIR, which can be
clobbered by concurrent pLimit(workers) jobs before filteredEnv() captures it.
Stop setting the mock state dir on process.env in run() and instead thread the
per-run mockStateDir directly into the spawned child env (the same place
filteredEnv() builds the agent environment), or otherwise serialize
non-sandboxed local execution. Keep the per-run directory creation in
mkdtempSync and ensure each job gets its own isolated state dir without relying
on global process.env.
🧹 Nitpick comments (7)
packages/eval-core/src/graders/executors/llm-judge.ts (1)

37-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

RUN_COMMAND_NAMES is hardcoded to two runner tool names.

Tool names that represent shell execution across runners (Claude: run_command, Gemini: bash). This is fine for the current base PR, but as more agent runners are wired into evals (e.g. Codex), this set will need updating in lockstep or their commands will silently be dropped from the trace with no warning.

Not blocking for this PR since it's scoped as shared plumbing, but worth keeping in mind as future runners are added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/graders/executors/llm-judge.ts` around lines 37 - 53,
The command-trace filter in formatCommandTrace currently hardcodes
RUN_COMMAND_NAMES to only run_command and bash, so commands from newly added
runners can be silently omitted. Update the shell-tool पहचान in llm-judge.ts to
use a centralized/extensible source of runner tool names (or a shared
helper/constants map) so adding runners only requires one place to update, and
keep formatCommandTrace using that symbol when deciding which EventToolCall
entries to include.
packages/eval-core/src/discovery.ts (1)

87-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

buildEvalConfigs always returns a single-element array.

The function is pluralized and returns EvalConfig[], but it only ever builds and returns one config from a single PROMPT.md. Per the PR history, the fan-out machinery (tenant_config_methods) that would have justified multiple configs per prompt was removed in favor of standalone evals, so this array wrapping now appears to be residual generality with no current caller that produces more than one element. Consider returning a single EvalConfig directly and let findEvalDirs push it, unless a near-term follow-up (e.g., the MFA Leg 2 / Custom Token Exchange PRs mentioned in the stack) is expected to reintroduce multi-config output from one prompt.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/discovery.ts` around lines 87 - 106, buildEvalConfigs
currently always produces exactly one EvalConfig, so the EvalConfig[] return
type and array literal are unnecessary residual generality. Update
buildEvalConfigs to return a single EvalConfig instead of an array, and adjust
its caller in findEvalDirs to push that object directly; keep the existing
validation and field construction (id, name, category, path) unchanged.
mocks/lib.sh (2)

21-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Silence the SC2034 false positive on HANDLED.

Shellcheck can't see that HANDLED is consumed by mocks/auth0 after sourcing this file, hence the "appears unused" warning. Add a disable directive so CI shellcheck runs stay clean without masking real findings later.

🔇 Suggested annotation
 emit() {
   printf '%s\n' "$1"
+  # shellcheck disable=SC2034  # consumed by the dispatcher (mocks/auth0) after sourcing
   HANDLED=1
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mocks/lib.sh` around lines 21 - 27, The HANDLED variable in emit() is
triggering a ShellCheck SC2034 false positive because its use is only visible in
mocks/auth0 after sourcing this file. Add a ShellCheck disable directive around
the HANDLED assignment in mocks/lib.sh, keeping the emit() behavior unchanged
while silencing only this warning and preserving future checks.

Source: Linters/SAST tools


12-19: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Marker-key path helpers don't sanitize input against traversal.

record_state/clear_state/has_state build the target path via plain "$STATE/$1" concatenation. Callers are expected to pass static, namespaced literals (per the doc comment), but since this is shared infra other feature route files will build on (MFA Leg 2, CTE), if any future route ever derives the key from request data (e.g. $PATH_, which is influenced by the agent's command argument), a value like ../../etc/passwd would let touch/rm/test operations escape $STATE. Worth a defensive guard now while the helper is small.

🛡️ Proposed guard
+_sanitize_key() {
+  case "$1" in
+    */*|*..*) return 1 ;;
+  esac
+}
+
-record_state() { touch "$STATE/$1" 2>/dev/null || true; }
+record_state() { _sanitize_key "$1" && touch "$STATE/$1" 2>/dev/null || true; }
 
-clear_state() { rm -f "$STATE/$1" 2>/dev/null || true; }
+clear_state() { _sanitize_key "$1" && rm -f "$STATE/$1" 2>/dev/null || true; }
 
-has_state() { [ -f "$STATE/$1" ]; }
+has_state() { _sanitize_key "$1" && [ -f "$STATE/$1" ]; }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mocks/lib.sh` around lines 12 - 19, The state marker helpers in lib.sh accept
raw keys and concatenate them directly into "$STATE/$1", so harden record_state,
clear_state, and has_state against path traversal by validating or sanitizing
the key before using it as a path segment. Update the shared helper logic so
only safe marker names are allowed, and keep the existing behavior for
legitimate static namespaced literals used by the route files.
docker/Dockerfile (1)

70-78: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Mock stubs aren't covered by the later read-only lockdown.

Line 93 (RUN chmod -R a-w /app/node_modules /app/packages /app/apps) doesn't include /app/mocks. In practice this is already effectively immutable to the non-root runtime user since COPY leaves it root-owned and chmod +x sets 755 (no write bit for non-owner), but for consistency with the stated intent ("Make application code read-only to prevent tampering") it'd be cleaner to include it explicitly.

🔒 Optional consistency fix
-RUN chmod -R a-w /app/node_modules /app/packages /app/apps
+RUN chmod -R a-w /app/node_modules /app/packages /app/apps /app/mocks
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker/Dockerfile` around lines 70 - 78, The mock CLI stubs copied by the
Dockerfile should be explicitly covered by the read-only lockdown for
consistency with the immutability intent. Update the final permission-hardening
step to include /app/mocks alongside /app/node_modules, /app/packages, and
/app/apps, using the existing Dockerfile setup around COPY mocks/ /app/mocks/
and the chmod hardening command.
mocks/README.md (1)

73-81: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fenced code block missing a language tag.

The directory-tree fence (line 73) has no language identifier, per the markdownlint MD040 hint.

Proposed fix
-```
+```text
 mocks/
 ├── auth0                 # dispatcher — shared mechanism, no feature knowledge
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mocks/README.md` around lines 73 - 81, The directory tree in mocks/README.md
is missing a fenced code block language tag, triggering the markdownlint MD040
warning. Update the fence around the directory-tree example to use the
appropriate language identifier so the block is explicitly marked as text,
keeping the content unchanged. Locate the fenced example near the
mocks/README.md directory layout section and apply the tag consistently to the
opening fence.

Source: Linters/SAST tools

packages/eval-core/tests/auth0-mock.test.ts (1)

31-49: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Test writes a fixture route file directly into the real mocks/routes/ source directory.

beforeEach/afterEach create and delete mocks/routes/zz-mock-test-fixture.sh in the actual package directory rather than an isolated location, because the dispatcher's $MOCK_DIR/routes is always dirname "$0". If the test process is killed mid-run (timeout, CI cancellation, crash) before afterEach fires, the fixture file is left behind in the working tree, where it would (a) show up as untracked repo state and (b) be picked up by the dispatcher for any other concurrent eval/process sharing the same checkout (e.g., parallel Vitest workers or a concurrently-running eval that also invokes mocks/auth0).

Consider allowing the dispatcher to read the routes directory from an overridable env var (e.g. EVAL_MOCK_ROUTES_DIR, defaulting to $MOCK_DIR/routes) so this test can point at a throwaway temp directory instead of mutating the real mocks/routes/.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/tests/auth0-mock.test.ts` around lines 31 - 49, The test
currently mutates the real mocks/routes directory by creating
zz-mock-test-fixture.sh in beforeEach and deleting it in afterEach, which can
leak state if cleanup never runs. Update the auth0 mock dispatcher (and the test
setup) so the routes directory can be overridden via an env var such as
EVAL_MOCK_ROUTES_DIR, defaulting to the current $MOCK_DIR/routes behavior. Then
point the test at a temp routes directory instead of writing into the package
source tree.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/eval-core/src/utils/env.ts`:
- Around line 44-53: The mock-bin PATH injection in env.ts bypasses the existing
case-insensitive environment-key lookup, so on Windows it may create a new PATH
entry instead of updating the resolved key. Update the mock injection logic to
reuse the same resolved PATH key used earlier in the function, and prepend
mockBinDir to that value instead of hardcoding env.PATH. This keeps the behavior
consistent in env normalization and avoids duplicate Path/PATH entries.

---

Outside diff comments:
In `@packages/eval-core/src/loader.ts`:
- Around line 142-159: `resolveScaffoldFromMeta` is hardcoding `PROMPT.md` in
its `EvalConfigError` paths, which breaks when `loadEval` uses a custom
`evalConfig.promptFile`. Update `resolveScaffoldFromMeta` to accept the prompt
filename (or full prompt path) from `loadEval` and use that value in both error
cases instead of the literal `PROMPT.md`. Make sure the call site in `loadEval`
threads the existing `promptFileName` through so scaffold resolution failures
point to the correct prompt file.

In `@packages/eval/src/cli/run.ts`:
- Around line 146-180: The local run path in run.ts is mutating shared
process.env for EVAL_MOCK_STATE_DIR, which can be clobbered by concurrent
pLimit(workers) jobs before filteredEnv() captures it. Stop setting the mock
state dir on process.env in run() and instead thread the per-run mockStateDir
directly into the spawned child env (the same place filteredEnv() builds the
agent environment), or otherwise serialize non-sandboxed local execution. Keep
the per-run directory creation in mkdtempSync and ensure each job gets its own
isolated state dir without relying on global process.env.

---

Nitpick comments:
In `@docker/Dockerfile`:
- Around line 70-78: The mock CLI stubs copied by the Dockerfile should be
explicitly covered by the read-only lockdown for consistency with the
immutability intent. Update the final permission-hardening step to include
/app/mocks alongside /app/node_modules, /app/packages, and /app/apps, using the
existing Dockerfile setup around COPY mocks/ /app/mocks/ and the chmod hardening
command.

In `@mocks/lib.sh`:
- Around line 21-27: The HANDLED variable in emit() is triggering a ShellCheck
SC2034 false positive because its use is only visible in mocks/auth0 after
sourcing this file. Add a ShellCheck disable directive around the HANDLED
assignment in mocks/lib.sh, keeping the emit() behavior unchanged while
silencing only this warning and preserving future checks.
- Around line 12-19: The state marker helpers in lib.sh accept raw keys and
concatenate them directly into "$STATE/$1", so harden record_state, clear_state,
and has_state against path traversal by validating or sanitizing the key before
using it as a path segment. Update the shared helper logic so only safe marker
names are allowed, and keep the existing behavior for legitimate static
namespaced literals used by the route files.

In `@mocks/README.md`:
- Around line 73-81: The directory tree in mocks/README.md is missing a fenced
code block language tag, triggering the markdownlint MD040 warning. Update the
fence around the directory-tree example to use the appropriate language
identifier so the block is explicitly marked as text, keeping the content
unchanged. Locate the fenced example near the mocks/README.md directory layout
section and apply the tag consistently to the opening fence.

In `@packages/eval-core/src/discovery.ts`:
- Around line 87-106: buildEvalConfigs currently always produces exactly one
EvalConfig, so the EvalConfig[] return type and array literal are unnecessary
residual generality. Update buildEvalConfigs to return a single EvalConfig
instead of an array, and adjust its caller in findEvalDirs to push that object
directly; keep the existing validation and field construction (id, name,
category, path) unchanged.

In `@packages/eval-core/src/graders/executors/llm-judge.ts`:
- Around line 37-53: The command-trace filter in formatCommandTrace currently
hardcodes RUN_COMMAND_NAMES to only run_command and bash, so commands from newly
added runners can be silently omitted. Update the shell-tool पहचान in
llm-judge.ts to use a centralized/extensible source of runner tool names (or a
shared helper/constants map) so adding runners only requires one place to
update, and keep formatCommandTrace using that symbol when deciding which
EventToolCall entries to include.

In `@packages/eval-core/tests/auth0-mock.test.ts`:
- Around line 31-49: The test currently mutates the real mocks/routes directory
by creating zz-mock-test-fixture.sh in beforeEach and deleting it in afterEach,
which can leak state if cleanup never runs. Update the auth0 mock dispatcher
(and the test setup) so the routes directory can be overridden via an env var
such as EVAL_MOCK_ROUTES_DIR, defaulting to the current $MOCK_DIR/routes
behavior. Then point the test at a temp routes directory instead of writing into
the package source tree.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 92ada64b-1f1d-478f-8d03-cea88366e7d8

📥 Commits

Reviewing files that changed from the base of the PR and between 7834214 and 3669985.

📒 Files selected for processing (21)
  • docker/Dockerfile
  • docker/entrypoint.sh
  • mocks/README.md
  • mocks/auth0
  • mocks/lib.sh
  • mocks/routes/README.md
  • packages/eval-core/src/discovery.ts
  • packages/eval-core/src/graders/executors/llm-judge.ts
  • packages/eval-core/src/loader.ts
  • packages/eval-core/src/utils/env.ts
  • packages/eval-core/tests/auth0-mock.test.ts
  • packages/eval-core/tests/env.test.ts
  • packages/eval-core/tests/graders/executors.test.ts
  • packages/eval-graders/src/index.ts
  • packages/eval-graders/src/primitives.ts
  • packages/eval-graders/src/types.ts
  • packages/eval-graders/tests/primitives.test.ts
  • packages/eval/src/cli/run.ts
  • packages/eval/src/runners/claude-code/agent.ts
  • packages/eval/src/runners/codex/agent.ts
  • packages/eval/tests/runners/claude-code-agent.test.ts

Comment on lines +44 to +53
// Mock CLI shims. When EVAL_MOCK_BIN_DIR points at an existing directory,
// prepend it to PATH so stubbed external CLIs (e.g. `auth0`) resolve to the
// hermetic no-op stubs in mocks/ instead of a real binary. Prepending
// (not appending) guarantees a real install on the host can never be hit —
// keeping runs deterministic and preventing accidental live side effects.
// Both execution paths set this: entrypoint.sh in the sandbox, run.ts locally.
const mockBinDir = process.env.EVAL_MOCK_BIN_DIR;
if (mockBinDir && existsSync(mockBinDir)) {
env.PATH = env.PATH ? `${mockBinDir}${delimiter}${env.PATH}` : mockBinDir;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

env.PATH bypasses the file's own case-insensitive Windows key handling.

Lines 20-36 of this function deliberately resolve PATH (and other keys) case-insensitively because on Windows, env var names like Path, ComSpec, SystemRoot are common mixed-case variants; this ensures we find them regardless of casing. The stored key in env therefore may be Path, not PATH.

The new mock-injection code hardcodes env.PATH:

env.PATH = env.PATH ? `${mockBinDir}${delimiter}${env.PATH}` : mockBinDir;

On Windows, if the resolved key is Path, env.PATH reads as undefined and this line creates a second, distinct PATH key holding only mockBinDir — the object ends up with both Path (full original value) and PATH (just the mock dir) as separate properties. Downstream, whichever key wins for the spawned process could either drop the mock dir entirely or clobber the real PATH, breaking CLI resolution for the agent.

🐛 Proposed fix
   const mockBinDir = process.env.EVAL_MOCK_BIN_DIR;
   if (mockBinDir && existsSync(mockBinDir)) {
-    env.PATH = env.PATH ? `${mockBinDir}${delimiter}${env.PATH}` : mockBinDir;
+    const pathKey = Object.keys(env).find((k) => k.toLowerCase() === 'path') ?? 'PATH';
+    env[pathKey] = env[pathKey] ? `${mockBinDir}${delimiter}${env[pathKey]}` : mockBinDir;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Mock CLI shims. When EVAL_MOCK_BIN_DIR points at an existing directory,
// prepend it to PATH so stubbed external CLIs (e.g. `auth0`) resolve to the
// hermetic no-op stubs in mocks/ instead of a real binary. Prepending
// (not appending) guarantees a real install on the host can never be hit —
// keeping runs deterministic and preventing accidental live side effects.
// Both execution paths set this: entrypoint.sh in the sandbox, run.ts locally.
const mockBinDir = process.env.EVAL_MOCK_BIN_DIR;
if (mockBinDir && existsSync(mockBinDir)) {
env.PATH = env.PATH ? `${mockBinDir}${delimiter}${env.PATH}` : mockBinDir;
}
// Mock CLI shims. When EVAL_MOCK_BIN_DIR points at an existing directory,
// prepend it to PATH so stubbed external CLIs (e.g. `auth0`) resolve to the
// hermetic no-op stubs in mocks/ instead of a real binary. Prepending
// (not appending) guarantees a real install on the host can never be hit —
// keeping runs deterministic and preventing accidental live side effects.
// Both execution paths set this: entrypoint.sh in the sandbox, run.ts locally.
const mockBinDir = process.env.EVAL_MOCK_BIN_DIR;
if (mockBinDir && existsSync(mockBinDir)) {
const pathKey = Object.keys(env).find((k) => k.toLowerCase() === 'path') ?? 'PATH';
env[pathKey] = env[pathKey] ? `${mockBinDir}${delimiter}${env[pathKey]}` : mockBinDir;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/utils/env.ts` around lines 44 - 53, The mock-bin PATH
injection in env.ts bypasses the existing case-insensitive environment-key
lookup, so on Windows it may create a new PATH entry instead of updating the
resolved key. Update the mock injection logic to reuse the same resolved PATH
key used earlier in the function, and prepend mockBinDir to that value instead
of hardcoding env.PATH. This keeps the behavior consistent in env normalization
and avoids duplicate Path/PATH entries.

…nostic

The mock Auth0 CLI is Auth0-specific (binary named `auth0`, normalizes the
Management API /api/v2 path, guardian/token-exchange routes), so it belongs with
the app that owns the Auth0 evals — not in generic framework code. eval-core
keeps only the generic contract it already had: prepend EVAL_MOCK_BIN_DIR to
PATH, forward EVAL_MOCK_STATE_DIR. It never learns the CLI is Auth0.

- Move mocks/ → apps/auth0-evals/mocks/ (git rename; dispatcher + lib.sh +
  routes/ contract).
- run.ts / sandbox-runner: anchor EVAL_MOCK_BIN_DIR to <cwd>/mocks
  (= apps/auth0-evals/mocks) instead of a fragile ../../../../ walk from dist.
- Per-eval routes: an eval may ship routes/*.sh next to its PROMPT.md; the runner
  sets EVAL_MOCK_ROUTES_DIRS and the dispatcher sources it after the app-level
  routes/. env.ts forwards the var.
- Docker: COPY apps/auth0-evals/mocks/ → /app/mocks (unchanged runtime path).
- Guardrails (defense-in-depth; route files never reach a workspace by
  construction): add `routes` to EXCLUDED_EVAL_DIRS and to the judge's excluded
  dirs, plus tests asserting per-eval route discovery and the exclusions.

Spec: docs/superpowers/specs/2026-07-07-mock-location-design.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/eval/src/cli/run.ts (2)

141-188: 🩺 Stability & Availability | 🔵 Trivial | ⚖️ Poor tradeoff

Mock env plumbing relies on an unenforced "one job per process" invariant.

EVAL_MOCK_BIN_DIR/EVAL_MOCK_ROUTES_DIRS/EVAL_MOCK_STATE_DIR are written to process.env (global mutable state) per job. This is currently safe only because runCli() dispatches to a subprocess-per-job whenever jobs.length > 1 (Line 455-497), so the in-process pLimit(workers) loop that calls runJob/runAgentJob concurrently (Line 527-558) only ever runs a single job at a time. If that dispatch threshold or batching logic is ever changed, concurrent runAgentJob calls in the same process would race on these env vars and could leak one eval's mock routes/state into another eval's run, silently corrupting eval results.

Consider either asserting this invariant explicitly (e.g., throw if runAgentJob is invoked while a mock state dir is already active) or plumbing these values through function parameters/an options object instead of global process.env, so correctness doesn't depend on an implicit cross-file contract with runCli().

Also applies to: 230-235

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval/src/cli/run.ts` around lines 141 - 188, The mock environment
setup in run.ts depends on an implicit “one job per process” guarantee because
runJob/runAgentJob mutate process.env globals like EVAL_MOCK_BIN_DIR,
EVAL_MOCK_ROUTES_DIRS, and EVAL_MOCK_STATE_DIR. Fix this by making the invariant
explicit in the job entry path (for example in runAgentJob/runJob) or by passing
mock env values through parameters/options instead of global process.env, so
concurrent runs can’t race and leak state between evals.

176-181: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate per-eval routes-dir resolution with sandbox-runner.ts.

This evalRoutesDir existence check and EVAL_MOCK_ROUTES_DIRS assignment duplicates the same logic in packages/eval/src/cli/sandbox-runner.ts (Lines 106-109). Consider extracting a small shared helper (e.g., in @a0/eval-core) that both the local and sandboxed runners call, to keep the "routes/ next to PROMPT.md" convention defined in one place.

♻️ Proposed shared helper
+// packages/eval-core/src/utils/mock-routes.ts
+export function resolveEvalMockRoutesDir(evalPath: string): string | undefined {
+  const dir = join(evalPath, 'routes');
+  return existsSync(dir) ? dir : undefined;
+}

Then in both call sites:

-const evalRoutesDir = join(evalDef.path, 'routes');
-if (existsSync(evalRoutesDir)) {
-  process.env.EVAL_MOCK_ROUTES_DIRS = evalRoutesDir;
-} else {
-  delete process.env.EVAL_MOCK_ROUTES_DIRS;
-}
+const evalRoutesDir = resolveEvalMockRoutesDir(evalDef.path);
+if (evalRoutesDir) {
+  process.env.EVAL_MOCK_ROUTES_DIRS = evalRoutesDir;
+} else {
+  delete process.env.EVAL_MOCK_ROUTES_DIRS;
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval/src/cli/run.ts` around lines 176 - 181, The per-eval routes
directory resolution in run.ts duplicates the same “routes next to PROMPT.md”
logic already used by sandbox-runner.ts, so extract it into a small shared
helper in `@a0/eval-core` and have both runners call that helper instead of
reimplementing the existsSync/process.env.EVAL_MOCK_ROUTES_DIRS handling. Use
the existing evalRoutesDir setup in run.ts and the matching code in
sandbox-runner.ts as the locations to replace with the shared function so the
convention is defined in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/eval-core/tests/auth0-mock.test.ts`:
- Around line 17-18: The fixture route is being written into the shared mocks
routes tree, which can leak between test runs; update the auth0 mock test setup
to use a per-test temp routes directory instead of ROUTES_DIR under
apps/auth0-evals/mocks/routes. Adjust the auth0-mock.test.ts fixture creation
logic in beforeEach so zz-mock-test-fixture.sh is created under a temporary
directory, and make sure the mock dispatcher points to that temp path rather
than the shared route set.

---

Nitpick comments:
In `@packages/eval/src/cli/run.ts`:
- Around line 141-188: The mock environment setup in run.ts depends on an
implicit “one job per process” guarantee because runJob/runAgentJob mutate
process.env globals like EVAL_MOCK_BIN_DIR, EVAL_MOCK_ROUTES_DIRS, and
EVAL_MOCK_STATE_DIR. Fix this by making the invariant explicit in the job entry
path (for example in runAgentJob/runJob) or by passing mock env values through
parameters/options instead of global process.env, so concurrent runs can’t race
and leak state between evals.
- Around line 176-181: The per-eval routes directory resolution in run.ts
duplicates the same “routes next to PROMPT.md” logic already used by
sandbox-runner.ts, so extract it into a small shared helper in `@a0/eval-core` and
have both runners call that helper instead of reimplementing the
existsSync/process.env.EVAL_MOCK_ROUTES_DIRS handling. Use the existing
evalRoutesDir setup in run.ts and the matching code in sandbox-runner.ts as the
locations to replace with the shared function so the convention is defined in
one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 435858eb-cb92-4e6c-97d8-e965239502db

📥 Commits

Reviewing files that changed from the base of the PR and between 3669985 and 819fe16.

📒 Files selected for processing (13)
  • apps/auth0-evals/mocks/README.md
  • apps/auth0-evals/mocks/auth0
  • apps/auth0-evals/mocks/lib.sh
  • apps/auth0-evals/mocks/routes/README.md
  • docker/Dockerfile
  • docs/superpowers/specs/2026-07-07-mock-location-design.md
  • packages/eval-core/src/graders/engine.ts
  • packages/eval-core/src/graders/executors/llm-judge.ts
  • packages/eval-core/src/utils/env.ts
  • packages/eval-core/tests/auth0-mock.test.ts
  • packages/eval-core/tests/graders/executors.test.ts
  • packages/eval/src/cli/run.ts
  • packages/eval/src/cli/sandbox-runner.ts
💤 Files with no reviewable changes (2)
  • apps/auth0-evals/mocks/routes/README.md
  • apps/auth0-evals/mocks/lib.sh
✅ Files skipped from review due to trivial changes (1)
  • apps/auth0-evals/mocks/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/eval-core/src/utils/env.ts
  • packages/eval-core/tests/graders/executors.test.ts
  • docker/Dockerfile

Comment thread packages/eval-core/tests/auth0-mock.test.ts Outdated
sanchitmehtagit and others added 7 commits July 7, 2026 11:57
…lidation

- Add collectRefProblems() to packages/eval-core/src/mock/manifest.ts to validate fixture/handler refs
- Export loadManifests, collectRefProblems, resolveBody from mock namespace
- Create mock:new script to scaffold <surface>.routes.json, fixtures, and handlers stub
- Create mock:check script to probe mock CLI with METHOD/path probes
- Add mock:new and mock:check to package.json scripts
- Add harness test at packages/eval-core/tests/mock/harness.test.ts to validate all app manifests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🧹 Nitpick comments (10)
docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md (1)

95-115: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Align the handler artifact contract. The spec still reads as if handlers are imported from <feature>.handlers.ts/dist, but the runtime and scaffolder use handlers.js next to the manifest dir. Call out one runtime contract explicitly and keep the generator, dispatcher, and docs on the same path/extension.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md` around
lines 95 - 115, The handler artifact contract is inconsistent between the spec
and the runtime, so update the documentation and any referenced
generator/dispatcher expectations to use one explicit path and extension for
handlers. Align the `HandlerContext`/handler loading description with the actual
runtime contract used by the scaffolder and dispatcher (the `handlers.js` file
beside the manifest directory), and remove or revise any
`<feature>.handlers.ts`/`dist` wording so the generator, runtime, and docs all
point to the same artifact location.
apps/auth0-evals/scripts/mock-check.mjs (1)

6-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same ESLint Node-globals gap as mock-new.mjs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/auth0-evals/scripts/mock-check.mjs` around lines 6 - 17, The CLI script
in mock-check.mjs still relies on Node globals that ESLint flags, matching the
issue in mock-new.mjs. Update the top-level setup around cwd, args, and
process.exit usage so it follows the same pattern as the other mock script,
explicitly using the appropriate Node globals/imports and keeping the argument
parsing and usage handling in sync with mock-new.mjs.

Source: Linters/SAST tools

apps/auth0-evals/scripts/mock-new.mjs (1)

6-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

ESLint no-undef errors on Node globals indicate missing Node env in lint config.

Static analysis flags URL, process, and console as undefined. These are legitimate Node.js globals, so this is a lint-config gap (missing env: node / globals.node) rather than a code defect — but since these are real errors reported for this new file, they will likely fail CI lint if this path is covered by that config.

Also applies to: 63-63

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/auth0-evals/scripts/mock-new.mjs` around lines 6 - 17, The new script
uses Node globals like URL, process, and console, but the lint setup is treating
them as undefined. Update the ESLint configuration to apply a Node
environment/globals override for the mock scripts path so the mock:new script is
linted with Node globals enabled, rather than changing the script itself. Make
sure the override covers the mock-new.mjs file and any related scripts that use
the same Node APIs.

Source: Linters/SAST tools

packages/eval-core/src/mock/state.ts (1)

6-8: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Marker-name collisions between distinct state keys.

markerName replaces every non-[A-Za-z0-9._-] character with _, so distinct keys like feature.a-b and feature.a_b collapse to the same marker file (feature.a_b). Since manifests only require dotted namespacing (Line 17-19 in manifest.ts) and not full name sanitization uniqueness, two routes using differently-punctuated state keys could silently share state.

Low practical risk given current naming conventions, but worth a small hardening.

♻️ Suggested collision-safe encoding
-function markerName(key: string): string {
-  return key.replace(/[^A-Za-z0-9._-]/g, '_');
-}
+function markerName(key: string): string {
+  return Buffer.from(key).toString('base64url');
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/state.ts` around lines 6 - 8, The current marker
naming in markerName can collapse different state keys into the same filename,
so make the encoding collision-safe instead of simple character replacement.
Update the marker generation used by the state mock to preserve uniqueness for
distinct keys like punctuation variants, and keep the existing manifest
namespace handling intact while changing only the transformation logic in
markerName (and any callers in mock state that rely on it).
packages/eval-core/tests/mock/manifest.test.ts (1)

15-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing failure-path coverage for two validateRoute branches.

validateRoute (in manifest.ts Lines 14-16, 20-22) throws for a create/set/reflect route missing state, and for a handler route missing handler. Neither branch is exercised here; only the unknown-verb and un-namespaced-state branches are covered.

✅ Suggested additional test cases
+  it('rejects a create verb missing a state key', () => {
+    writeManifest('bad.routes.json', {
+      surface: 'bad', routes: [{ match: 'POST x', verb: 'create', body: {} }],
+    });
+    expect(() => loadManifests([dir])).toThrow(/state/i);
+  });
+
+  it('rejects a handler verb missing a handler name', () => {
+    writeManifest('bad.routes.json', {
+      surface: 'bad', routes: [{ match: 'GET x', verb: 'handler' }],
+    });
+    expect(() => loadManifests([dir])).toThrow(/handler/i);
+  });

As per path instructions, "Every new function and every logic change must be accompanied by tests in the package where the code lives; new functions need at least one happy-path test and one failure/edge-case test."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/tests/mock/manifest.test.ts` around lines 15 - 44, Add
missing failure-path tests in loadManifests to cover the validateRoute branches
for stateful verbs and handler routes. In manifest.test.ts, extend the existing
describe('loadManifests') block with cases that write a manifest using a
create/set/reflect route without state and a handler route without handler, then
assert loadManifests([dir]) throws with the appropriate error. Keep the tests
alongside the current unknown-verb and namespace checks so validateRoute is
fully covered.

Source: Path instructions

packages/eval-core/src/mock/manifest.ts (1)

58-58: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use join() instead of template-literal path concatenation.

node:path's join is already imported and used at Line 2/51 for path building, but collectRefProblems (and the equivalent construction in engine.ts) builds fixturesDir via string interpolation instead. This is inconsistent within the same file and less robust to trailing slashes in m.dir.

♻️ Suggested fix
-    const fixturesDir = `${m.dir}/fixtures/${m.surface}`;
+    const fixturesDir = join(m.dir ?? '', 'fixtures', m.surface);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/manifest.ts` at line 58, `collectRefProblems` is
building `fixturesDir` with template-string path concatenation instead of the
imported `join()` helper, which is inconsistent and less robust. Update the
`fixturesDir` construction in `manifest.ts` to use `join(m.dir, "fixtures",
m.surface)` and apply the same path-building approach in the equivalent
`engine.ts` logic so both locations use the shared path utility consistently.
packages/eval-core/src/mock/types.ts (1)

20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant unknown | string unions.

unknown | string is equivalent to unknown — the string member is entirely subsumed. As written, the type gives no compile-time signal that body/present/absent can be either an inline JSON value or a fixture-filename string (that distinction is only enforced at runtime in resolveBody via typeof ref !== 'string').

♻️ Suggested type clarification
-  body?: unknown | string;
-  present?: unknown | string;
-  absent?: unknown | string;
+  /** Inline JSON body, or a fixture filename string resolved via resolveBody(). */
+  body?: unknown;
+  present?: unknown;
+  absent?: unknown;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/types.ts` around lines 20 - 22, The `unknown |
string` unions in `types.ts` are redundant and don’t communicate the intended
“inline JSON value or fixture filename” contract. Update the `body`, `present`,
and `absent` property types to use a clearer shape or alias that reflects the
runtime check in `resolveBody` (which branches on `typeof ref !== 'string'`), so
callers can see the intended distinction at compile time and the mock types stay
self-documenting.
packages/eval-core/tests/mock/state.test.ts (1)

7-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing test for cross-instance (marker-file) persistence.

All three tests operate on a single createState() instance. The actual use case — per PR objectives, state persists via marker files across separate CLI invocations (EVAL_MOCK_STATE_DIR) — isn't verified: a fresh createState(dir) call should see keys set by a prior instance pointed at the same dir.

As per coding guidelines, "Every new function and every logic change must be accompanied by tests in the package where the code lives."

✅ Suggested additional test
+  it('persists state across separate createState() instances (same dir)', () => {
+    const s1 = createState(dir);
+    s1.set('cte.action');
+    const s2 = createState(dir);
+    expect(s2.has('cte.action')).toBe(true);
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/tests/mock/state.test.ts` around lines 7 - 30, Add a new
test in createState to verify marker-file persistence across instances: create
one state with createState(dir), set a key, then create a fresh createState(dir)
and assert it can read the same key as present. Use the existing createState,
set, and has symbols so the test covers the cross-invocation behavior backed by
the shared EVAL_MOCK_STATE_DIR directory.

Source: Coding guidelines

packages/eval-core/src/mock/engine.ts (1)

22-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

payload includes the subcommand/method/path, not just the actual request body/args.

argv.join(' ') embeds sub, method, and rawPath alongside any actual data flags, which is unusual for a field named payload. If handler functions do naive substring parsing on payload to extract request data, this noise could cause false matches.

♻️ Possible fix (verify against actual handler usage first)
-  const payload = argv.join(' ');
+  const payload = argv.slice(3).join(' ');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/engine.ts` at line 22, The mock request payload
in the engine currently includes routing metadata from argv rather than only the
request body/args. Update the payload construction in the mock engine logic so
`payload` contains just the actual user-provided data flags, and keep `sub`,
`method`, and `rawPath` separate for dispatching. Verify any consumers of the
mock engine payload still receive the expected body-only content and adjust the
`engine.ts` request assembly accordingly.
packages/eval-core/src/mock/verbs.ts (1)

1-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add focused tests for applyVerb
engine.test.ts exercises the create, reflect (present/absent), static, and handler paths indirectly, but a small packages/eval-core/src/mock/verbs.ts test would make this branching easier to maintain.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/verbs.ts` around lines 1 - 21, Add a focused unit
test suite for applyVerb in verbs.ts to cover each branch directly instead of
relying only on engine.test.ts. Exercise the create/set path, reflect with both
present and absent state, static resolution, and the default handler/undefined
case, using applyVerb plus MockState/RouteDef fixtures to keep the assertions
close to the branching logic.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/auth0-evals/mocks/auth0`:
- Around line 19-24: The dynamic import in the handlers-loading loop uses a bare
absolute path from join(), which is not a valid ESM specifier across platforms.
Update the import in the manifestDirs loop to convert hFile to a file URL first
using pathToFileURL(hFile).href before calling import(), keeping the existing
existsSync and Object.assign logic in place.

In `@apps/auth0-evals/scripts/mock-check.mjs`:
- Around line 28-45: The mock-check script is calling runMockCli with the wrong
argument shape and treating its return value like an array. Update the call in
mock-check.mjs so the argv array is passed first, followed by the config object
and probes/handlers as expected by runMockCli. Then parse the returned JSON
string for each probe before logging method/path/body, instead of iterating
result directly.

In `@docs/superpowers/plans/2026-07-07-declarative-mock-runtime.md`:
- Around line 687-692: The dynamic import in the handler-loading logic uses a
filesystem path directly, which can resolve inconsistently across platforms.
Update the import in the manifest directory scan to use a file URL by converting
the `hFile` path with `pathToFileURL(hFile).href` before calling `import`,
keeping the rest of the `handlers` merge logic unchanged.

In `@docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md`:
- Around line 49-67: The architecture layout fence in the declarative mock
runtime design doc is missing a language tag, which triggers markdownlint MD040.
Update the fenced block in the mock runtime directory layout section to use a
text/plaintext language label so the docs lint passes.

In `@packages/eval-core/src/mock/engine.ts`:
- Around line 29-38: The mock response path in engine.ts can return a non-string
when a handler or verb result is undefined because JSON.stringify(undefined)
yields undefined. Update the logic in the route handling block around runMockCli
so the handler call and applyVerb result are checked before stringifying, and
coerce missing responses to null or throw a clear validation error instead of
returning undefined. Use the handler lookup and applyVerb branches to locate the
fix.

In `@packages/eval-core/tests/mock/harness.test.ts`:
- Line 5: The MOCKS path in the harness test points to the wrong directory, so
loadManifests() never sees the real manifests and the assertions remain vacuous.
Update the MOCKS constant in harness.test.ts to resolve to the actual
apps/auth0-evals/mocks location, using the existing fileURLToPath/new URL setup
so the harness exercises real manifests in the test.

---

Nitpick comments:
In `@apps/auth0-evals/scripts/mock-check.mjs`:
- Around line 6-17: The CLI script in mock-check.mjs still relies on Node
globals that ESLint flags, matching the issue in mock-new.mjs. Update the
top-level setup around cwd, args, and process.exit usage so it follows the same
pattern as the other mock script, explicitly using the appropriate Node
globals/imports and keeping the argument parsing and usage handling in sync with
mock-new.mjs.

In `@apps/auth0-evals/scripts/mock-new.mjs`:
- Around line 6-17: The new script uses Node globals like URL, process, and
console, but the lint setup is treating them as undefined. Update the ESLint
configuration to apply a Node environment/globals override for the mock scripts
path so the mock:new script is linted with Node globals enabled, rather than
changing the script itself. Make sure the override covers the mock-new.mjs file
and any related scripts that use the same Node APIs.

In `@docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md`:
- Around line 95-115: The handler artifact contract is inconsistent between the
spec and the runtime, so update the documentation and any referenced
generator/dispatcher expectations to use one explicit path and extension for
handlers. Align the `HandlerContext`/handler loading description with the actual
runtime contract used by the scaffolder and dispatcher (the `handlers.js` file
beside the manifest directory), and remove or revise any
`<feature>.handlers.ts`/`dist` wording so the generator, runtime, and docs all
point to the same artifact location.

In `@packages/eval-core/src/mock/engine.ts`:
- Line 22: The mock request payload in the engine currently includes routing
metadata from argv rather than only the request body/args. Update the payload
construction in the mock engine logic so `payload` contains just the actual
user-provided data flags, and keep `sub`, `method`, and `rawPath` separate for
dispatching. Verify any consumers of the mock engine payload still receive the
expected body-only content and adjust the `engine.ts` request assembly
accordingly.

In `@packages/eval-core/src/mock/manifest.ts`:
- Line 58: `collectRefProblems` is building `fixturesDir` with template-string
path concatenation instead of the imported `join()` helper, which is
inconsistent and less robust. Update the `fixturesDir` construction in
`manifest.ts` to use `join(m.dir, "fixtures", m.surface)` and apply the same
path-building approach in the equivalent `engine.ts` logic so both locations use
the shared path utility consistently.

In `@packages/eval-core/src/mock/state.ts`:
- Around line 6-8: The current marker naming in markerName can collapse
different state keys into the same filename, so make the encoding collision-safe
instead of simple character replacement. Update the marker generation used by
the state mock to preserve uniqueness for distinct keys like punctuation
variants, and keep the existing manifest namespace handling intact while
changing only the transformation logic in markerName (and any callers in mock
state that rely on it).

In `@packages/eval-core/src/mock/types.ts`:
- Around line 20-22: The `unknown | string` unions in `types.ts` are redundant
and don’t communicate the intended “inline JSON value or fixture filename”
contract. Update the `body`, `present`, and `absent` property types to use a
clearer shape or alias that reflects the runtime check in `resolveBody` (which
branches on `typeof ref !== 'string'`), so callers can see the intended
distinction at compile time and the mock types stay self-documenting.

In `@packages/eval-core/src/mock/verbs.ts`:
- Around line 1-21: Add a focused unit test suite for applyVerb in verbs.ts to
cover each branch directly instead of relying only on engine.test.ts. Exercise
the create/set path, reflect with both present and absent state, static
resolution, and the default handler/undefined case, using applyVerb plus
MockState/RouteDef fixtures to keep the assertions close to the branching logic.

In `@packages/eval-core/tests/mock/manifest.test.ts`:
- Around line 15-44: Add missing failure-path tests in loadManifests to cover
the validateRoute branches for stateful verbs and handler routes. In
manifest.test.ts, extend the existing describe('loadManifests') block with cases
that write a manifest using a create/set/reflect route without state and a
handler route without handler, then assert loadManifests([dir]) throws with the
appropriate error. Keep the tests alongside the current unknown-verb and
namespace checks so validateRoute is fully covered.

In `@packages/eval-core/tests/mock/state.test.ts`:
- Around line 7-30: Add a new test in createState to verify marker-file
persistence across instances: create one state with createState(dir), set a key,
then create a fresh createState(dir) and assert it can read the same key as
present. Use the existing createState, set, and has symbols so the test covers
the cross-invocation behavior backed by the shared EVAL_MOCK_STATE_DIR
directory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5fbe9e3f-686a-4ed2-89de-75bbca3fce22

📥 Commits

Reviewing files that changed from the base of the PR and between 819fe16 and 3af4340.

📒 Files selected for processing (21)
  • apps/auth0-evals/mocks/auth0
  • apps/auth0-evals/mocks/routes/README.md
  • apps/auth0-evals/package.json
  • apps/auth0-evals/scripts/mock-check.mjs
  • apps/auth0-evals/scripts/mock-new.mjs
  • docs/superpowers/plans/2026-07-07-declarative-mock-runtime.md
  • docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md
  • packages/eval-core/src/index.ts
  • packages/eval-core/src/mock/engine.ts
  • packages/eval-core/src/mock/index.ts
  • packages/eval-core/src/mock/manifest.ts
  • packages/eval-core/src/mock/matcher.ts
  • packages/eval-core/src/mock/state.ts
  • packages/eval-core/src/mock/types.ts
  • packages/eval-core/src/mock/verbs.ts
  • packages/eval-core/tests/auth0-mock.test.ts
  • packages/eval-core/tests/mock/engine.test.ts
  • packages/eval-core/tests/mock/harness.test.ts
  • packages/eval-core/tests/mock/manifest.test.ts
  • packages/eval-core/tests/mock/matcher.test.ts
  • packages/eval-core/tests/mock/state.test.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/auth0-evals/mocks/routes/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/eval-core/tests/auth0-mock.test.ts

Comment on lines +19 to +24
// Load handler maps from every handlers.js next to a manifest dir.
const handlers = {};
for (const dir of manifestDirs) {
const hFile = join(dir, 'handlers.js');
if (existsSync(hFile)) Object.assign(handlers, (await import(hFile)).default ?? {});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Dynamic import of bare absolute path violates guideline; breaks on Windows and with special characters.

hFile is an absolute path built with join(), then passed directly to import(). Per Node's ESM loader, absolute paths must be valid file: URLs — bare absolute paths only work by accident on POSIX (via URL path-reset semantics) and definitively break on Windows (Only URLs with a scheme in: file, data are supported... On Windows, absolute paths must be valid file:// URLs), and any path containing #, ?, spaces, or other reserved URL characters will resolve incorrectly since ESM specifiers are parsed as URLs.

🔧 Proposed fix
-import { existsSync } from 'node:fs';
+import { existsSync } from 'node:fs';
+import { pathToFileURL } from 'node:url';
 ...
-  if (existsSync(hFile)) Object.assign(handlers, (await import(hFile)).default ?? {});
+  if (existsSync(hFile)) Object.assign(handlers, (await import(pathToFileURL(hFile).href)).default ?? {});

As per coding guidelines, "For dynamic imports of absolute paths, use pathToFileURL(path).href instead of importing bare absolute paths."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Load handler maps from every handlers.js next to a manifest dir.
const handlers = {};
for (const dir of manifestDirs) {
const hFile = join(dir, 'handlers.js');
if (existsSync(hFile)) Object.assign(handlers, (await import(hFile)).default ?? {});
}
import { existsSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
// Load handler maps from every handlers.js next to a manifest dir.
const handlers = {};
for (const dir of manifestDirs) {
const hFile = join(dir, 'handlers.js');
if (existsSync(hFile)) Object.assign(handlers, (await import(pathToFileURL(hFile).href)).default ?? {});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/auth0-evals/mocks/auth0` around lines 19 - 24, The dynamic import in the
handlers-loading loop uses a bare absolute path from join(), which is not a
valid ESM specifier across platforms. Update the import in the manifestDirs loop
to convert hFile to a file URL first using pathToFileURL(hFile).href before
calling import(), keeping the existing existsSync and Object.assign logic in
place.

Source: Coding guidelines

Comment thread apps/auth0-evals/scripts/mock-check.mjs
Comment on lines +687 to +692
// Load handler maps from every <surface>.handlers.js next to a manifest dir.
const handlers = {};
for (const dir of manifestDirs) {
const hFile = join(dir, 'handlers.js');
if (existsSync(hFile)) Object.assign(handlers, (await import(hFile)).default ?? {});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='docs/superpowers/plans/2026-07-07-declarative-mock-runtime.md'

printf '== context ==\n'
wc -l "$file"
printf '\n== targeted lines ==\n'
sed -n '670,705p' "$file"

printf '\n== search for related import usage ==\n'
rg -n "import\(hFile\)|pathToFileURL|handlers\.js|manifestDirs" "$file" .

Repository: auth0/auth0-evals

Length of output: 9490


Import handlers via a file URL. import(hFile) is using a filesystem path here; convert it with pathToFileURL(hFile).href first so the dynamic import resolves consistently across platforms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-07-07-declarative-mock-runtime.md` around lines
687 - 692, The dynamic import in the handler-loading logic uses a filesystem
path directly, which can resolve inconsistently across platforms. Update the
import in the manifest directory scan to use a file URL by converting the
`hFile` path with `pathToFileURL(hFile).href` before calling `import`, keeping
the rest of the `handlers` merge logic unchanged.

Source: Coding guidelines

Comment on lines +49 to +67
```
packages/eval-core/src/mock/ # GENERIC engine (no Auth0 knowledge)
dispatcher.ts # parse argv, normalize (configurable), route, respond
manifest.ts # load + schema-validate *.routes.json
state.ts # marker-file state under EVAL_MOCK_STATE_DIR
verbs.ts # create / set / reflect / static
types.ts # RouteManifest, HandlerContext, ...

apps/auth0-evals/mocks/
auth0 # thin entrypoint: #!/usr/bin/env node → runs the engine
# with Auth0 config (binary=auth0, normalize=/api/v2)
<feature>.routes.json # app-level shared manifests (optional)
fixtures/<feature>/*.json # response bodies
<feature>.handlers.ts # computed-field handlers (optional)

apps/auth0-evals/src/evals/<eval>/mock/ # OPTIONAL per-eval, discovered via EVAL_MOCK_ROUTES_DIRS
<feature>.routes.json
fixtures/... , handlers.ts
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Tag the architecture fence with a language.

The bare fence here triggers markdownlint MD040. Mark it as text/plaintext so the docs lint stays green. As per static analysis, fenced code blocks should have a language specified.

Suggested fix
-```
+```text
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
packages/eval-core/src/mock/ # GENERIC engine (no Auth0 knowledge)
dispatcher.ts # parse argv, normalize (configurable), route, respond
manifest.ts # load + schema-validate *.routes.json
state.ts # marker-file state under EVAL_MOCK_STATE_DIR
verbs.ts # create / set / reflect / static
types.ts # RouteManifest, HandlerContext, ...
apps/auth0-evals/mocks/
auth0 # thin entrypoint: #!/usr/bin/env node → runs the engine
# with Auth0 config (binary=auth0, normalize=/api/v2)
<feature>.routes.json # app-level shared manifests (optional)
fixtures/<feature>/*.json # response bodies
<feature>.handlers.ts # computed-field handlers (optional)
apps/auth0-evals/src/evals/<eval>/mock/ # OPTIONAL per-eval, discovered via EVAL_MOCK_ROUTES_DIRS
<feature>.routes.json
fixtures/... , handlers.ts
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 49-49: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-07-07-declarative-mock-runtime-design.md` around
lines 49 - 67, The architecture layout fence in the declarative mock runtime
design doc is missing a language tag, which triggers markdownlint MD040. Update
the fenced block in the mock runtime directory layout section to use a
text/plaintext language label so the docs lint passes.

Source: Linters/SAST tools

Comment on lines +29 to +38
if (route.verb === 'handler') {
const fn = handlers[route.handler!];
if (!fn) continue; // unknown handler → keep searching, then fallthrough
return JSON.stringify(fn({ method: (method ?? '').toLowerCase(), path, payload, state }));
}
// manifestDirs entries hold fixtures/<surface>/ alongside the manifest.
const fixturesDir = `${manifest.dir}/fixtures/${manifest.surface}`;
return JSON.stringify(applyVerb(route, state, fixturesDir));
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and surrounding definitions.
git ls-files packages/eval-core/src/mock/engine.ts
wc -l packages/eval-core/src/mock/engine.ts
cat -n packages/eval-core/src/mock/engine.ts | sed -n '1,220p'

Repository: auth0/auth0-evals

Length of output: 2303


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file structure first, then inspect related symbols and call sites.
ast-grep outline packages/eval-core/src/mock/engine.ts --view expanded
rg -n "runMockCli|applyVerb|HandlerFn|JSON.stringify|never throws|Promise<string>" packages/eval-core/src/mock/engine.ts packages/eval-core/src -S

Repository: auth0/auth0-evals

Length of output: 2565


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Focus on the relevant region only.
sed -n '1,160p' packages/eval-core/src/mock/engine.ts

Repository: auth0/auth0-evals

Length of output: 1923


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the full target file with line numbers.
cat -n packages/eval-core/src/mock/engine.ts

Repository: auth0/auth0-evals

Length of output: 2224


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect consumers and related mock helpers for error handling and return-shape expectations.
rg -n "runMockCli\(|runMockCli\b|stdout\.write|process\.stdout\.write|console\.log|never throws|Promise<string>|JSON.stringify\(applyVerb|HandlerFn|applyVerb\(" packages/eval-core/src -S

echo '--- verbs.ts ---'
cat -n packages/eval-core/src/mock/verbs.ts

echo '--- types.ts ---'
cat -n packages/eval-core/src/mock/types.ts

Repository: auth0/auth0-evals

Length of output: 3106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the CLI entrypoint that consumes runMockCli.
fd -a '.*' packages/eval-core/src | sed -n '1,80p'
echo '--- search cli files ---'
rg -n "runMockCli|stdout\.write|process\.stdout\.write|console\.log|writeFile|stderr" packages/eval-core/src -S

Repository: auth0/auth0-evals

Length of output: 5677


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the CLI entrypoint and the verb helper.
cat -n packages/eval-core/src/mock/verbs.ts
echo '---'
rg -n "runMockCli|stdout\.write|process\.stdout\.write|console\.log|never throws|Promise<string>" packages/eval-core/src -S

Repository: auth0/auth0-evals

Length of output: 1463


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find any documentation of the mock engine's error/output contract.
rg -n "mock.*never throws|never throws|runMockCli|auth0 \(mock\)|Successfully logged in \(mock\)" -S .

Repository: auth0/auth0-evals

Length of output: 5412


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect applyVerb and any documentation for the mock engine's error contract.
cat -n packages/eval-core/src/mock/verbs.ts
echo '---'
rg -n "never throws|runMockCli|mock engine|auth0 \(mock\)|Successfully logged in \(mock\)" packages/eval-core/src packages/eval-core/README* README* -S

Repository: auth0/auth0-evals

Length of output: 1564


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect resolveBody and the existing mock tests for handler behavior.
cat -n packages/eval-core/src/mock/manifest.ts
echo '--- tests ---'
cat -n packages/eval-core/tests/mock/engine.test.ts

Repository: auth0/auth0-evals

Length of output: 6509


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the mock CLI wrapper to understand whether engine exceptions are expected to surface.
cat -n apps/auth0-evals/mocks/auth0
echo '---'
cat -n apps/auth0-evals/scripts/mock-check.mjs

Repository: auth0/auth0-evals

Length of output: 3213


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect resolveBody semantics directly.
rg -n "export function resolveBody|function resolveBody|resolveBody\(" packages/eval-core/src/mock/manifest.ts packages/eval-core/src/mock -S
cat -n packages/eval-core/src/mock/manifest.ts

Repository: auth0/auth0-evals

Length of output: 4028


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any explicit contract around undefined values in mock responses.
rg -n "undefined|null|Promise<string>|JSON.stringify\(.*undefined|handler.*undefined|runMockCli" packages/eval-core/src packages/eval-core/tests docs -S

Repository: auth0/auth0-evals

Length of output: 17344


Handle undefined mock responses before stringifying packages/eval-core/src/mock/engine.ts:32-36

If a handler or verb result is undefined, JSON.stringify(...) returns undefined, so runMockCli can resolve with a non-string and the CLI prints undefined. Coerce missing results to null or validate them explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/mock/engine.ts` around lines 29 - 38, The mock
response path in engine.ts can return a non-string when a handler or verb result
is undefined because JSON.stringify(undefined) yields undefined. Update the
logic in the route handling block around runMockCli so the handler call and
applyVerb result are checked before stringifying, and coerce missing responses
to null or throw a clear validation error instead of returning undefined. Use
the handler lookup and applyVerb branches to locate the fix.

import { fileURLToPath } from 'node:url';
import { loadManifests, collectRefProblems } from '../../src/mock/manifest.js';

const MOCKS = fileURLToPath(new URL('../../../apps/auth0-evals/mocks/', import.meta.url));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
node -e "
const { fileURLToPath } = require('url');
const base = 'file:///repo/packages/eval-core/tests/mock/harness.test.ts';
console.log(fileURLToPath(new URL('../../../apps/auth0-evals/mocks/', base)));
console.log(fileURLToPath(new URL('../../../../apps/auth0-evals/mocks/', base)));
"

Repository: auth0/auth0-evals

Length of output: 224


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== harness test ==\n'
sed -n '1,120p' packages/eval-core/tests/mock/harness.test.ts

printf '\n== search loadManifests ==\n'
rg -n "function loadManifests|const loadManifests|loadManifests\\(" packages/eval-core -g '!**/dist/**' -g '!**/build/**'

printf '\n== candidate files ==\n'
fd -a "loadManifests" packages/eval-core

Repository: auth0/auth0-evals

Length of output: 2062


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== manifest.ts ==\n'
sed -n '1,220p' packages/eval-core/src/mock/manifest.ts

printf '\n== mock path existence ==\n'
python3 - <<'PY'
from pathlib import Path
paths = [
    Path('apps/auth0-evals/mocks'),
    Path('packages/apps/auth0-evals/mocks'),
]
for p in paths:
    print(f"{p}: exists={p.exists()} is_dir={p.is_dir()}")
    if p.exists():
        for child in sorted(p.glob('*'))[:10]:
            print('  ', child)
PY

printf '\n== manifest tests ==\n'
sed -n '1,120p' packages/eval-core/tests/mock/manifest.test.ts

Repository: auth0/auth0-evals

Length of output: 4754


MOCKS points at a non-existent directory, so the harness never exercises real manifests.

new URL('../../../apps/auth0-evals/mocks/', import.meta.url) resolves to /repo/packages/apps/auth0-evals/mocks/, but the manifests live under /repo/apps/auth0-evals/mocks/. Because loadManifests() skips missing directories, both checks stay vacuous.

🔧 Proposed fix
-const MOCKS = fileURLToPath(new URL('../../../apps/auth0-evals/mocks/', import.meta.url));
+const MOCKS = fileURLToPath(new URL('../../../../apps/auth0-evals/mocks/', import.meta.url));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const MOCKS = fileURLToPath(new URL('../../../apps/auth0-evals/mocks/', import.meta.url));
const MOCKS = fileURLToPath(new URL('../../../../apps/auth0-evals/mocks/', import.meta.url));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/tests/mock/harness.test.ts` at line 5, The MOCKS path in
the harness test points to the wrong directory, so loadManifests() never sees
the real manifests and the assertions remain vacuous. Update the MOCKS constant
in harness.test.ts to resolve to the actual apps/auth0-evals/mocks location,
using the existing fileURLToPath/new URL setup so the harness exercises real
manifests in the test.

sanchitmehtagit and others added 2 commits July 7, 2026 14:50
…fold

- mock-check: fix runMockCli signature from (config, probes) to (argv, config, handlers).
  Build argv per probe, loop over each, manage temp state dir with try/finally, load
  handlers.js if present. Output: "METHOD path -> body" per probe.
- mock-new: fix reflect route scaffold from body:'example.json' to present:'example.json',
  absent:[] to comply with reflect verb requirements.
- Both validated: manifest loads sans errors, mock-check runs without crashing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds scaffolds/cli/platform-context/AGENTS.md — a generic, feature-agnostic
platform-engineer context for CLI tenant-config evals. Unlike the per-feature
cli/auth0 and cli/cte scaffolds (which embed the exact guardian/actions commands
the graders check, letting an agent copy-paste the answer), this ships only the
generic `auth0 api <METHOD> <path>` passthrough shape — so evals test whether the
agent knows the tenant flow.

Rewords mocks/routes/README.md: manifests co-locate in each consuming eval's
routes/ dir; the shared mocks/ dir holds only feature-neutral surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/auth0-evals/scripts/mock-check.mjs`:
- Line 37: The handlers import in mock-check.mjs is using a filesystem path
directly, but handlersFile is already an absolute path from fileURLToPath.
Update the import to use pathToFileURL(handlersFile).href and add pathToFileURL
from node:url so the dynamic import works reliably; keep the change localized
around the handlers assignment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 65cea29e-04f3-44d1-a824-765cc32b2a4a

📥 Commits

Reviewing files that changed from the base of the PR and between 3af4340 and adb085d.

📒 Files selected for processing (4)
  • apps/auth0-evals/mocks/routes/README.md
  • apps/auth0-evals/scripts/mock-check.mjs
  • apps/auth0-evals/scripts/mock-new.mjs
  • apps/auth0-evals/src/evals/scaffolds/cli/platform-context/AGENTS.md
✅ Files skipped from review due to trivial changes (2)
  • apps/auth0-evals/src/evals/scaffolds/cli/platform-context/AGENTS.md
  • apps/auth0-evals/mocks/routes/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/auth0-evals/scripts/mock-new.mjs

let handlers = {};
const handlersFile = join(MOCKS, 'handlers.js');
if (existsSync(handlersFile)) {
handlers = (await import(handlersFile)).default ?? {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect lines 1-28 to find the MOCKS definition
sed -n '1,28p' apps/auth0-evals/scripts/mock-check.mjs

Repository: auth0/auth0-evals

Length of output: 968


Use pathToFileURL for the handlers import

MOCKS is built from fileURLToPath(...), so handlersFile is an absolute filesystem path. Import it as await import(pathToFileURL(handlersFile).href) and add pathToFileURL from node:url.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/auth0-evals/scripts/mock-check.mjs` at line 37, The handlers import in
mock-check.mjs is using a filesystem path directly, but handlersFile is already
an absolute path from fileURLToPath. Update the import to use
pathToFileURL(handlersFile).href and add pathToFileURL from node:url so the
dynamic import works reliably; keep the change localized around the handlers
assignment.

Source: Coding guidelines

sanchitmehtagit and others added 2 commits July 8, 2026 17:31
A CLI tenant-config eval's shared AGENTS.md is platform *context* (operating-model
guidance every such eval inherits), not a *scaffold* (starter code), and it should
not have to be declared per-eval. Attach it by convention instead:

- loadSharedContextForEval: an eval that ships a `routes/` dir is mocking a CLI
  (it drives EVAL_MOCK_ROUTES_DIRS), i.e. it's a CLI tenant-config eval, so it
  auto-inherits src/evals/contexts/cli-platform/AGENTS.md — no frontmatter field.
  Merged on top of the scaffold record; writeAgentGuidance injects it into the
  runner's context file. Wins over a scaffold AGENTS.md.
- Move the shared file scaffolds/cli/platform-context/AGENTS.md →
  contexts/cli-platform/AGENTS.md; remove the now-empty scaffolds/cli/.
- Tests: injects when routes/ present; no-op without routes/; no-op (no throw)
  when the shared file is absent; shared context wins over scaffold AGENTS.md.

Safe by default: no routes/ dir or missing shared file → nothing injected, so
every existing scaffold-based eval is unchanged. No workspace.ts/run.ts/type change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ADDING_EVALS.md: explain that an eval shipping a routes/ dir auto-inherits
src/evals/contexts/cli-platform/AGENTS.md — shared platform context (guidance),
distinct from a scaffold (starter code). No frontmatter field; edit one file to
change the context for every CLI tenant-config eval.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sanchitmehtagit added a commit that referenced this pull request Jul 8, 2026
…text

The mfa_tenant_cli eval no longer declares a scaffold. Its routes/ dir now
triggers auto-injection of the shared src/evals/contexts/cli-platform/AGENTS.md
(the platform context is guidance shared across all CLI-config evals, not
per-eval starter code). Removes the leaky per-feature scaffolds/cli/platform-context.

Depends on the loader convention landing on plumbing (#82); on rebase, the
routes/-triggered injection supplies the platform context.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sanchitmehtagit added a commit that referenced this pull request Jul 8, 2026
…text

The cte_tenant_cli eval no longer declares a scaffold. Its routes/ dir triggers
auto-injection of the shared src/evals/contexts/cli-platform/AGENTS.md (platform
context shared across all CLI-config evals, not per-eval starter code). Removes
the leaky per-feature scaffolds/cli/platform-context.

Depends on the loader convention landing on plumbing (#82).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

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)
packages/eval-core/src/loader.ts (1)

112-114: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Error message hardcodes PROMPT.md even when a custom promptFile is used.

parsePromptMd throws 'PROMPT.md not found' regardless of the actual filename. When evalConfig.promptFile is set (e.g., CUSTOM.md), this message is misleading during debugging.

Proposed fix
   if (!existsSync(promptPath)) {
-    throw new EvalConfigError('PROMPT.md not found', promptPath);
+    throw new EvalConfigError(`${basename(promptPath)} not found`, promptPath);
   }

Ensure basename is imported from node:path (with node: prefix per coding guidelines).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/src/loader.ts` around lines 112 - 114, The error message
in parsePromptMd is hardcoded to PROMPT.md, so it can be misleading when
evalConfig.promptFile points to a different file. Update the missing-file branch
to derive the displayed filename from the actual prompt path or configured
promptFile, and keep throwing EvalConfigError with that dynamic name. Also
ensure basename is imported from node:path using the node: prefix so the message
can reference the correct file cleanly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/ADDING_EVALS.md`:
- Line 323: The Mock CLI stubs reference in this section points to the wrong
AGENTS.md location. Update the link so it targets the shared CLI context file
used by CLI evals, and keep the surrounding guidance aligned with the existing
routes/ and scaffold notes in ADDING_EVALS.md so readers are directed to the
correct source.

In `@packages/eval-core/tests/loader.test.ts`:
- Around line 324-383: Add a regression test in loadEval to cover a non-default
evalConfig.promptFile, since loader.ts now supports a custom prompt filename
path. Extend the existing loader.test.ts coverage by creating an eval that uses
a prompt file name other than the default and assert loadEval reads that file
correctly, using the loadEval helper and EVAL_CONFIG setup to verify the custom
promptFile behavior.

---

Outside diff comments:
In `@packages/eval-core/src/loader.ts`:
- Around line 112-114: The error message in parsePromptMd is hardcoded to
PROMPT.md, so it can be misleading when evalConfig.promptFile points to a
different file. Update the missing-file branch to derive the displayed filename
from the actual prompt path or configured promptFile, and keep throwing
EvalConfigError with that dynamic name. Also ensure basename is imported from
node:path using the node: prefix so the message can reference the correct file
cleanly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 212f726b-d44b-452f-b5ad-1d85d7fe993e

📥 Commits

Reviewing files that changed from the base of the PR and between adb085d and 57f779e.

📒 Files selected for processing (4)
  • apps/auth0-evals/src/evals/contexts/cli-platform/AGENTS.md
  • docs/ADDING_EVALS.md
  • packages/eval-core/src/loader.ts
  • packages/eval-core/tests/loader.test.ts
💤 Files with no reviewable changes (1)
  • apps/auth0-evals/src/evals/contexts/cli-platform/AGENTS.md

Comment thread docs/ADDING_EVALS.md

> **An eval that ships a `routes/` dir automatically inherits `src/evals/contexts/cli-platform/AGENTS.md`.**

A `routes/` dir means the eval mocks the Auth0 CLI (see [Mock CLI stubs](../AGENTS.md)), i.e. it's a CLI tenant-config eval — so it gets the shared "you are a platform engineer; use the Auth0 CLI; it's already authenticated" context injected into the agent's native context file. No frontmatter field is needed (and CLI evals declare no `scaffold`). The shared file is generic — it names no feature-specific commands, so it never leaks a grader's expected answer. Edit that one file to change the context for every CLI eval at once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the root AGENTS.md exists (the link target from docs/ADDING_EVALS.md)
ls -la AGENTS.md 2>/dev/null && echo "EXISTS" || echo "NOT FOUND"

# Also check if there's a mock-related AGENTS.md elsewhere
fd -t f "AGENTS.md" --max-depth 3 | head -20

Repository: auth0/auth0-evals

Length of output: 232


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file map =="
fd -H -t f 'AGENTS.md' . 
echo
echo "== contexts/cli-platform =="
fd -H -t f '.' contexts/cli-platform 2>/dev/null || true
echo
echo "== docs/ADDING_EVALS.md around cited lines =="
sed -n '315,330p' docs/ADDING_EVALS.md
echo
echo "== root AGENTS.md excerpt =="
grep -n -A4 -B4 'Mock CLI stubs\|cli-platform\|Auth0 CLI' AGENTS.md || true
echo
echo "== search for mock cli stubs references =="
rg -n 'Mock CLI stubs|cli-platform|contexts/cli-platform|AGENTS.md' -S docs AGENTS.md contexts -g '!**/.git/**' || true

Repository: auth0/auth0-evals

Length of output: 7109


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs/ADDING_EVALS.md (around link) =="
sed -n '318,326p' docs/ADDING_EVALS.md

echo
echo "== root AGENTS.md around any CLI/context guidance =="
rg -n -A3 -B3 'AGENTS\.md|cli-platform|Mock CLI stubs|injected agent guidance|routes/ dir' AGENTS.md

echo
echo "== moved shared context file =="
sed -n '1,80p' apps/auth0-evals/src/evals/contexts/cli-platform/AGENTS.md

Repository: auth0/auth0-evals

Length of output: 6309


Update the Mock CLI stubs link
../AGENTS.md points to the repo-root AGENTS.md; the shared CLI context now lives at apps/auth0-evals/src/evals/contexts/cli-platform/AGENTS.md. Point the link there or it will keep sending readers to the wrong file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ADDING_EVALS.md` at line 323, The Mock CLI stubs reference in this
section points to the wrong AGENTS.md location. Update the link so it targets
the shared CLI context file used by CLI evals, and keep the surrounding guidance
aligned with the existing routes/ and scaffold notes in ADDING_EVALS.md so
readers are directed to the correct source.

Comment on lines +324 to +383
// ── shared CLI platform-context auto-injection tests ─────────────────────────
// Convention: an eval that ships a `routes/` dir is a CLI tenant-config eval and
// auto-inherits the shared src/evals/contexts/cli-platform/AGENTS.md — no
// frontmatter field. See loadSharedContextForEval in loader.ts.

const SHARED_CLI_CONTEXT_REL = 'src/evals/contexts/cli-platform';

function makeSharedCliContext(base: string, content = 'You are a platform engineer.\n'): void {
makeScaffoldVariant(base, SHARED_CLI_CONTEXT_REL, { 'AGENTS.md': content });
}

function addRoutesDir(evalDir: string): void {
const routes = join(evalDir, 'routes');
mkdirSync(routes, { recursive: true });
writeFileSync(join(routes, 'guardian.routes.json'), '{"surface":"guardian","routes":[]}');
}

describe('loadEval - shared CLI platform context', () => {
it('auto-injects the shared context AGENTS.md when the eval ships a routes/ dir', async () => {
makeSharedCliContext(tmpBase);
const evalDir = makeEvalDir(tmpBase);
addRoutesDir(evalDir);

const result = await loadEval(EVAL_CONFIG, tmpBase);

expect(result.scaffold['AGENTS.md']).toContain('You are a platform engineer');
});

it('does not inject context when the eval has no routes/ dir', async () => {
makeSharedCliContext(tmpBase);
makeEvalDir(tmpBase); // no routes/

const result = await loadEval(EVAL_CONFIG, tmpBase);

expect(result.scaffold['AGENTS.md']).toBeUndefined();
});

it('is a no-op (no throw) when routes/ exists but the shared context file is absent', async () => {
const evalDir = makeEvalDir(tmpBase);
addRoutesDir(evalDir); // routes/ present, but no contexts/cli-platform

const result = await loadEval(EVAL_CONFIG, tmpBase);

expect(result.scaffold['AGENTS.md']).toBeUndefined();
});

it('shared context AGENTS.md wins over a scaffold AGENTS.md', async () => {
makeSharedCliContext(tmpBase, 'Shared platform context.\n');
// local scaffold/ also ships an AGENTS.md
const evalDir = makeEvalDir(tmpBase, MINIMAL_PROMPT, DEFAULT_GRADERS, {
'AGENTS.md': 'Local scaffold guidance.\n',
});
addRoutesDir(evalDir);

const result = await loadEval(EVAL_CONFIG, tmpBase);

expect(result.scaffold['AGENTS.md']).toContain('Shared platform context');
expect(result.scaffold['AGENTS.md']).not.toContain('Local scaffold');
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for tests referencing promptFile in the test file
rg -n 'promptFile' packages/eval-core/tests/loader.test.ts

Repository: auth0/auth0-evals

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## promptFile occurrences in repo\n'
rg -n --hidden --glob '!**/node_modules/**' 'promptFile' packages/eval-core || true

printf '\n## loader.ts outline\n'
ast-grep outline packages/eval-core/src/loader.ts --view expanded || true

printf '\n## test files mentioning loadEval or loader\n'
rg -n --hidden --glob 'packages/eval-core/tests/**/*.ts' 'loadEval|loader|promptFile' packages/eval-core/tests || true

Repository: auth0/auth0-evals

Length of output: 6161


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## promptFile occurrences across eval-core\n'
rg -n --hidden --glob '!**/node_modules/**' 'promptFile' packages/eval-core || true

printf '\n## loader.ts around promptFile\n'
line=$(rg -n 'promptFile' packages/eval-core/src/loader.ts | head -n1 | cut -d: -f1 || true)
if [ -n "${line:-}" ]; then
  start=$(( line > 25 ? line - 25 : 1 ))
  end=$(( line + 60 ))
  sed -n "${start},${end}p" packages/eval-core/src/loader.ts | cat -n
fi

printf '\n## tests in packages/eval-core\n'
rg -n --hidden --glob 'packages/eval-core/tests/**/*.ts' 'promptFile|loadEval|loadSharedContextForEval|AGENTS.md' packages/eval-core/tests || true

Repository: auth0/auth0-evals

Length of output: 11330


Add a regression test for promptFile
packages/eval-core/src/loader.ts:49-53 adds a custom prompt filename path; packages/eval-core/tests/loader.test.ts should cover a non-default evalConfig.promptFile.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/eval-core/tests/loader.test.ts` around lines 324 - 383, Add a
regression test in loadEval to cover a non-default evalConfig.promptFile, since
loader.ts now supports a custom prompt filename path. Extend the existing
loader.test.ts coverage by creating an eval that uses a prompt file name other
than the default and assert loadEval reads that file correctly, using the
loadEval helper and EVAL_CONFIG setup to verify the custom promptFile behavior.

Source: Coding guidelines

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