Skip to content

[bug] The docs-drift tool count reads three files and reports "every doc claim" — the public /about page says ~67 tools while production serves 135 #555

Description

@serge-ivo

The number a prospective user reads is half the real one

https://proagentstore.online/about/ — measured today:

~67 tools across creator operations, public trials, private instance runtime, coding, and error observability

https://mcp.proagentstore.online/health — measured the same minute:

{"ok":true,"service":"proagentstore-mcp","tools":135}

Source: store/about/index.html:179. The constant it disagrees with is workers/mcp/src/tool-count.ts:23 (MCP_TOOL_COUNT = 135).

This is #210's own defect, in the one surface a person reads before they decide whether to use the product — and pnpm docs:drift is green.

Why the guard did not see it

scripts/docs-drift.mjs:307:

const claimFiles = [
    p("platform-docs/mcp.md"),
    p("store/llms-full.txt"),
    p("workers/mcp/README.md"),
].filter(existsSync);

Three files. The failure message and the success line both speak in a wider voice — the CI output reads:

✓ MCP tool count: 135 registered == constant == every doc claim (+ 16 per-file counts)

"every doc claim" is a claim about the repo; the measurement is over three paths. That is the same shape as #536 and #531: a guard whose printed number is trusted because of how it is worded, taken over a subset.

The root of it is one layer up. docFiles() (scripts/docs-drift.mjs:49) opens with "Everything a reader or an agent is expected to trust" and then collects only platform-docs/*.md, store/llms.txt, store/llms-full.txt, README.md, AGENTS.md, SECURITY.md, and per-package README/AGENTS/CLAUDE.md. The store's own HTML — the marketing site, which workers/host builds and serves — is in none of it. Grepped: store/about/index.html is the only HTML page in the tree carrying a tool count, so the fix is small; but nothing was ever going to report it.

A second, independent stale claim in a file the guard DOES read

store/llms.txt:65:

Twelve tools require an exact confirm value, compared with ===. In eleven cases the value is the tool's own name: write_agent_file, batch_write_agent_files, unregister_instance_runtime, cancel_instance_task, cancel_instance, delete_instance_knowledge, delete_instance_memory, delete_instance_file, delete_instance_trigger, delete_instance_connector_grant, clear_instance_messages. The exception is remove_repo

Counted from the code — every requireConfirmation(…) call site outside safety.ts and the tests — there are 13, twelve of which use the tool's own name. The missing one is delete_supervision (workers/mcp/src/instance-tools/composition.ts:97 declares the arg, :115 enforces confirm === "delete_supervision").

platform-docs/mcp.md:135-148 and workers/mcp/README.md:328 both list it correctly, so the drift is isolated to the LLM-facing file — which is precisely the reader that cannot ask. store/llms.txt is inside docFiles(), but the confirm-value list is not one of the eight checks, so nothing compares it to anything.

What to do, cheapest first

  1. One line, fixes today's defect: correct store/about/index.html:179 to the real number, or better, drop the number ("the full tool surface — see the MCP docs") so a marketing page is not a second place the count has to be maintained.
  2. Add the store HTML to claimFiles. store/**/*.html for the N tools regex. This is the smallest change that makes the guard's sentence true, and it would have failed on the line above.
  3. Widen docFiles() to include the served HTML, so the removed-command and removed-identity checks cover the marketing site too. Grepped before proposing: cloudflared, --tunnel and the FAGS runtime-plane strings do not currently appear in any store/*.html (the only freeagentstore reference is a sibling-store profile link in store/developers/profile.html:206, which is the ecosystem cross-link, not a runtime-identity claim). So this widening lands green and holds new ground rather than opening a backlog.
  4. Derive the confirm list. requireConfirmation(safety, toolName, confirm, expected, …) gives both halves; a check like the MCP tool-table one — every documented confirm pair equals a registration, and every registration is documented — turns store/llms.txt, platform-docs/mcp.md and workers/mcp/README.md into one derived table. That is the same construction that makes the tool-count check strong: tool-count.ts is asserted by index.test.ts running the REAL registration path, not by a regex over prose.
  5. Make the success line say its denominator. ✓ MCP tool count: 135 == constant == every claim in 4 files costs nothing and stops the next reader over-trusting it. Consider it a general rule for this script — see the systemic issue filed alongside this one.

Alternatives considered and rejected

  • Scan every file in the repo for N tools. Rejected: workers/mcp/CLAUDE.md:85 legitimately says "67 of those 86 tools until [standards][tracking] Split worst large files into testable modules #305" — a historical statement — and the per-file module-layout counts are already checked separately by their own rule. A blanket scan would fire on the prose that explains the rule, which is the failure mode usage-claims.test.ts and control-labels.ts both had to design around.
  • Assert the about page against /health at build time. Rejected: it makes the docs build depend on a deployed service, and the constant already exists locally.

Acceptance criteria

  1. node scripts/docs-drift.mjs FAILS on store/about/index.html as it stands today — demonstrate that before fixing the page, the way [bug] A "Runs on" tile claims "Attached · online" for a node the pin excludes — one line below "Status: Offline", and the invariant test only covers the pinned tile #531 proved its assertion by reverting the fix.
  2. After the fix, the live /about/ number equals MCP_TOOL_COUNT, or states no number at all.
  3. store/llms.txt's confirm list names all 13 tools and the count says 13/12, and a check compares that list to the requireConfirmation call sites rather than to another document.
  4. Each line in docs-drift.mjs names how many files it read.

Regression risk

Widening docFiles() to HTML risks the removed-command check firing on prose inside a <code> block. fencedLines() (scripts/docs-drift.mjs:81) is Markdown-fence-specific and has no HTML equivalent, so step 3 needs an HTML-aware "is this an instruction" rule or it will need its own exclusions. Step 2 alone (the N tools regex over HTML) has no such coupling and can ship first.

Verified vs inferred

Verified: the live /about/ string and the live /health count (both fetched today); store/about/index.html:179; claimFiles at scripts/docs-drift.mjs:307; docFiles() at :49; 13 requireConfirmation call sites; store/llms.txt:65 listing eleven; platform-docs/mcp.md:148 listing delete_supervision; node scripts/docs-drift.mjs green at cfe8ff7.

Inferred: that no other served HTML page carries a checkable numeric claim — grepped for N tools|agents|migrations across store/**/*.html and found only this one, but a claim phrased differently would not have matched.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions