Skip to content

fix(generate): unwedge docs-sync and correct the MCP security model - #90

Merged
Ethan-Arrowood merged 2 commits into
mainfrom
fix/manifest-stale-anchors
Sep 8, 2026
Merged

Ethan-Arrowood merged 2 commits into
mainfrom
fix/manifest-stale-anchors

Conversation

@Ethan-Arrowood

Copy link
Copy Markdown
Member

The auto-sync pipeline has not produced a PR since 2026-08-05. The six open "Auto-sync generation failure" issues (#73, #83, #85, #87, #88, #89) are not six transient failures — they are two permanently unsatisfiable manifest entries plus one broken reporter.

Why every run failed

Cause Rules Since
must_cover: live-class — a term that has never existed in the MCP docs custom-mcp-tools 2026-08-05 (#73)
must_cover: getContext() — absent from every sourced section checking-authentication 2026-09-02 (#85, #87, #88)
section: Module Loading — renamed by documentation#664 custom-resources, programmatic-table-requests 2026-09-04 (#89, and the silent 09-07 run)

live-class was manufacturing content, not just failing

git log -S'live-class' --all -- reference/mcp/ finds no commit that ever added it. And lib/llm.mjs echoes must_cover to the model as "The rule body MUST include each of the following (verbatim for code/identifiers)" — so when this anchor passed, it passed because the model coined the term. The shipped rule carries:

Dispatch is live-class. Calls construct an instance of the class currently in the Resource registry, so an exported subclass (and its access-control overrides) always wins after a reload/deploy.

Nothing in the sourced section mentions a registry, a subclass, a reload, or a deploy. The anchor invented the sentence and then validated it. Replaced with allow*, which is in the source and pins the security fact instead.

getContext() was unsatisfiable a third way

It occurs 7 times in resource-api.md — none inside `getCurrentUser()` or Session and Login from a Resource, and 0 times in the JWT source. The model could only satisfy it by inventing, which system-prompt.md forbids, so the run failed validation every time. Fixed by sourcing the `getContext(): Context` section, which carries the instance-vs-static contract the rule needs anyway.

Module Loading → Module Formats

documentation#664 renamed the heading (it described ESM vs CJS, not module loading) and split real module-loading docs into a new page. The section is sourced for exactly one thing — keep harper external when bundling for SSR — and vite.config moved with the rename, so this is like-for-like.

Live-wrong security guidance, now corrected

Regenerating custom-mcp-tools fixes something worse than a wedged pipeline. The rule on main says:

The method runs inside the normal transactional() envelope, so data access it performs still hits per-record allow* predicates

The docs say the opposite:

The MCP transport audits the tools/call, but invokes the custom instance method directly. It does not open a Resource transaction or run an allow* gate automatically.

An agent trusting the old sentence ships ungated table reads and writes on a tool reachable by anonymous, unauthenticated MCP sessions. The regenerated rule states the negative list correctly and picks up the checkPermission / fresh-RequestTarget guidance added upstream.

The retention check earned its place immediately

#86's fact-retention gate caught 8 dropped facts across these four regenerations. Two were real and restored by anchoring:

  • HdbError: <attribute> is not indexed and not combined with any other conditions — the literal string an agent matches a runtime failure against. Regeneration had reduced it to "throws an error".
  • npm link harper

Six were notation rather than content and are waived in allow_dropped, each with its reasoning recorded inline: authentication.enableSessions: true (same fact as YAML + prose), {param} (replaced by a concrete /widget/{id}/action/{action} example), databases, and the txn table's Type column (() => Promise, () => void, number — all four members and their behavior survived).

Known limitation, stated plainly: that last group is the check's noise floor. It compares code spans, so a dropped table column reads the same as a dropped fact, and number is too generic to pin anything. Waiving is the intended response and stays visible in review.

Closing the remediation loop

The check tells you to "restore them", and anchoring is how — except it didn't work. inputHash covers resolved sources only, so editing must_cover left it matching, the rule was skipped, the new anchor never reached the model, and validate-generated then failed on it forever. Generation now also regenerates when the body on disk fails its own anchors. Verified: breaking an anchor in a body triggers regeneration with no --force.

Verification

  • npm run validate green.
  • validate-generated.mjs --docs-path <docs@9e6ecf8> green, including source-exists, byte-identical and fact-retention.
  • All four regenerations done against a docs build at 9e6ecf8 (docs main), so the rules are not born stale.

Not in this PR

reference/resources/resource-api.md still says `harperdb-config.yaml` in the sessions section, which documentation@d895f428 was meant to eliminate. The regenerated checking-authentication faithfully reproduces it. That is an upstream docs fix, and worth doing before the next sync picks it up again.

🤖 Generated with Claude Code

The auto-sync pipeline has not produced a PR since 2026-08-05. Six open
"Auto-sync generation failure" issues trace to two permanently
unsatisfiable manifest entries, not to transient failures.

1. `live-class` (harper-mcp/custom-mcp-tools) has never existed in the
   MCP docs — `git log -S` across all of reference/mcp/ finds no commit
   that ever added it. Because lib/llm.mjs echoes must_cover to the
   model as "MUST include ... verbatim", the anchor did not merely fail:
   when it passed, it passed because the model coined the term. The
   shipped rule carried "Dispatch is live-class. Calls construct an
   instance of the class currently in the Resource registry ..." — a
   sentence with no counterpart anywhere in the sourced section. That is
   the anchor manufacturing content, then validating it. Replaced with
   `allow*`, which is in the source and pins the security fact.

2. `Module Loading` (custom-resources, programmatic-table-requests) was
   renamed to `Module Formats` by documentation#664 on 2026-09-04. The
   section is sourced for one thing — keep `harper` external when
   bundling for SSR — and `vite.config` moved with the rename, so the
   new heading is a like-for-like replacement.

3. `getContext()` (checking-authentication) was unsatisfiable a third
   way: the anchor demanded a string that appears in no sourced section.
   `getContext()` occurs seven times in resource-api.md, none of them
   inside `getCurrentUser()` or `Session and Login from a Resource`, and
   zero times in the JWT source. The model could only satisfy it by
   inventing, which the system prompt forbids, so every run failed
   validation. Added the `getContext(): Context` section, which carries
   the instance-vs-static contract the rule needs anyway.

Regenerated the four affected rules against documentation@9e6ecf8. The
MCP regeneration also fixes live-wrong security guidance on main: the
rule claimed data access inside a custom tool "still hits per-record
`allow*` predicates", where the docs now say the transport "invokes the
custom instance method directly ... does not open a Resource
transaction or run an `allow*` gate automatically". An agent trusting
the old sentence shipped ungated table access on an anonymously
reachable tool.

The fact-retention check from #86 earned its place immediately: it
caught eight dropped facts across these four regenerations. Two were
restored by anchoring (the literal `HdbError: <attribute> is not
indexed ...` string an agent matches a runtime failure against, and
`npm link harper`); six were judged notation rather than content and
waived in `allow_dropped`, each with its reasoning recorded inline.

Also closes the remediation loop the check points at. `inputHash`
covers resolved sources only, so editing `must_cover` left it matching
and the rule was skipped — the new anchor never reached the model and
validate-generated then failed on it forever. Generation now also
regenerates when the body on disk does not satisfy its own anchors.
Verified: breaking an anchor in a body triggers regeneration with no
--force.

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the Harper and Harper MCP best practices documentation, rule manifests, and the rule generation script to improve clarity, structure, and accuracy around authentication, custom resources, and programmatic table requests. The review feedback correctly identifies that the documentation incorrectly references the deprecated harperdb-config.yaml instead of the canonical harper-config.yaml for Harper v5, which would lead to silent configuration failures.

Comment thread harper-best-practices/AGENTS.md Outdated
Comment thread harper-best-practices/rules/checking-authentication.md Outdated
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@Ethan-Arrowood
Ethan-Arrowood merged commit e98efec into main Sep 8, 2026
1 of 2 checks passed
@Ethan-Arrowood
Ethan-Arrowood deleted the fix/manifest-stale-anchors branch September 8, 2026 15:54
github-actions Bot pushed a commit that referenced this pull request Sep 8, 2026
## [1.12.10](v1.12.9...v1.12.10) (2026-09-08)

### Bug Fixes

* **generate:** unwedge docs-sync and correct the MCP security model ([#90](#90)) ([e98efec](e98efec)), closes [documentation#664](HarperFast/documentation#664) [#86](#86)
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.12.10 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant