plans
Plan — Outline Shaping and Adherence Modes
Hand-author an outline into a market map today and the plugin appends the draft below it instead of following it.
Plan — Outline Shaping and Adherence Modes
Why care
The market-map-profile template produces a very good analyst draft, and the reason
it does is the heading skeleton below its cft fence — that skeleton is an
outline, and Perplexity follows it closely. But the skeleton is shared by every file
matching applies-to-paths: ["market-maps/**"]. There is no way to say "for this
map, I want these seven sub-segments in this order, and I want you to treat that as
binding" without editing the template that every other map also uses.
Worse, the obvious move fails silently. applyTemplate decides fill-vs-append by
whether the target body is empty:
const mode = existingBody.trim().length === 0 ? 'fill' : 'append'; So a hand-authored outline in the target file makes the body non-empty, the run switches to append mode, and the draft gets written underneath the outline instead of following it. The outline is stranded as dead prose above a draft that ignored it. That is the bug this plan fixes first.
Second, even with an outline in hand there's no way to say how binding it is. The three modes are not a nicety — they're three genuinely different research tasks:
Hard follow — the outline is a contract. Useful when the map has to line up with a deck, a prior map, or a partner's expectations.
Include but don't limit — every named section must appear, and the model may add sections where the research warrants. The default for real analyst work.
Loose suggestions — shape and emphasis only. Useful for a first pass on a market you don't know well enough to outline confidently.
Findings
Established by reading the current source on 2026-09-10.
The fill-vs-append bug is real and silent
applyTemplate sets mode from existingBody.trim().length === 0. A hand-authored
outline in the target file makes the body non-empty, so the run takes the append
branch, initialContent becomes frontmatter + existingBody + blank lines, and the
generated draft is concatenated below the outline.
Nothing errors. The user gets a complete, well-cited draft that ignored the outline they wrote, sitting under the outline they wrote. The most likely reading of that output is "the adherence setting didn't work" — which is why this bug needs fixing before the adherence modes ship, or the modes will be blamed for it.
Prior art is largely superseded, but not by this plan alone
[[Using-Files-as-Prompt-Outlines]] (2026-05-02) predates the directory-template system. Of its proposals: outline-as-vault-file, config frontmatter, and a body of level-2-or-deeper headings are all now provided by templates themselves. What has never been built is the per-target outline — the thing that shapes one file's run without editing the shared template.
Its one durable technical claim is worth carrying forward verbatim: no level-1 headings in an outline body, because they collide with the model's own nesting of the response. The three adherence partials should restate this rather than assume it.
Note the file's status: has not been touched — per the drift policy, promoting it to
Superseded is a decision for the user, not a side effect of writing this plan.
Adherence has a natural home in machinery that already exists
The partials system shipped: expandIncludes() with depth and cycle guards,
BUNDLED_PREAMBLES with vault-first / bundled-fallback resolution, and a seeder that
writes defaults into zz-cf-lib/ without clobbering user edits. Three adherence
partials drop into that with no new prompt-assembly code.
This matters beyond convenience: it means the operator can edit the wording of the adherence contract itself without a plugin release. If "hard follow" isn't strict enough in practice, that is a one-line vault edit, not an issue filed against the plugin.
Relationship to prior art
[[Using-Files-as-Prompt-Outlines]] (2026-05-02) proposed outlines as standalone vault files with their own frontmatter and provider declarations, plus two new commands. Most of that ambition was absorbed by the directory-template system that shipped after it — templates already are vault files with config fences and heading skeletons. What survives from the old spec and is still wanted is the narrow part: a per-target outline, resolvable from a file, that shapes one run. This plan implements that and lets the older spec be marked superseded.
Design
Outline resolution — three tiers, first hit wins
A
cf-outlinefenced block in the target file body. Inline, visible, edited in place next to the work.A
cf_outline:frontmatter key naming a vault path. For an outline reused across several maps, or one long enough to be its own document.The template's user skeleton — today's behavior, unchanged.
Both new tiers matter and they're about twenty lines apart in implementation, so build both rather than picking.
The fill-vs-append fix
The empty-body check must exclude the cf-outline fence. Concretely: strip the fence
from existingBody before the .trim().length === 0 test, so a file containing
only an outline is still fill mode.
Then decide what happens to the fence after a successful run. Three options, and this needs a call before implementation:
Preserve it above the draft — provenance stays visible, file is noisier
Move it below a
***divider — mirrors the template's own scratch conventionStrip it, having stamped
cf_outline_used:into frontmatter — cleanest file, provenance lives in metadata
Lean toward preserving, because the whole revision arc ([[2026-09-10_Section-Refresh-and-Source-Curation]]) wants the outline still present when a section is refreshed later.
Adherence modes as partials, not code
The three modes ship as three bundled partials under src/docs/partials/:
outline-adherence-strict.mdoutline-adherence-inclusive.mdoutline-adherence-loose.md
They are seeded into zz-cf-lib/partials/ like every other partial, which means the
user can edit the wording of the adherence contract itself without a plugin
release. That is the whole reason to do it this way rather than as string constants —
it matches the doctrine already established in
[[Partials-And-Preambles-For-Perplexed-Templates]], where the entire point was that
shared prompt rules must be vault-visible.
The selected partial splices into the user prompt immediately before the outline block, so the instruction and the thing it governs are adjacent.
Selecting a mode
Precedence, most specific wins:
Run-modal dropdown (per-run override)
cf_outline_adherence:in the target file's frontmatteroutline-adherence:in the template's cft fencePlugin default setting (ship as
inclusive)
UI labels use the operator's vocabulary, not the slugs: "Hard follow — the outline is a contract", "Include but don't limit — every section, plus what the research warrants", "Loose suggestions — shape and emphasis only".
Steps
Fix the empty-body check so a
cf-outlinefence doesn't force append mode. This is the load-bearing bug; it is worth landing on its own.Outline resolution —
resolveOutline(app, target, template)returning{ text, source: 'fence' | 'file' | 'skeleton' }. Thesourcefield feeds the frontmatter stamp and the run Notice.Three bundled partials +
BUNDLED_PARTIALSregistration + seeder coverage.Mode selection chain and
outline-adherence:cft key parsing.Run-modal dropdown, defaulted from the resolved chain and labeled in operator vocabulary.
DirectoryTemplateRunModalalready carries template and model selectors; this is a thirdSetting.Prompt assembly in
applyTemplate— splice adherence partial, then outline, in place of the skeleton when an outline resolved.Frontmatter stamps —
cf_outline_used:,cf_outline_adherence:so a finished map records how it was shaped.Docs —
docs/directory-templates.mdgets an Outlines section; mark [[Using-Files-as-Prompt-Outlines]] superseded by this plan.
Verification
A file containing only a
cf-outlinefence runs infillmode, notappend.Strict mode on a seven-section outline returns exactly seven H1s, in order.
Inclusive mode on the same outline returns at least those seven and marks additions.
Loose mode departs from the outline without being penalized as a failure.
A template with no outline anywhere behaves byte-identically to today.
Open items
Post-run disposition of the
cf-outlinefence — preserve / move / strip. Leaning preserve; decide before step 6.Should an outline be able to carry per-section instructions (bullets under each heading, like the template skeleton does), or is it headings-only? Headings-only is simpler; per-section bullets make the outline a full local template override and start to overlap with the section-refresh plan.
Whether strict mode should post-validate the returned headings against the outline and warn on mismatch, or trust the prompt. Post-validation is cheap and turns a soft failure into a visible one.