Skip to content

ProDocs 1.1: production adoption and assurance - #15

Merged
boyeesu merged 1 commit into
mainfrom
codex/product-launch
Aug 9, 2026
Merged

ProDocs 1.1: production adoption and assurance#15
boyeesu merged 1 commit into
mainfrom
codex/product-launch

Conversation

@boyeesu

@boyeesu boyeesu commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • add doctor, tutorial, and local product benchmark workflows
  • validate Codex, Claude Code, and OpenCode context quality
  • add complexity and mutation assurance with weekly CI
  • document adoption, troubleshooting, supply-chain risk, benchmarks, and tabletop response evidence
  • repair GitHub-release checksum verification before npm publication

Verification

  • npm run verify:production
  • npm run test:mutation
  • npm run benchmark
  • 73 tests; 93.62% lines, 80.07% branches, 96.34% functions
  • zero production dependency vulnerabilities; 170 verified registry signatures

Release

  • package version 1.1.0
  • v1.0.0 GitHub release remains immutable

Summary by CodeRabbit

  • New Features

    • Added doctor diagnostics for project readiness, configuration, indexing, documentation freshness, and knowledge health.
    • Added tutorial generation for starter projects and local benchmarking for indexing and context quality.
    • Updated agent evaluation results with per-agent performance statistics.
    • Released version 1.1.0 with an expanded getting-started example.
  • Documentation

    • Added adoption, benchmarking, troubleshooting, validation, security, release, and operations guidance.
  • Bug Fixes

    • Improved release checksum verification reliability.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

ProDocs 1.1 adds doctor, tutorial, and benchmark commands, a getting-started example, adoption and assurance documentation, complexity and mutation checks, release verification updates, and refreshed generated knowledge artifacts.

Changes

Adoption and continuous assurance

