Skip to content

Commit 3b975eb

Browse files
committed
feat: build-context factory, resolveEntryTitle and toPlainText
Resolves backlog cross-link conflicts: both branches moved a task file to resolved/ while updating its link to the other on the assumption the other was still open. Both now live in resolved/, so the links are siblings. Also drops two stale open rows that a keep-both merge left alongside their own resolved rows -- caught by lint:tasks, which is exactly the drift that guard exists to catch.
2 parents bc267e1 + c9ff1f8 commit 3b975eb

14 files changed

Lines changed: 699 additions & 8 deletions

.claude/future-tasks/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ silently skips conflict detection. It is rated P1 because its trigger is narrow
166166
| [url-mapping-system.md](url-mapping-system.md) | Flexible URL-to-content mapping: date-based URLs, custom slug transforms, multiple patterns per collection. The heaviest and most speculative of the URL designs — keep shelved unless the marketing site needs pattern-based URLs (a dated blog) that a per-entry override can't express. Treat as an appendix to `isurlpath-field-marker` [MKT] |
167167
| [list-permission-level.md](list-permission-level.md) | New "list" permission level: see content exists without read/edit access. Unstarted; `PermissionLevel` is still `'read' \| 'edit' \| 'review'`. Two path drifts corrected 2026-08-13 (the named `path-permissions.ts` no longer exists; the `EditorEntry` sketch uses field names the real interface doesn't have). Note it would add a **sixth** ACL matcher unless the consolidation lands first [NEITHER] |
168168
| [transform-lambda-bundle-bloat.md](transform-lambda-bundle-bloat.md) | Transform Lambda's `handler.js` bundles unrelated octokit/simple-git/proper-lockfile code via the broad `canopycms/server` barrel (~1 MB dead weight). Non-functional, but **the only blocker is a yes/no decision** — the repo bans new package entrypoints without approval, and the code change itself is a small re-export module. **Re-tagged 2026-08-14** — the transform Lambda ships with whichever site deploys first: the KB [BOTH] |
169-
| [search-document-extraction-primitives.md](search-document-extraction-primitives.md) | New 2026-08-14 — adopter request #17. The requested `extractSearchDocuments` API is the wrong shape (the two adopters' derivations share nothing at that level); what's actually duplicated is the boot block, a markdown→plaintext stripper (`ai/strip-mdx.ts:19`, unexported), and title derivation (`utils/title-field.ts:60`, unexported) — plus the unrelated-but-same-shape `parseTypedFilename` (`content-listing.ts:94`, unreachable) and `listEntries` dropping `updatedAt`. **This epic is exporting the primitives now**; `extractSearchDocuments` itself is not being built [BOTH] |
170169
| [toc-heading-id-contract.md](toc-heading-id-contract.md) | New 2026-08-14 — from the site audits. Both sites independently built `rehype-heading-attrs.ts`; `website`'s `toc.ts` hand-mirrors rehype-slug's dedup counter as a second, unsynced piece of state, and desyncs the first time a heading contains `#`/`####`. Ship one canonical heading-ID pass plus an `extractToc()` that reads it, not a second slugger [BOTH] |
171170
| [resolved-references-url.md](resolved-references-url.md) | New 2026-08-14 — from the site audits. A resolved reference carries no URL, so the KB built `getDocLinkIndex()` — a **second full `listEntries` pass** just to link to referenced entries — and some pages set `resolveReferences: false` entirely to avoid paying for both. Carry a `urlPath` on resolved references (the target entry is already loaded) and delete the workaround [KB] |
172171
| [trailing-slash-router-helpers.md](trailing-slash-router-helpers.md) | New 2026-08-14 — from the site audits. Both sites solved the same static-export trailing-slash problem at very different weight: the KB built a whole subsystem (`paths.ts` + a wrapped router hook + a custom ESLint rule), `website` built one function. Ship the primitive (`withTrailingSlash`), let adopters choose enforcement strictness [BOTH] |
@@ -245,6 +244,7 @@ The tables above list OPEN work only.
245244
| [block-registry-types.md](resolved/block-registry-types.md) | RESOLVED (2026-08-14, branch `feat/block-registry-types-and-recipes`, `integration-202608-b` epic) — adopter request #13, shipped as decided: types, not a component. `BlockValueOf<Blocks, N>` and `BlockComponentRegistry<Blocks, ExtraProps>` added to `entry-schema.ts`, exported off the `canopycms` root entry, with type-level tests proving exhaustiveness in both directions (missing template, unknown extra key). README's new "Block Component Registries" section documents the recipe, including the one contained type assertion the dispatch loop needs. `apps/example1/app/components/PostView.tsx` now uses the registry instead of the if-chain this file originally flagged [BOTH] |
246245
| [field-fragments-docs.md](resolved/field-fragments-docs.md) | RESOLVED (2026-08-14, branch `feat/block-registry-types-and-recipes`, `integration-202608-b` epic) — adopter request #15, pure documentation gap closed. README's new "Reusable Field Fragments" section covers both mechanisms (spreading a `const`-inferred field array, nesting `defineInlineFieldGroup` inside a block template) plus a per-use-override worked example. A small `defineFieldFragment()` identity helper was added beside `defineBlockTemplate` for discoverability [BOTH] |
247246
| [shared-blocks-listentries-caveat.md](resolved/shared-blocks-listentries-caveat.md) | RESOLVED (2026-08-14, branch `feat/block-registry-types-and-recipes`, `integration-202608-b` epic) — adopter request #16, recipe + caveat documented. README's new "Shared / Referenced Blocks" section gives the full recipe (snippet-style entry type + one-field block template holding a `reference`); the `listEntries`-never-resolves-references caveat is called out in that section and again at the top of "Listing Entries". `apps/example1/app/schemas.ts` now wires one real shared-block reference, rendered null-safely in `PostView.tsx`. The underlying capability gap (`listEntries` still doesn't resolve references) remains open — this closed the documentation gap only [BOTH] |
247+
| [search-document-extraction-primitives.md](resolved/search-document-extraction-primitives.md) | RESOLVED (2026-08-14, epic `integration-202608-b`) — adopter request #17. `parseTypedFilename`/`defaultBuildPath`/`updatedAt` landed first (commit `4a8992fe`); this closes the rest: `resolveEntryTitle` exported from `canopycms/server` and the root `canopycms` entry (client-safe), `toPlainText` (new, built on `ai/strip-mdx.ts`'s `stripMdxImports`) exported from `canopycms/ai`, and the boot-block pattern formalized as `createBuildCanopy(config, options)` in `canopycms/server` rather than left as prose. `extractSearchDocuments` itself remains deliberately unbuilt — see `docs/adopter-migration.md` [BOTH] |
248248
| [optional-property-inference.md](resolved/optional-property-inference.md) | RESOLVED (2026-08-14, epic `integration-202608-b`, commit `8da62672`) — adopter request #14, shipped as proposed. `InferContentShape`/`RequiredValue` now infer a field as an optional property (`subheading?: string`) iff its schema entry has an explicit `required: false`; README's two previously-contradicting sections reconciled; covered by `entry-schema.test.ts`'s three-way required/optional/omitted distinction [BOTH] |
249249
| [readbyurlpath-entry-type.md](resolved/readbyurlpath-entry-type.md) | RESOLVED (2026-08-14, epic `integration-202608-b`, commit `4a8992fe`) — shipped as proposed, widened to also return `entryId`. `read()` and `readByUrlPath()` both surface `meta.entryType`/`meta.entryId`, closing the gap that forced adopters into per-content-type Next.js routes or a separate `listEntries` lookup for catch-all routing [BOTH] |
250250
| [leaf-slug-static-params-docs-gap.md](resolved/leaf-slug-static-params-docs-gap.md) | RESOLVED (2026-08-14) — adopter request #12 was already fully shipped before it was asked: `collectStaticParams(..., {shape: 'single'})` (`canopycms-next/src/static.ts:23,64-66`), documented (`README.md:1420-1428`), used in-repo (`apps/example1/app/posts/[slug]/page.tsx:14-15`), and already available on the website's pinned 0.0.41. Zero work — a discoverability gap on the request side, not a package gap [BOTH] |

.claude/future-tasks/markdown-rendering-not-building.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ field docs) explaining:
4747

4848
## Related
4949

50-
- [search-document-extraction-primitives.md](search-document-extraction-primitives.md)
50+
- [search-document-extraction-primitives.md](resolved/search-document-extraction-primitives.md)
5151
— same shape of decision (ship the shared primitive underneath, not an
5252
opinionated top-level API).

.claude/future-tasks/search-document-extraction-primitives.md renamed to .claude/future-tasks/resolved/search-document-extraction-primitives.md

File renamed without changes.

.claude/future-tasks/resolved/shared-blocks-listentries-caveat.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ all (see the general finding in
3636
index, a sitemap, an AI-content export — sees a shared block as either `null`
3737
or a bare reference ID, never the resolved data. This is the same underlying
3838
gap `#17`'s search-document work
39-
([search-document-extraction-primitives.md](../search-document-extraction-primitives.md))
39+
([search-document-extraction-primitives.md](search-document-extraction-primitives.md))
4040
runs into, and the same one
4141
[resolved-references-url.md](../resolved-references-url.md) is designed around.
4242

.claude/future-tasks/script-runner-entrypoint.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ exist.
1717

1818
This is the shared prerequisite underneath several other items filed in this
1919
same re-baseline: the boot-block duplication noted in
20-
[search-document-extraction-primitives.md](search-document-extraction-primitives.md)
20+
[search-document-extraction-primitives.md](resolved/search-document-extraction-primitives.md)
2121
(`createCanopyServices` + `createCanopyContext` + `STATIC_DEPLOY_USER`, byte-
2222
similar in both sites' scripts), and the programmatic content-authoring gap in
2323
[content-authoring-api-id-generator.md](content-authoring-api-id-generator.md)
@@ -47,7 +47,7 @@ loader) that:
4747

4848
- [content-authoring-api-id-generator.md](content-authoring-api-id-generator.md)
4949
— the concrete first consumer of this.
50-
- [search-document-extraction-primitives.md](search-document-extraction-primitives.md)
50+
- [search-document-extraction-primitives.md](resolved/search-document-extraction-primitives.md)
5151
— documents the boot-block pattern this would formalize.
5252
- `cli-sync-migrate-ignore-adopter-content-root.md` — the CLI's existing
5353
config-loading path this should reuse rather than duplicate.

0 commit comments

Comments
 (0)