Skip to content

fix(write-protocol): thread pagesDirectory through the write, index, search, and chain paths - #160

Merged
drewstone merged 1 commit into
mainfrom
fix/pages-directory-write-path
Aug 21, 2026
Merged

fix(write-protocol): thread pagesDirectory through the write, index, search, and chain paths#160
drewstone merged 1 commit into
mainfrom
fix/pages-directory-write-path

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Why

isSafeKnowledgePath('kb/pages/q36/x.md') returned false while loadKnowledgePages(root, { pagesDirectory }) already read such a store. The read side was parameterized; the write protocol, the file-transaction allowlist, the indexer, the search provider, the run-scoped chain, and the CLI all hardcoded knowledge/. A lab whose store lives at kb/pages/<line>/ could not use the safe-write protocol, index, search, or chain-read it.

What

One option, one normalizer, default unchanged:

  • src/pages-directory.ts (new, leaf module): KnowledgePagesOptions { pagesDirectory? }, DEFAULT_PAGES_DIRECTORY, normalizePagesDirectory. The normalizer refuses .., . and empty segments, absolute paths, drive letters, control characters, and the package-owned .agent-knowledge and raw trees, because the value is a write allowlist prefix as well as a read location. LoadKnowledgePagesOptions is renamed to KnowledgePagesOptions (it drives writes now).
  • applyKnowledgeWriteBlocks(root, text, { pagesDirectory }) / applyKnowledgeWriteBlocksFile parse with [${dir}/] and commit with the same pagesDirectory.
  • file-transaction.ts: assertKnowledgeMutationPath(path, pagesDirectory?) derives the allowlist from the validated directory (<dir>/, raw/, exact .agent-knowledge/sources.json); prepareKnowledgeFileTransaction / commitKnowledgeFileMutations take pagesDirectory. The transaction journal records the canonical pagesDirectory it was prepared under (absent for the default) so inspect/recover/replay enforce the same allowlist without a caller-supplied value. The default journal is byte-identical; the default allowlist is byte-identical.
  • buildKnowledgeIndex / writeKnowledgeIndex, FileSystemSearchProviderOptions (validated at construction, exposed as provider.pagesDirectory), RunScopedStoresOptions (validated at creation, used by loadChain for every origin) take pagesDirectory.
  • CLI: --pages-dir <dir> on apply-write-blocks, index, search, and every other index-reading command.
  • README, AGENTS.md, CHANGELOG (9.0.0: one export renamed, four added).

Proof (local, macOS, node 24.11.1)

  • pnpm run typecheck (src + contracts): clean
  • pnpm run lint: 226 files, no findings
  • pnpm test (TMPDIR on a non-symlinked path): 681 passed, 16 skipped, 48 failed — all 48 are tests/kb-improvement/* and fail with exact knowledge candidate workflows require Linux directory descriptors, identical on a clean origin/main checkout on this machine; CI (ubuntu) covers them.
  • Focused suites (src/pages-directory.test.ts, tests/core.test.ts, tests/file-transaction.test.ts, tests/filesystem-search-provider.test.ts, src/run-scoped.test.ts, src/store.test.ts): 79 passed, 2 skipped.
  • New tests: normalizer accept/reject table (16 refusals); default apply refuses kb/pages/... while { pagesDirectory } writes it and it lands in the scoped index and search while the default index stays empty; ../escape, .agent-knowledge, raw, . refused on write; journal carries pagesDirectory and recovery replays against it; default journal has no pagesDirectory key; forged journal pagesDirectory of ., .., ./kb/, and kb + package.json entry all refused; provider and run-scoped chain read kb/pages.
  • pnpm run build, pnpm run api:surface (+DEFAULT_PAGES_DIRECTORY, +KnowledgePagesOptions, +normalizePagesDirectory, -LoadKnowledgePagesOptions), pnpm run check:version-bump (major required, paid 8.0.10 -> 9.0.0), pnpm run verify:package: clean.
  • Built CLI smoke: apply-write-blocks without the flag warns unsafe path and exits 2; with --pages-dir kb/pages/q36 writes, index --pages-dir indexes 1 page, search --pages-dir ranks it, --pages-dir ../x fails loud.

Closes #159

tangletools
tangletools previously approved these changes Aug 21, 2026

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

✅ Auto-approved drewstone PR — 93d223fc

This PR was opened by the trusted drewstone account.

This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.

…search, and chain paths

The reader accepted { pagesDirectory } while the write protocol and the file
transaction allowlist hardcoded knowledge/. One KnowledgePagesOptions now
drives applyKnowledgeWriteBlocks, buildKnowledgeIndex, writeKnowledgeIndex,
FileSystemSearchProvider, createRunScopedStores, and the CLI (--pages-dir).
normalizePagesDirectory refuses .., absolute paths, drive letters, control
characters, and the package-owned .agent-knowledge and raw trees. A file
transaction journals the pages directory it was prepared under so recovery
enforces the same allowlist. The default journal and allowlist are unchanged.

Closes #159

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

✅ Auto-approved drewstone PR — 7c688ea1

This PR was opened by the trusted drewstone account.

This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.

@drewstone
drewstone merged commit 8a54446 into main Aug 21, 2026
2 checks passed
@drewstone
drewstone deleted the fix/pages-directory-write-path branch August 21, 2026 00:30
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.

Write protocol hardcodes knowledge/ while the read side is parameterized

2 participants