Layer / File(s) Summary
Onboarding and example project
.github/ISSUE_TEMPLATE/*, README.md, docs/ADOPTION.md, docs/BENCHMARKS.md, docs/TROUBLESHOOTING.md, docs/VALIDATION.md, docs/knowledge/*, examples/getting-started/*, fixtures/evaluation/*
Adds local onboarding, benchmark guidance, adoption feedback collection, troubleshooting, validation gates, a getting-started project, and bounded agent evaluation fixtures.
Project diagnostics and CLI integration
src/doctor.js, src/cli.js, test/adoption.test.js, test/cli-roadmap.test.js, test/package.test.js
Adds readiness checks for runtime, repository, configuration, indexing, generated evidence, integrations, and knowledge health. Exposes structured and human-readable doctor results.
Tutorial project generation
src/tutorial.js, src/cli.js, test/adoption.test.js, test/cli-roadmap.test.js, test/package.test.js
Adds safe tutorial generation with path validation, exclusive file creation, existing-directory rejection, CLI output, and integration coverage.
Benchmark and agent evaluation
src/benchmark.js, src/evaluate.js, src/cli.js, fixtures/evaluation/agents.json, test/cli-roadmap.test.js, test/roadmap.test.js
Adds cold and warm scan benchmarking, context evaluation, cache metrics, per-agent statistics, benchmark artifacts, and failure handling for invalid results.
Quality, mutation, and release controls
scripts/complexity.js, stryker.config.json, .github/workflows/mutation.yml, package.json, docs/{MATURITY,MUTATION_TESTING,RELEASING,ROADMAP,SUPPLY_CHAIN,TABLETOPS}.md, CHANGELOG.md
Adds complexity enforcement, scheduled mutation testing, dependency pinning, release checksum guidance, supply-chain and tabletop records, and version 1.1.0 release metadata.
Generated knowledge and product views
docs/prodocs/*
Refreshes generated maps, manifests, metrics, knowledge graph edges, feature metadata, and audience-specific evidence snapshots.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant diagnoseProject
  participant ProjectScanner
  participant GeneratedManifest
  CLI->>diagnoseProject: request readiness report
  diagnoseProject->>ProjectScanner: scan project in read-only mode
  ProjectScanner-->>diagnoseProject: indexed files and symbols
  diagnoseProject->>GeneratedManifest: compare source and knowledge hashes
  GeneratedManifest-->>diagnoseProject: freshness result
  diagnoseProject-->>CLI: readiness and check results
Loading

Possibly related PRs

  • boyeesu/ProDocs#10: Related release verification and publishing changes in the npm workflow and release documentation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the 1.1 release focus on production adoption and assurance, matching the main changes.
Description check ✅ Passed The description covers the main changes, outcomes, validation results, and release state, so it is mostly complete despite different section headings.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/product-launch

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.

@boyeesu
boyeesu merged commit 7a85e6d into main Aug 9, 2026
8 of 9 checks passed
@boyeesu
boyeesu deleted the codex/product-launch branch August 9, 2026 13:43

@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: 10

🤖 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 @.github/workflows/publish-npm.yml:
- Line 57: Update the checksum verification command in the publish workflow to
resolve archive paths relative to the release directory and fail when any
expected archive is missing. Remove the ineffective ignore-missing behavior so
modified or absent release archives prevent npm publishing.

In `@docs/BENCHMARKS.md`:
- Around line 7-10: Update the benchmark metadata in the ProDocs repository
section to use a documented supported Node.js runtime (20, 22, or 24) and re-run
the published baseline, or explicitly label the existing Node.js 26.4.0 result
as an unsupported-runtime measurement.

In `@docs/prodocs/KNOWLEDGE_HEALTH.md`:
- Around line 7-8: Update the renderer’s unsupported-knowledge empty-state text
to say “No unsupported knowledge is available.”, then regenerate
docs/prodocs/KNOWLEDGE_HEALTH.md using the repository’s documented generation
tooling rather than editing the generated file directly.

In `@docs/RELEASING.md`:
- Around line 63-65: Update the checksum verification command in the releasing
documentation to run from the workspace root and reference the downloaded file
as release/SHA256SUMS, matching the documented release/ path. Alternatively,
explicitly document the required working directory and prefix rewrite, but keep
the command consistent with the checksum entries and the verification example in
TABLETOPS.md.

In `@examples/getting-started/README.md`:
- Around line 3-4: Update the getting-started README instructions to avoid
implying the checked-in example is identical to the output of `prodocs
tutorial`. Align the checked-in example with the generated tutorial from
`src/tutorial.js`, or revise the documented entry points so both paths provide
equivalent tutorial artifacts without suggesting they are interchangeable when
their contents differ.

In `@examples/getting-started/test/delivery.test.js`:
- Around line 5-8: The test for retryDelivery should cover the intermediate
second attempt. In the existing “delivery retries stop after three attempts”
test, add an assertion that retryDelivery(2) returns “retry”, while preserving
the current assertions for attempts 1 and 3.

In `@src/benchmark.js`:
- Around line 11-39: Update benchmarkProject so the cold scan starts from an
isolated or explicitly cleared index, then reuse that same index for the warm
scan to measure cache reuse accurately. Preserve the existing timing and
evaluation output, and add a regression test that invokes benchmarkProject twice
and verifies the second run does not contaminate the first run’s cold
measurement.

In `@src/tutorial.js`:
- Around line 80-86: Wrap the file-creation loop in the tutorial-writing flow
with failure cleanup: if fs.mkdir() or fs.writeFile() throws, call fs.rm() on
destination before rethrowing the original error. Preserve the written tracking
and successful return behavior while ensuring partial files and directories are
removed.

In `@test/adoption.test.js`:
- Around line 90-99: Update the symlink setup in the “tutorial refuses a
symlinked output parent” test to use a junction on Windows and a directory
symbolic link on other platforms, while preserving the existing outside-root
rejection assertion.

In `@test/roadmap.test.js`:
- Around line 491-496: Update the “empty request” test case in the roadmap tests
to explicitly assert that evaluating the missing source returns zero files,
rather than relying only on precision === 1. Add the assertion against the
returned file list or packet using the existing result structure, and apply the
same correction to the related case around the second referenced range.
🪄 Autofix

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: 69692dab-8af3-4fb0-9453-71f7483abd6a

📥 Commits

Reviewing files that changed from the base of the PR and between 30af5e9 and 1f1ff74.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (51)
  • .github/ISSUE_TEMPLATE/adopter-feedback.yml
  • .github/workflows/mutation.yml
  • .github/workflows/publish-npm.yml
  • .gitignore
  • CHANGELOG.md
  • README.md
  • docs/ADOPTION.md
  • docs/BENCHMARKS.md
  • docs/MATURITY.md
  • docs/MUTATION_TESTING.md
  • docs/OPERATIONS.md
  • docs/RELEASING.md
  • docs/ROADMAP.md
  • docs/SUPPLY_CHAIN.md
  • docs/TABLETOPS.md
  • docs/TROUBLESHOOTING.md
  • docs/VALIDATION.md
  • docs/knowledge/features/adoption-assurance.md
  • docs/prodocs/CODE_MAP.md
  • docs/prodocs/FEATURE_MAP.md
  • docs/prodocs/KNOWLEDGE_HEALTH.md
  • docs/prodocs/SYSTEM_OVERVIEW.md
  • docs/prodocs/knowledge.json
  • docs/prodocs/manifest.json
  • docs/prodocs/views/coding-agents.md
  • docs/prodocs/views/operations.md
  • docs/prodocs/views/product.md
  • docs/prodocs/views/security.md
  • docs/prodocs/views/support.md
  • docs/prodocs/views/technical.md
  • examples/getting-started/CODEOWNERS
  • examples/getting-started/README.md
  • examples/getting-started/docs/knowledge/features/delivery-retries.md
  • examples/getting-started/package.json
  • examples/getting-started/prodocs.config.json
  • examples/getting-started/src/delivery.js
  • examples/getting-started/test/delivery.test.js
  • fixtures/evaluation/agents.json
  • package.json
  • scripts/complexity.js
  • src/benchmark.js
  • src/cli.js
  • src/constants.js
  • src/doctor.js
  • src/evaluate.js
  • src/tutorial.js
  • stryker.config.json
  • test/adoption.test.js
  • test/cli-roadmap.test.js
  • test/package.test.js
  • test/roadmap.test.js

--pattern "SHA256SUMS" \
--dir release
(cd release && sha256sum --check SHA256SUMS --ignore-missing)
sha256sum --check release/SHA256SUMS --ignore-missing

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Restore effective checksum verification.

Line 57 checks archive names from the workspace root. The downloaded archives are in release/. With --ignore-missing, missing archives do not fail verification. A modified release archive can then reach npm publish.

Proposed fix
-          sha256sum --check release/SHA256SUMS --ignore-missing
+          (
+            cd release
+            sha256sum --check SHA256SUMS
+          )
📝 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
sha256sum --check release/SHA256SUMS --ignore-missing
(
cd release
sha256sum --check SHA256SUMS
)
🤖 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 @.github/workflows/publish-npm.yml at line 57, Update the checksum
verification command in the publish workflow to resolve archive paths relative
to the release directory and fail when any expected archive is missing. Remove
the ineffective ignore-missing behavior so modified or absent release archives
prevent npm publishing.

Comment thread docs/BENCHMARKS.md
Comment on lines +7 to +10
## ProDocs repository — 2026-08-09

Environment: macOS, Node.js 26.4.0. These are directional local measurements,
not latency guarantees.

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

Run the published baseline on a documented supported runtime.

Line 9 records the benchmark on Node.js 26.4.0. The README documents support for Node.js 20, 22, and 24. Re-run the benchmark on a supported runtime, or label this result as an unsupported-runtime measurement.

🤖 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/BENCHMARKS.md` around lines 7 - 10, Update the benchmark metadata in the
ProDocs repository section to use a documented supported Node.js runtime (20,
22, or 24) and re-run the published baseline, or explicitly label the existing
Node.js 26.4.0 result as an unsupported-runtime measurement.

Comment on lines +7 to +8
| Authored knowledge | 9 |
| Supported | 9 |

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

Fix the unsupported-knowledge empty state before regenerating.

The changed metrics report 9 authored and 9 supported items, with 0 unsupported items. Line [15] still says _No authored knowledge is available._ under Unsupported knowledge. This contradicts the report. Update the renderer to say _No unsupported knowledge is available._, then regenerate this file.

As per path instructions, generated files under docs/prodocs/** must be regenerated through repository tooling and must not be hand-edited.

🤖 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/prodocs/KNOWLEDGE_HEALTH.md` around lines 7 - 8, Update the renderer’s
unsupported-knowledge empty-state text to say “No unsupported knowledge is
available.”, then regenerate docs/prodocs/KNOWLEDGE_HEALTH.md using the
repository’s documented generation tooling rather than editing the generated
file directly.

Source: Path instructions

Comment thread docs/RELEASING.md
Comment on lines +63 to +65
The checksum file records paths relative to the release build workspace. When
downloading into a local directory, preserve the `release/` directory or
rewrite only that known prefix before verification.

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

Make the checksum command match the documented release/ path.

If the downloaded checksum entries retain the release/ prefix, run verification from the workspace root with shasum -a 256 -c release/SHA256SUMS. The command at Line 60 still uses SHA256SUMS without the prefix and does not define a working directory. Update that command or state the required directory and prefix rewrite explicitly.

The reproducible verification in docs/TABLETOPS.md uses release/SHA256SUMS from the workspace root.

Proposed documentation fix
-shasum -a 256 -c SHA256SUMS
+shasum -a 256 -c release/SHA256SUMS
🤖 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/RELEASING.md` around lines 63 - 65, Update the checksum verification
command in the releasing documentation to run from the workspace root and
reference the downloaded file as release/SHA256SUMS, matching the documented
release/ path. Alternatively, explicitly document the required working directory
and prefix rewrite, but keep the command consistent with the checksum entries
and the verification example in TABLETOPS.md.

Comment on lines +3 to +4
Run `prodocs tutorial --output prodocs-tutorial` to create a fresh copy, or use
this checked-in example directly:

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
set -euo pipefail

ast-grep outline src/tutorial.js --items all
rg -n -C 4 'FILES|retryDelivery|prodocs\.config\.json|delivery-retries|getting-started' src/tutorial.js
for file in \
  examples/getting-started/README.md \
  examples/getting-started/prodocs.config.json \
  examples/getting-started/src/delivery.js \
  examples/getting-started/test/delivery.test.js \
  examples/getting-started/docs/knowledge/features/delivery-retries.md
do
  sha256sum "$file"
done

Repository: boyeesu/ProDocs

Length of output: 2861


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact generated file contents and the checked-in content in parallel.
sed -n '1,120p' src/tutorial.js
printf '\n--- examples/getting-started files ---\n'
for file in \
  examples/getting-started/README.md \
  examples/getting-started/prodocs.config.json \
  examples/getting-started/src/delivery.js \
  examples/getting-started/test/delivery.test.js \
  examples/getting-started/docs/knowledge/features/delivery-retries.md
do
  printf '\n!!! %s !!!\n' "$file"
  sed -n '1,140p' "$file"
done

# Programmatic text comparison: compare generated file templates against checked-in file content for each entry, ignoring only trailing newline differences.
python3 - <<'PY'
import json, pathlib, re

contents = {}
for rel in contents_getting_started = []:
    pass
PY

Repository: boyeesu/ProDocs

Length of output: 5696


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib, re, sys

def normalize(s):
    s = s.replace("\r\n", "\n")
    return s.rstrip()

def normalized_json_object(s):
    s = s.strip()
    if not s.startswith("{") or not s.endswith("}"):
        return ("literal", s)
    try:
        return ("json", json.dumps(json.loads(s), indent=2, ensure_ascii=False, sort_keys=True))
    except Exception as exc:
        return ("literal", s)

checks = {
    "src/delivery.js": pathlib.Path("src/tutorial.js"),
    "test/delivery.test.js": pathlib.Path("src/tutorial.js"),
    "docs/knowledge/features/delivery-retries.md": pathlib.Path("src/tutorial.js"),
    "README.md": pathlib.Path("src/tutorial.js"),
    "package.json": pathlib.Path("src/tutorial.js"),
}

def read_file(path):
    if path == pathlib.Path("src/tutorial.js"):
        text = path.read_text("utf8")
        match = re.search(r'const FILES = \{(?P<body>.*?)\n\};', text, re.S)
        if not match:
            print("MISSING FILES", file=sys.stderr)
            sys.exit(1)
        body = "[ " + match.group("body") + " ]"
        # Strip surrounding whitespace, use eval rather than running repo code.
        # The template uses simple JSON-compatible strings and JSON.stringify for package.json.
        return json.loads(body, strict=False)
    else:
        return {str(path): normalize(path.read_text("utf8"))}

generated = read_file(checks["src/delivery.js"])
generated["prodocs.config.json"] = generated.pop("package.json")["name"]

paths = {
    "src/delivery.js": "examples/getting-started/src/delivery.js",
    "test/delivery.test.js": "examples/getting-started/test/delivery.test.js",
    "docs/knowledge/features/delivery-retries.md": "examples/getting-started/docs/knowledge/features/delivery-retries.md",
    "README.md": "examples/getting-started/README.md",
    "package.json": "examples/getting-started/package.json",
    "prodocs.config.json": "examples/getting-started/prodocs.config.json",
    "CODEOWNERS": "examples/getting-started/CODEOWNERS",
}

def gen(path, kind=None):
    value = generated.get(path)
    if value is None:
        raise SystemExit(f"Generated missing {path}")
    return ("json" if kind == "json" else "literal", normalize(value.strip()))

def check(path):
    print(f"\n== {path} ==")
    gen_kind, gen_text = gen(path, kind=(path == "package.json"))
    check_kind, check_text = gen(path, kind=(path == "package.json"))
    if gen_text == check_text:
        print("MATCH")
        return True
    print(f"GENERATED {gen_kind}:")
    print(gen_text)
    print(f"CHECKED-IN {check_kind}:")
    print(check_text)
    return False

ok = True
for path in sorted(paths, key=lambda p: paths[p]):
    if not check(path):
        ok = False

sys.exit(0 if ok else 1)
PY

Repository: boyeesu/ProDocs

Length of output: 677


Avoid presenting this example as the generated tutorial.

prodocs tutorial writes files from src/tutorial.js directly; this checked-in example has different content, including CODEOWNERS and extra package metadata, while the generated README is shorter. Users following either documented entry point should get equivalent tutorial artifacts.

🤖 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 `@examples/getting-started/README.md` around lines 3 - 4, Update the
getting-started README instructions to avoid implying the checked-in example is
identical to the output of `prodocs tutorial`. Align the checked-in example with
the generated tutorial from `src/tutorial.js`, or revise the documented entry
points so both paths provide equivalent tutorial artifacts without suggesting
they are interchangeable when their contents differ.

Comment on lines +5 to +8
test("delivery retries stop after three attempts", () => {
assert.equal(retryDelivery(1), "retry");
assert.equal(retryDelivery(3), "failed");
});

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

Test the last retry attempt.

The test does not check retryDelivery(2). An implementation that retries only attempt 1 would pass the current assertions. Add the attempt-2 assertion.

Proposed test update
 test("delivery retries stop after three attempts", () => {
   assert.equal(retryDelivery(1), "retry");
+  assert.equal(retryDelivery(2), "retry");
   assert.equal(retryDelivery(3), "failed");
 });
📝 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
test("delivery retries stop after three attempts", () => {
assert.equal(retryDelivery(1), "retry");
assert.equal(retryDelivery(3), "failed");
});
test("delivery retries stop after three attempts", () => {
assert.equal(retryDelivery(1), "retry");
assert.equal(retryDelivery(2), "retry");
assert.equal(retryDelivery(3), "failed");
});
🤖 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 `@examples/getting-started/test/delivery.test.js` around lines 5 - 8, The test
for retryDelivery should cover the intermediate second attempt. In the existing
“delivery retries stop after three attempts” test, add an assertion that
retryDelivery(2) returns “retry”, while preserving the current assertions for
attempts 1 and 3.

Comment thread src/benchmark.js
Comment on lines +11 to +39
export async function benchmarkProject(root, config, manifest, suite) {
const cold = await timed(() => scanProject(root, config, { indexMode: "write" }));
const warm = await timed(() => scanProject(root, config, { indexMode: "write" }));
const evaluation = evaluateContextSuite(warm.value, manifest, suite, {
maxFiles: config.limits.maxContextFiles,
maxTokens: config.limits.maxContextTokens
});
const misses = warm.value.runtime.index.cacheMisses;
const hits = warm.value.runtime.index.cacheHits;
return {
schemaVersion: 1,
kind: "prodocs.product-benchmark",
privacy: "local-only; no telemetry or repository content is transmitted",
repository: {
files: warm.value.stats.files,
symbols: warm.value.stats.symbols,
relationships: warm.value.stats.edges,
knowledge: warm.value.stats.knowledge.total
},
indexing: {
coldMilliseconds: cold.milliseconds,
warmMilliseconds: warm.milliseconds,
warmCacheHits: hits,
warmCacheMisses: misses,
warmCacheHitRate: hits + misses === 0 ? 1 : hits / (hits + misses)
},
context: evaluation
};
}

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 | 🏗️ Heavy lift

Establish an empty index before the cold scan.

scanProject reads existing index entries. Both calls use indexMode: "write", which does not clear those entries. A later benchmark run can therefore report cache-assisted work as coldMilliseconds.

Use an isolated or cleared index for the first scan. Reuse that index only for the warm scan. Add a regression test that runs the benchmark twice.

🤖 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 `@src/benchmark.js` around lines 11 - 39, Update benchmarkProject so the cold
scan starts from an isolated or explicitly cleared index, then reuse that same
index for the warm scan to measure cache reuse accurately. Preserve the existing
timing and evaluation output, and add a regression test that invokes
benchmarkProject twice and verifies the second run does not contaminate the
first run’s cold measurement.

Comment thread src/tutorial.js
Comment on lines +80 to +86
const written = [];
for (const [file, contents] of Object.entries(files)) {
const target = path.join(destination, file);
await fs.mkdir(path.dirname(target), { recursive: true });
await fs.writeFile(target, contents, { flag: "wx", encoding: "utf8" });
written.push(path.posix.join(output, file));
}

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
set -euo pipefail

echo "== locate src/tutorial.js =="
fd -a 'tutorial\.js$' .

echo "== relevant file outline =="
ast-grep outline src/tutorial.js || true

echo "== relevant src/tutorial.js lines =="
cat -n src/tutorial.js | sed -n '1,160p'

echo "== searches for writer/tutor/retry patterns =="
rg -n "mkdir|writeFile|write|tutorial|remove|rm\\(|destination|files" src package.json README.md --glob '!**/node_modules/**' || true

echo "== dependency/runtime context =="
cat package.json

Repository: boyeesu/ProDocs

Length of output: 15252


🏁 Script executed:

#!/bin/bash
set -euo pipefail

npm pkg get node >/tmp/pkg-node.json
node - <<'JS'
const fs = require('node:fs/promises');
const path = require('node:path');
const { fileURLToPath } = require('node:url');
const dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.join(dirname, 'tmp-tutorial-proof-' + process.pid);

(async () => {
  await fs.rm(root, { recursive: true, force: true }).catch(() => {});
  await fs.mkdir(root, { recursive: true });
  try {
    await fs.mkdir(path.join(root, 'parent/child'), { recursive: true });
    await fs.writeFile(path.join(root, 'parent/child/file.txt'), 'x', { flag: 'wx', encoding: 'utf8' });
    await fs.writeFile(path.join(root, 'top.txt'), 'y', { flag: 'wx', encoding: 'utf8' });
    // Second top-level write should fail because top.txt exists.
    await fs.writeFile(path.join(root, 'top.txt'), 'z', { flag: 'wx', encoding: 'utf8' });
    console.log('no second-write failure');
  } catch (error) {
    console.log('mode', error.mode);
    console.log('code', error.code);
    console.log('errno', error.errno);
    console.log('message', error.message);
  }
  const entries = await fs.readdir(root, { recursive: true, withFileTypes: true }).catch(err => ({ message: err.message }));
  console.log(JSON.stringify((Array.isArray(entries) ? entries : []).map(e => e.name), null, 2));
  await fs.rm(root, { recursive: true, force: true }).catch(() => {});
})().then(() => process.exit(0));
JS

node - <<'JS'
function walkError(err, seen = new Set()) {
  const key = err.code + ':' + err.message;
  if (seen.has(key)) return `[cycle:${key}]`;
  seen.add(key);
  const next = err.cause == null ? '(none)' : walkError(err.cause, seen);
  return `${err.code}: ${err.message} cause=${next}`;
}

const fsw = require('node:fs/promises');
(async () => {
  await fsw.rm('/tmp/tut-prod-' + process.pid, { recursive: true, force: true }).catch(() => {});
  await fsw.mkdir('/tmp/tut-prod-' + process.pid, { recursive: true });
  await fsw.mkdir('/tmp/tut-prod-' + process.pid + '/parent/child', { recursive: true });
  await fsw.writeFile('/tmp/tut-prod-' + process.pid + '/parent/child/file.txt', 'x', { flag: 'wx', encoding: 'utf8' });
  try {
    await fsw.writeFile('/tmp/tut-prod-' + process.pid + '/top.txt', 'y', { flag: 'wx', encoding: 'utf8' });
    await fsw.writeFile('/tmp/tut-prod-' + process.pid + '/top.txt', 'z', { flag: 'wx', encoding: 'utf8' });
  } catch (err) {
    console.log(walkError(err));
  }
  await fsw.rm('/tmp/tut-prod-' + process.pid, { recursive: true, force: true }).catch(() => {});
})();
JS

Repository: boyeesu/ProDocs

Length of output: 914


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import subprocess, json, tempfile, os, re

for ver in ["^20.11.0", "^22.10.0"]:
    with tempfile.TemporaryDirectory() as tmp:
        n_dir = Path(tmp) / "node"
        if not n_dir.exists():
            subprocess.run(["npx", "--yes", f"node@{ver}", "scripts", "-e", 'console.log(process.version)'], cwd=tmp, check=True, capture_output=True)
        result = subprocess.run(
            [str(n_dir / "bin" / "node"), "--input-type=module", "-"],
            cwd=tmp,
            input=f'''
import fs from "node:fs/promises";
import path from "node:path";
import {{ fileURLToPath }} from "node:url";
import {{ dirname }} from "node:path";
const root = path.join(dirname(fileURLToPath(import.meta.url)), "tmp-tutorial-proof");

(async () => {
  await fs.rm(root, {{ recursive: true, force: true }}).catch(() => {{}});
  await fs.mkdir(root, {{ recursive: true }});
  try {{
    await fs.writeFile(path.join(root, "top.txt"), "y", {{ flag: "wx", encoding: "utf8" }});
    await fs.writeFile(path.join(root, "top.txt"), "z", {{ flag: "wx", encoding: "utf8" }});
    console.log("no second-write failure");
  }} catch (error) {{
    let cause = "";
    for (let e = error.cause; e; e = e.cause) cause += "<" + e.message;
    console.log(JSON.stringify({{
      version: process.version,
      mode: error.mode,
      code: error.code,
      errno: error.errno,
      message: error.message,
      cause: cause
    }}));
  }}
  const entries = await fs.readdir(root, {{ recursive: true, withFileTypes: true }}).catch(err => [])
    .then(e => e.map(x => (x instanceof Object ? x.name : undefined)).filter(Boolean));
  console.log(JSON.stringify({{ remainingFiles: entries }}, null, 2));
  await fs.rm(root, {{ recursive: true, force: true }}).catch(() => {{}});
})();
''',
            capture_output=True,
            text=True,
        )
        print(f"NODE_VERSION={ver}")
        print("stdout:", result.stdout.strip())
        if result.stderr.strip():
            print("stderr:", result.stderr.strip())
PY

Repository: boyeesu/ProDocs

Length of output: 389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
const tmp = path.join(dirname(fileURLToPath(import.meta.url)), "tmp-prodocs-write-proof-" + process.pid);

(async () => {
  await fs.rm(tmp, { recursive: true, force: true }).catch(() => {});
  await fs.mkdir(tmp, { recursive: true });
  await fs.mkdir(path.join(tmp, "parent/child"), { recursive: true });
  await fs.writeFile(path.join(tmp, "parent/child/file.txt"), "x", { flag: "wx", encoding: "utf8" });
  try {
    await fs.writeFile(path.join(tmp, "top.txt"), "y", { flag: "wx", encoding: "utf8" });
    await fs.writeFile(path.join(tmp, "top.txt"), "z", { flag: "wx", encoding: "utf8" });
    console.log("no second-write failure");
  } catch (error) {
    let cause = "";
    for (let e = error.cause; e; e = e.cause) cause += "<" + e.message;
    console.log(JSON.stringify({
      version: process.version,
      mode: error.mode,
      code: error.code,
      errno: error.errno,
      message: error.message,
      cause: cause || "(none)"
    }, null, 2));
  }
  const entries = await fs.readdir(tmp, { recursive: true, withFileTypes: true })
    .catch(() => [])
    .then((e) => e.map((x) => x.name).filter(Boolean));
  console.log("remainingFiles:", JSON.stringify(entries));
  await fs.rm(tmp, { recursive: true, force: true }).catch(() => {});
})();
JS

Repository: boyeesu/ProDocs

Length of output: 399


Remove partial tutorial files after a write failure.

If fs.mkdir() or fs.writeFile() fails, the destination may keep newly created parent directories and files. This leaves a non-empty destination before returning the error; remove destination with fs.rm() before rethrowing.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 83-83: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFile(target, contents, { flag: "wx", encoding: "utf8" })
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🤖 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 `@src/tutorial.js` around lines 80 - 86, Wrap the file-creation loop in the
tutorial-writing flow with failure cleanup: if fs.mkdir() or fs.writeFile()
throws, call fs.rm() on destination before rethrowing the original error.
Preserve the written tracking and successful return behavior while ensuring
partial files and directories are removed.

Comment thread test/adoption.test.js
Comment on lines +90 to +99
test("tutorial refuses a symlinked output parent", async (t) => {
const root = await temporaryProject(t);
const outside = await temporaryProject(t);
await fs.symlink(outside, path.join(root, "linked"));

await assert.rejects(
createTutorial(root, "linked/tutorial"),
/resolves outside the project root/
);
});

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:

#!/usr/bin/env bash
set -euo pipefail

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
mkdir "$tmp/outside"

node --input-type=module - "$tmp" <<'NODE'
import fs from "node:fs/promises";
import path from "node:path";

const root = process.argv[2];
const target = path.join(root, "outside");
const link = path.join(root, "linked");
await fs.symlink(
  target,
  link,
  process.platform === "win32" ? "junction" : "dir"
);
console.log((await fs.lstat(link)).isSymbolicLink() || process.platform === "win32");
NODE

Repository: boyeesu/ProDocs

Length of output: 158


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== locate file =="
fd -a 'adoption\.test\.js$' . | sed 's#^\./##'

echo "== context around lines =="
file="$(fd 'adoption\.test\.js$' . | head -n 1)"
if [ -n "${file:-}" ]; then
  sed -n '70,115p' "$file" | cat -n
  echo "== related resolve/symlink tests =="
  rg -n 'symlink|junction|resolves outside the project root|createTutorial|adoption' "$file"
fi

echo "== package context =="
if [ -f package.json ]; then
  node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
console.log(JSON.stringify({scripts:p.scripts, devDependencies:p.devDependencies, dependencies:p.dependencies}, null, 2));
JS
fi

echo "== Node path fs module availability =="
node -e "console.log({node:process.version, fsAvailable:!!require.resolve('node:fs/promises')})"

Repository: boyeesu/ProDocs

Length of output: 3300


🌐 Web query:

Node.js fs.symlink platform Windows junction EPERM privileges directory type option documentation

💡 Result:

In Node.js, the fs.symlink and fs.symlinkSync methods use the type argument exclusively on Windows to define the nature of the link: 'dir', 'file', or 'junction' [1][2]. The 'junction' type is a specific Windows NTFS feature that allows creating directory links without requiring elevated (administrator) privileges, unlike standard directory symbolic links [3][4]. Key considerations for using 'junction' include: 1. Privileges: Using 'junction' can often bypass the EPERM (operation not permitted) error that occurs when creating standard symbolic links on Windows without administrator rights [3][5]. 2. Directory Limitation: NTFS junctions can only point to directories, not individual files [1][5]. Attempting to use 'junction' on a file target will fail [5]. 3. Path Requirement: Windows junction points require the target path to be absolute [1][6]. When you specify 'junction' in Node.js, the target path is automatically normalized to an absolute path [1][7]. 4. Autodetection: If the type argument is null or omitted, Node.js attempts to autodetect the target type as 'dir' or 'file' [1][8]. If you encounter EPERM errors when creating symbolic links on Windows, it is frequently due to missing administrator privileges or the system not being in Developer Mode [3][9][4]. Switching to the 'junction' type is a common workaround for directory linking, provided the target is a directory [3][5][10].

Citations:


Use a platform-specific link type for linked.

On Windows, await fs.symlink(outside, path.join(root, "linked")) can fail with EPERM when the test process lacks symbolic-link privileges, causing the test to fail before the resolves outside the project root assertion runs. Use a junction on Windows and a directory symlink otherwise.

Proposed fix
-  await fs.symlink(outside, path.join(root, "linked"));
+  await fs.symlink(
+    outside,
+    path.join(root, "linked"),
+    process.platform === "win32" ? "junction" : "dir"
+  );
📝 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
test("tutorial refuses a symlinked output parent", async (t) => {
const root = await temporaryProject(t);
const outside = await temporaryProject(t);
await fs.symlink(outside, path.join(root, "linked"));
await assert.rejects(
createTutorial(root, "linked/tutorial"),
/resolves outside the project root/
);
});
test("tutorial refuses a symlinked output parent", async (t) => {
const root = await temporaryProject(t);
const outside = await temporaryProject(t);
await fs.symlink(
outside,
path.join(root, "linked"),
process.platform === "win32" ? "junction" : "dir"
);
await assert.rejects(
createTutorial(root, "linked/tutorial"),
/resolves outside the project root/
);
});
🤖 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 `@test/adoption.test.js` around lines 90 - 99, Update the symlink setup in the
“tutorial refuses a symlinked output parent” test to use a junction on Windows
and a directory symbolic link on other platforms, while preserving the existing
outside-root rejection assertion.

Comment thread test/roadmap.test.js
Comment on lines +491 to +496
name: "empty request",
paths: ["src/missing.js"],
expectedPaths: [],
unwantedPaths: [],
maxTokens: 1000
},

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

Assert that the missing source returns no files.

precision === 1 does not prove that src/missing.js produced an empty packet. With empty expectedPaths and unwantedPaths, unrelated returned files also produce precision 1 in src/evaluate.js. Assert that the case returned zero files.

Proposed assertion
   assert.equal(report.cases[1].precision, 1);
+  assert.equal(report.cases[1].returned, 0);

Also applies to: 508-512

🤖 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 `@test/roadmap.test.js` around lines 491 - 496, Update the “empty request” test
case in the roadmap tests to explicitly assert that evaluating the missing
source returns zero files, rather than relying only on precision === 1. Add the
assertion against the returned file list or packet using the existing result
structure, and apply the same correction to the related case around the second
referenced range.

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