Skip to content

Build1 publisher3 min readPublished

VanillaPM's charter generator reads only the three documents its dependency graph declares

The Django tool caps each input document at 8,000 characters and the whole grounding context at 40,000. Its system prompt tells the model to name the missing input in one sentence when the data is not there.

The Engineer · Build desk

Illustration accompanying VanillaPM's charter generator reads only the three documents its dependency graph declares

What happened

  • A Django project-management tool, VanillaPM, drafts charter prose only from documents already stored on the project, with a charter's declared inputs being the business case, the benefits management plan and the agreements.
  • Grounding is bounded by two constants in the code: 8,000 characters per input document and 40,000 characters for the whole grounding context, which also holds the structured facts.
  • The third rule restricts output to JSON with a body per section key, using only the keys supplied, with no markdown headings and no preamble.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Adopting this design is a schema decision first, before it is a prompt decision, because a team whose document types do not declare their predecessors has nothing for the generator to go and read.
  • constraint The 8,000-character slice bounds the context and the bill, and it also means the drafter never sees the tail of a long business case.
  • exposure Refusal still depends on the model choosing the exit, and a fabricated sponsor placed under an allowed section key passes the JSON check and reaches a document people act on.
  • capability When an input document is thin, the gap in the records gets stated inside the draft as a named missing prerequisite.

Pressing the generate button starts a short call sequence. Read the document type's declared prerequisites from the dependency graph, fetch the latest instance of each, walk its stored rich-text tree into plain text, cut that text at 8,000 characters, and append it to the structured facts the deterministic autopopulate already produces [5][6][3][7].

The deterministic path was there first. Autopopulate copies the sponsor from the project record and the dates from the schedule, and it cannot produce a scope statement, an objectives section or a rationale, because those are prose synthesised from the pre-project inputs [14].

Two constants bound the input side. DOC_CHAR_CAP is 8,000 and TOTAL_CONTEXT_CAP is 40,000 [3][4], so five documents at the full per-document cap reach the ceiling [15]. A charter declares three inputs: the business case, the benefits management plan and the agreements [2]. At the cap those take 24,000 characters and leave 16,000 for the structured facts and the header [16]. Both limits count characters, not tokens [17].

Refusal is engineered in the prompt. Rule 1 tells the model the supplied project data and input documents are its only source of truth [8]. Rule 2 tells it that when a section's data is missing, it should write one short professional sentence stating what input is still needed, and not fabricate [9]. "Rule 2 is the difference between a demo and a tool," wrote the author, who publishes as vanillapm [11]. The worked example of that exit in the post is the placeholder "needs input: the benefits plan doesn't state a target ROI" [13].

Whether the design transfers depends on your codebase. Your schema has to declare, per document type, which documents precede it, so grounding is a graph lookup [2]. And the authored prose has to exist, because document_text() pulls what a human actually wrote [6]. A project whose business case is a two-line stub gets back a list of needed inputs. Someone then has to go and write the business case.

The weak point is provenance. Rule 3 fixes the output shape and restricts the model to the section keys it was given [10]. A fixed key set makes parsing deterministic. A key check confirms the model wrote "scope" and not "skope". It does not confirm that the sentence under "scope" came from the business case, and an invented sponsor sitting under an allowed key parses cleanly. The post offers code and prompts as its evidence, and does not report how often the model takes the refusal exit [18].

Truncation is the piece I would change first. The slice is text[:8_000], so the excerpt ends at whatever character lands there [3][5], and a business case that puts its success criteria in the final section loses them with no signal to the drafter. The cap exists so one 200-page attachment cannot crowd out the other inputs or blow the context window and the bill [19]. Section-aware excerpting would serve that goal better than a flat slice.

What to watch

  • Whether the author publishes a measured count of refusal placeholders against fabrications on real projects.
  • Whether the flat 8,000-character slice is replaced by section-aware excerpting of long input documents.
  • Whether output validation ever goes past the section key and compares a drafted sentence against the supplied excerpts.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories