Skip to content

refactor(mcp): phase 5.5 — remote reads complete (validateProject + countEntries + checkReferences)#32

Merged
ABB65 merged 2 commits into
next-mcpfrom
refactor/phase-5-5-remote-reads-complete
Apr 17, 2026
Merged

refactor(mcp): phase 5.5 — remote reads complete (validateProject + countEntries + checkReferences)#32
ABB65 merged 2 commits into
next-mcpfrom
refactor/phase-5-5-remote-reads-complete

Conversation

@ABB65

@ABB65 ABB65 commented Apr 17, 2026

Copy link
Copy Markdown
Member

Summary

Phase 5.5 closes the last gap in the remote read path. Every tool reachable over HTTP + a remote provider (GitHubProvider today, GitLab/Bitbucket later) now gets the same read-side behaviour as stdio + LocalProvider callers — counts, reference checks, and validation all run over the shared RepoReader surface.

Two commits, ship together:

  • 5.5a (7abcf90) — countEntries + checkReferences reader overloads; buildContextChange populates real stats.entries via the reader; contentrain_model_delete runs the REFERENCED_MODEL pre-check against any provider.
  • 5.5b (8684f2f) — validateProject reader overload. Every per-kind validator (collection/singleton/dictionary/document) reads via RepoReader; fix-mode writes stay gated on projectRoot. Post-save validation in contentrain_content_save and read-only contentrain_validate now run against remote providers too; fix mode still capability-gates on localWorktree.

Net effect: Studio (HTTP + GitHubProvider) now gets the same validation + reference-integrity + entry-count envelope that stdio callers have had since Phase 0, without any duplicated code paths.

Test plan

  • pnpm --filter @contentrain/mcp typecheck → 0 errors
  • oxlint packages/mcp/src packages/mcp/tests → 0 warnings
  • fast tests (tests/core tests/conformance tests/serialization-parity tests/git tests/providers tests/server tests/util) → 377/377 green, 2 skipped
  • tool tests (tests/tools/content.test.ts tests/tools/model.test.ts tests/tools/workflow.test.ts) → 40/40 green (~800s)
  • tests/server/http.test.ts → 7/7 green (includes the remote content_save E2E seeded in 5.4)

Scope notes

Phase 5.5 completes the remote read path. Intentionally not in this PR (handled later):

  • bulk, normalize, setup, submit, merge — still local-only. Each has AST walks, git operations, or dir scaffolding that need their own abstraction pass before they can run over a remote provider.
  • GitLab / Bitbucket providers — Phase 8.

🤖 Generated with Claude Code

Contentrain and others added 2 commits April 17, 2026 13:21
…rloads

Enriches the remote write path that landed in phase 5.4 so GitHubProvider-
backed tool calls match LocalProvider in two visible ways:

1. Remote context.json now carries real stats.entries. Previously
   buildContextChange emitted entries: null because no reader-based
   countEntries existed; remote writes therefore diverged from local
   writes in a small but meaningful field that Studio surfaces to users.
   countEntries now has the same dual overload as readConfig / readModel /
   listModels — projectRoot string OR RepoReader. Helper functions
   (countDocumentFileStrategy, countDocumentSuffixStrategy,
   countDocumentDirectoryStrategy, countDocumentNoneStrategy,
   countCollectionEntries) were refactored to take a reader and
   content-root-relative paths; the string-path variant wraps a
   LocalReader so stdio behaviour is bit-for-bit unchanged. A new
   `contentDirPath(model)` helper produces relative paths symmetrical
   with `resolveContentDir(projectRoot, model)` but free of the
   projectRoot prefix.

2. Remote model_delete now runs the REFERENCED_MODEL pre-check too.
   checkReferences gets the same dual overload; the handler dropped its
   `provider instanceof LocalProvider` guard around the reference check
   and now runs it unconditionally. Local deletes continue to branch-
   health gate; remote deletes skip that (no local worktree to inspect)
   but still honour referential integrity.

Changes:

core/model-manager.ts
- `contentDirPath(model)` — content-root-relative helper.
- countDocumentFileStrategy / countDocumentSuffixStrategy /
  countDocumentDirectoryStrategy / countDocumentNoneStrategy /
  countCollectionEntries — helpers now take (reader, contentDir, entries)
  and walk via reader.listDirectory + tryReadJsonViaReader.
- countEntries — dual overload (projectRoot | reader). The string path
  wraps LocalReader; the body uses reader exclusively. Every locale-
  strategy branch (file, suffix, directory, none × collection / document
  / singleton / dictionary) now goes through the reader.
- checkReferences — dual overload. Reuses the already-overloaded
  listModels and readModel, so the body is unchanged beyond the
  signature fan-out.

core/context.ts
- buildContextChange now walks listModels + readModel + countEntries
  through the reader to populate stats.entries. Falls back to null when
  any walk fails, matching the behaviour of the legacy writeContext.

tools/model.ts
- model_delete's reference check moved outside the LocalProvider-only
  branch; it now runs on any provider via checkReferences(provider,
  modelId). Local deletes still do the branch-health gate first.

Verification:
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 68/68 green.
- pnpm vitest run tools/model.test → 8/8 green (~100s). REFERENCED_MODEL
  guard fires exactly as before.
- pnpm typecheck → all 6 packages clean.
- oxlint on packages/mcp/src → 0 warnings.

What phase 5.5b picks up next (intentionally staged):
- validateProject reader overload. This is a 795-line file that needs
  a careful (rather than mechanical) refactor because fix mode is
  inherently projectRoot-bound and the per-kind helpers all reach into
  the filesystem. The split lets 5.5a ship with tight review scope;
  5.5b lands alongside workflow_validate's remote path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends Phase 5.5a: the read surface (context.json, countEntries,
checkReferences) now runs over any RepoReader. This PR makes
validateProject itself reader-backed so post-save validation and the
standalone contentrain_validate tool work against remote providers —
not just LocalProvider — closing the last gap in the remote read path.

validateProject now has two signatures:

- `validateProject(projectRoot, options?)` — legacy disk-backed flow.
  `options.fix: true` still applies structural repairs (canonical sort,
  orphan meta, missing locale files) directly on disk.
- `validateProject(reader, options?)` — reader-backed flow. `fix` is
  ignored because remote providers cannot write to the local
  filesystem; fix mode is a local-only capability.

Read-side behaviour is identical across both signatures, so Studio
(HTTP + GitHubProvider) and the local CLI see the same issue set for
the same content state.

Changes:
- core/ops/paths.ts
  `contentDirPath(model)` now exported — content-root-relative content
  directory for a model. Single source of truth for reader-backed
  callers and plan-API consumers.
- core/model-manager.ts
  Drops its private `contentDirPath`; delegates to `ops/paths.ts` so
  the two helpers cannot drift.
- core/validator/project.ts
  Every per-kind validator (collection, singleton, dictionary,
  document) now takes `(reader, projectRoot | undefined, ...)`. All
  reads go through the `RepoReader` surface (readFile / listDirectory
  / fileExists); fix-mode writes gate on `projectRoot !== undefined`
  and still call `writeJson` / `writeText` / `writeMeta` directly.
  `buildProjectTargetResolver`, `discoverDocumentSlugs`, and
  `checkOrphanContent` are reader-based too, so cross-dictionary and
  relation-integrity checks work against remote providers.
- tools/content.ts
  Post-save validation drops the local-only guard — remote flows now
  drive `validateProject(provider, ...)` instead of synthesising an
  empty result. Agents orchestrating a GitHubProvider-backed server
  get the same validation envelope as stdio callers.
- tools/workflow.ts
  `contentrain_validate` splits on `input.fix`: fix mode still
  capability-gates on `localWorktree` (uses git transaction +
  worktree), read-only mode runs over any provider. `config` lookup
  moved to the shared `provider` reader so stdio / HTTP both use the
  same path.

Verification:
- pnpm --filter @contentrain/mcp typecheck → 0 errors.
- oxlint packages/mcp/src + tests/ → 0 warnings.
- vitest run tests/core tests/conformance.test.ts
      tests/serialization-parity.test.ts tests/git tests/providers
      tests/server tests/util → 377/377 green (2 skipped).
- vitest run tests/tools/content.test.ts tests/tools/workflow.test.ts
      tests/tools/model.test.ts → 40/40 green (~800s).

Phase 5 scope after this PR:
- 5.1 GitHubProvider ✓
- 5.2 HTTP transport ✓
- 5.3 Provider threading ✓
- 5.4 Remote write path for content + model CRUD ✓
- 5.5a countEntries + checkReferences reader overloads,
        context.json entries stats for remote ✓
- 5.5b validateProject reader overload ✓ (this PR)

What Phase 5.5 intentionally does not cover (handled in later phases):
- bulk / normalize / setup / submit / merge tools — still
  projectRoot-only. AST walks, git operations, and dir scaffolding
  need separate abstraction passes before they can run against
  a remote provider.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ABB65 ABB65 merged commit 906baae into next-mcp Apr 17, 2026
1 check passed
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