alignr / design-doc · v0.1 · 2026
Structured generation DITA MVP
ALIGNR v0.1  ·  structured content generation  ·  2026

AlignR: Generate new DITA topics from the ones you have
and check them against the schema before a writer sees them

Disclaimer. This is a portfolio design document. Google has not engaged, sponsored or endorsed this work. The sample content is adapted from the Cloud TPU documentation, which Google publishes under the Creative Commons Attribution 4.0 licence1. The adapted versions are modified from the originals. The sample runs, pull requests and costs in this document are modelled, not measured. Stakeholders are job roles, not real people. The two readers in §3 are fictional.

2
schema gates before
a writer sees a topic
3
TPU generation pages
with one shared skeleton
0
training runs needed
to start
Abstract

Each new Cloud TPU generation gets a set of topics with the same structure as the last one and new values. The v5p, v6e and TPU7x pages share one skeleton: an introduction, a system architecture table, configurations and a VM section234. Writers build each new set by copying the last one and editing it by hand. Labels drift and values get typed twice.

AlignR is an agent that drafts the new set from the existing DITA topics. It finds sibling topics of the same type, reads the new values, and asks Gemini to fill a JSON Schema that mirrors the DITA content model. Gemini never writes XML. A plain Python serialiser writes it, with a key reference for every value that has a key. The topic then passes the DITA RELAX NG grammar, house rules in Schematron, a grounding check and Vale before a writer sees it as a pull request.

The original AlignR design aligned a model by fine-tuning on 12,000 to 20,000 preference pairs5. This version keeps its preference pair format and drops the training. Every writer edit to a draft is logged as a pair. A fixed rule (§5.9) decides when the log is large enough, and good enough, to pay for tuning.

structured outputJSON SchemaDITARELAX NGSchematronkeysgroundingGeminiAgent Development Kitpreference pairshuman reviewCloud Run

§1 Client Brief

The client is Google's data center products and services documentation. The sample content is the Cloud TPU documentation for three hardware generations: TPU v5p, TPU v6e (Trillium) and TPU7x (Ironwood)234. The MANIFEST project converted this content to DITA, with generation values stored once as keys6.

It is a good test for two reasons. First, the generations repeat a structure, so there are real siblings to learn from. Second, the answer exists. AlignR holds out TPU7x, drafts its topics from the v5p and v6e topics and the TPU7x values, and compares the draft with the page Google published. That page was last updated on 13 October 20262.

TermMeaning in this document
Topic typeA DITA content model: concept, task or reference. Each type fixes which elements a topic can contain and in what order.
Sibling topicA topic of the same type in the same place in the map, for another generation. The v6e VM topic is a sibling of the TPU7x VM topic.
Key and key referenceA value defined once in a map (tpu7x-hbm-capacity) and pulled into topics with keyref. Change the key, every page changes.
RELAX NG grammarThe normative OASIS grammar files for DITA. They decide whether a topic is valid DITA11.
SchematronAn ISO rule language for checks a grammar can't express, such as "spec values must use a key"13.
Structured outputA Gemini API setting that makes the model return JSON that matches a JSON Schema you supply9.
Draft commentThe DITA <draft-comment> element. Processors don't render it by default, so it carries open questions safely14.
Preference pairA prompt with a rejected and a chosen answer. Here: the agent's draft (rejected) and the text the writer merged (chosen)5.
Figure 1
System context: as-is state
ENGINEERING DOCS REPOSITORY AD HOC Launch spec TPU7x values · prose DITA set in Git v5p · v6e topics · keys General chat assistant no schema · no sources Technical writer copies v6e topic · edits each value prose, not DITA DITA-OT build in CI grammar errors found late Engineer review checks every number Published TPU7x pages docs.cloud.google.com fix, rebuild, re-review 192 GiB here, 192 GB there
The structure exists in the repository, but nothing uses it to draft. The writer copies by hand, the grammar check runs only at build time, and the engineer reviews whole pages to find a few numbers. The red tag is a real case from the TPU7x page (§2.1).

§2 Problem Statement

A new hardware generation is the most predictable writing job in data center documentation. The topic set is known in advance. The structure is the same as last time. Only the values and a few new concepts change. Today that predictability is used by hand: a writer copies the previous set and edits it.

2.1 Same skeleton, new values

Figure 2 lines up the three TPU generation pages as Google publishes them. Every page has the same four slots. The labels in those slots have drifted, because each page was written separately.

Figure 2
One skeleton, three pages: headings as published
TPU v5p TPU v6e (Trillium) TPU7x (Ironwood) FIRST SENTENCE SPEC TABLE CONFIGURATIONS VM SECTION THIS GENERATION ONLY "This document describes the supported configurations" "This document describes the supported configurations" "This page describes the available configurations" System architecture System architecture System architecture Configurations Supported configurations Supported configurations VM, host and slice properties VM types TPU7x VM Twisted torus topologies Cloud TPU ICI resiliency none Memory hierarchy · Dual-chiplet Supported models · Hyperdisk
same slot, same wordingsame slot, wording driftednew to this generationempty
Headings and first sentences as published on 13 October 2026234. Four slots repeat on every page. The purple sections are where a writer's knowledge is needed most, and where no sibling exists to learn from.
A real example
The TPU7x page states its memory values twice. The specification table gives HBM capacity as 192 GiB and bandwidth as 7380 GBps. The memory hierarchy section below it says each chip has 192 GB of HBM, with bandwidth of approximately 7.37 TB/s2. GiB and GB are different units. When a value is typed into prose, it can drift from the table. When it is a key, it can't.

The skeleton stays. The values move a lot from one generation to the next (Figure 3). In a DITA set every one of these numbers is a key, so a new generation adds a column of values, not a new structure.

Figure 3
The values that change: four keys across three generations
Chips per Pod 8,9602569,216 Peak BF16 TFLOPs / chip 4599182,307 HBM / chip (GiB) 9532192 HBM bandwidth / chip (GBps) 2,7651,6387,380 v5pv6eTPU7x v5pv6eTPU7x v5pv6eTPU7x v5pv6eTPU7x
existing generations: keys already in the setnew generation: values AlignR must place
Values from the TPU7x comparison table2. Each chart has its own scale. The generator never has to invent one of these numbers. It has to put each one in the right slot, as a key, and nowhere else.

2.2 Why the current options fail

There are four common ways to draft the new set. None of them gives a writer a draft that is guaranteed to be valid DITA with sourced values.

OptionWhat it doesWhat it missesResult
Copy the last generation and edit by handThe writer copies each v6e topic and changes the values.No check until the build. Labels drift (Figure 2). Values get typed twice.Slow
Prompt a general chat assistantWrites prose from a description of the new chip.Output is not valid DITA. Values have no source and no keys.Invalid
Retrieval plus a prompt, with no gateAdds the sibling topics to the prompt as examples.Closer in shape, but the XML and every value are still unchecked.Unchecked
Fine-tune a model (original AlignR)Trains on preference pairs so the model learns the house structure.Needs 12,000 to 20,000 pairs over 4 to 8 weeks5. A tuned model is still free to write invalid XML.No data yet
AlignR v0.1: constrained generationGemini fills a JSON Schema built from the content model. Python writes the XML. Two schema gates run before review.Prose quality still needs a writer. New concepts still need a writer.Valid by construction
Figure 4
Options by upfront cost and by what is guaranteed before a writer reads the draft
UPFRONT COST BEFORE THE FIRST DRAFT → GUARANTEE BEFORE REVIEW → lowhigh nonevalid DITA Chat assistant Retrieval + prompt Copy and edit (build checks late) Supervised fine-tuning Preference tuning (AlignR v1) AlignR v0.1 schema at generation + grammar at review tuning moves right, not up
A qualitative placement, not a measurement. Tuning raises the upfront cost but does not add a guarantee, because a tuned model can still write a topic outside the content model. A schema gate adds the guarantee at almost no cost.
Problem statement
Each new TPU generation needs a topic set with the same structure as the last and new values. Writers build it by copying and editing by hand, so labels drift and values get typed twice. A general model drafts faster, but its output is not valid DITA and its values have no source. Fine-tuning a model to learn the structure needs data the team does not have, and still does not guarantee valid output.

§3 User Profiles

Five roles at Google decide whether AlignR is useful. Two readers at Google use what it produces.

Documentation lead, Cloud TPUSponsor
GoalShip launch documentation on launch day with fewer writer hours for each new topic set.
Measured byWriter hours per generation set. Cost per topic.
TodayEach generation set starts as a copy of the last one.
ConcernPaying for AI work with no measured return.
SuccessA logged cost for every topic, and no training spend until the log justifies it.
Technical writer, Cloud TPUMain user
GoalStart each topic from a draft that is valid and sourced.
Measured byTime to first draft. Share of the draft changed before merge.
TodayCopies the v6e topics, edits each value, and finds missed values in review.
ConcernReviewing confident text that is wrong.
SuccessEvery value is a key or has a source. Every gap is a marked question.
Information architectStructure owner
GoalKeep the content model intact as content grows.
Measured byGrammar and rule failures at merge.
TodayCatches structure drift by reading pull requests.
ConcernGenerated content that bends the model a little at a time.
SuccessThe generator can't produce a topic outside the content model. Her rules decide pass or fail.
TPU hardware product engineerContributor
GoalAnswer only the questions that need him.
Measured byQuestions per launch. Time to answer.
TodayReviews whole pages to check a few numbers.
ConcernA wrong specification published under his launch.
SuccessA short list of questions, each with the sentence in context.
Customer support engineer, Cloud TPUDownstream
GoalGive customers one answer for one specification.
Measured bySupport cases that cite two different documented values.
TodayFinds conflicts such as GiB in a table and GB in prose, and reports them by hand.
ConcernGenerated pages that multiply the conflicts.
SuccessOne value, one key, on every page.

Readers

Aiko TanakaReader
RoleML engineer on a model training team at Google.
GoalSize a training batch for TPU7x from the HBM per chip.
TodaySees 192 GiB in one place and 192 GB in another, and asks which to use.
SuccessThe same number and unit on every TPU7x page.
Rafael MendesReader
RolePlatform engineer at Google who provisions TPU slices.
GoalCompare slice shapes and VM sizes across v6e and TPU7x.
TodayFinds the VM details under three different headings.
SuccessThe same table layout and heading for every generation.

§4 User Stories

Each story has acceptance criteria that a check in §5 tests.

US-1 · Documentation lead
As the documentation lead, I want each generated topic's model cost and edit ratio logged, so that I pay for tuning only when the log shows it pays.
Accepted when: every run writes one row with tokens, cost and attempts; log_pair adds the edit ratio after merge; the tuning rule in §5.9 reads only that table.
US-2 · Technical writer
As the Cloud TPU writer, I want a first draft of each TPU7x topic built from its v6e and v5p siblings, so that I start from structure and sources, not from a copy.
Accepted when: the draft pull request passes every gate; every value is a keyref or carries a source ID; every open question is a <draft-comment>.
US-3 · Information architect
As the information architect, I want generated topics to pass the same grammar and rules as hand-written topics, so that the content model holds as volume grows.
Accepted when: validate_grammar and check_rules are required status checks; the agent's account has no merge permission.
US-4 · Product engineer
As the TPU product engineer, I want questions only where the sources are silent, so that I don't review whole pages.
Accepted when: check_grounding passes with no unsourced value; each question links to its line in the pull request.
US-5 · Support engineer
As a support engineer, I want every specification value in new topics to come from one key, so that two pages never disagree.
Accepted when: the Schematron rule spec-values-use-keys passes; no literal specification value remains in the topic.
US-6 · Aiko, reader
As an ML engineer, I want the HBM per chip to have one number and one unit on every TPU7x page, so that I size my batch once.
Accepted when: check_grounding compares units as well as numbers, and flags GiB against GB as a mismatch.
US-7 · Rafael, reader
As a platform engineer, I want the TPU7x configuration table to use the v6e layout, so that I can compare generations side by side.
Accepted when: the Schematron rule same-columns-as-siblings passes for every reference table.
Figure 5
Stories mapped to the agent tools
find_siblings load_facts build_schema generate_topic serialise_dita validate_grammar check_rules check_grounding open_pr log_pair US-1 Lead · cost log US-2 Writer · first draft US-3 IA · same gates US-4 Engineer · questions US-5 Support · one key US-6 Aiko · one unit US-7 Rafael · same layout
deterministic toolcheck that decides pass or failmodel call
Only two stories touch the model, and neither depends on it alone. Every acceptance test runs on a deterministic tool or check.

§5 Proposed Design

AlignR v0.1 is an agent built with Agent Development Kit (ADK), Google's open-source agent framework16. It uses Gemini 3.8 Flash (gemini-3.8-flash)17 in one mode only: structured output, where the model must return JSON that matches a JSON Schema9. Plain Python tools, called through function calling18, do the rest. They find siblings, write the XML and run the checks. The checks decide pass or fail, and the agent can't override them.

5.1 How it works

A writer opens an issue for a new generation set. AlignR reads three inputs that already exist: the launch values, the DITA set, and the content model. It drafts each topic, gates it, and opens one draft pull request per topic. Figure 6 is the same context as Figure 1, with AlignR added.

Figure 6
System context: AlignR added
Launch facts file TPU7x values · source IDs DITA set in Git maps · v5p, v6e topics · keys Content model DITA RELAX NG · Schematron ALIGNR AGENT · ADK · CLOUD RUN find_siblings load_facts build_schema serialise_dita validate_grammar check_rules · Vale check_grounding Gemini 3.8 Flash fills the JSON Schema never writes XML schema JSON DITA XML with key references failed fields · max 2 Draft pull request report · questions as draft comments Writer · IA · engineer review · answer · merge Edit log in BigQuery pairs · cost · edit ratio merge
existing sourcedeterministic tool or outputmodelcheck that decidesretry path
The model sits in the middle of the agent, boxed in on both sides. Deterministic tools decide what it sees and what shape it must return. Checks decide whether its output reaches a person.

5.2 Two schema gates

The schema is the guardrail, twice. Gate 1 runs inside the Gemini API: the response must match the JSON Schema that AlignR built for this run. Gate 2 runs on the XML: the topic must be valid against the OASIS DITA grammar, which is normative in RELAX NG11. Between and after the gates, three checks catch what a grammar can't.

Figure 7
What each stage stops on the way to a writer
GATE 1 · IN THE GEMINI API JSON Schema stops: missing fields, wrong types, keys and sources that don't exist, lists that are too long TOOL · PYTHON serialise_dita writes the XML from the JSON; turns literal values into key refs; questions into draft comments GATE 2 · ON THE XML DITA RELAX NG grammar stops: elements in the wrong place, missing required elements, invalid attributes CHECK · ISO SCHEMATRON House rules stops: spec values not in keys, table columns that differ from siblings, long short descriptions CHECK · PYTHON Grounding stops: numbers, units, flags and commands with no matching fact; GiB written where the fact says GB CHECK · VALE GOOGLE PACKAGE Style stops: Google style guide errors, such as headings in title case; warnings go to the report
schema gatedeterministic toolcheck
Gate 1 shapes the output. Gate 2 proves it is DITA. The checks prove it is house DITA with true values. A topic reaches a writer only after all six.
StageRuns onCatchesCan't catch
Gate 1: JSON SchemaGemini API, at generationShape, types, required fields, enums of real keys and sources, list lengths9Wrong values in the right shape. Google's own guidance is to validate values in the application9.
Gate 2: DITA grammarlxml RelaxNG with the OASIS grammars12Any topic that is not valid DITAValid DITA that breaks house rules
House ruleslxml.isoschematron13Rules the IA owns: keys for spec values, sibling table layout, ID patternsWhether a value is true
GroundingPython, against facts and keysValues, units, flags and commands with no sourceWhether the prose explains well
StyleVale with the Google package15Google developer documentation style errorsMeaning. That is the writer's job.

5.3 From the DITA content model to a JSON Schema

AlignR does not give Gemini the DITA grammar. Gemini accepts a subset of JSON Schema and may reject very large or deeply nested schemas9. Instead, build_schema maps one topic type to a small, flat JSON Schema. Figure 8 shows the mapping for a reference topic.

Figure 8
Reference topic: content model, JSON Schema field, serialised XML
DITA CONTENT MODEL JSON SCHEMA FIELD (GATE 1) XML FROM serialise_dita <title> <shortdesc> <properties><property> rows <section><title> · <p> no element no element titlestring · maxLength 80 shortdescstring · maxLength 300 specs[ ]label · key: enum of real keyscolumns fixed by siblings sections[ ]title · paras[ text, sources ]sources: enum S1..Sn · minItems 1 new_keys[ ]name pattern · value · unit enum questions[ ]text · for: engineer | writer <title> <shortdesc> <propvalue><ph keyref="…"/> <section> · <p>sources in report <keydef> in map PR <draft-comment>
DITA elementJSON field Gemini fillsXML Python writesopen question
The schema is built fresh for each run. Its enums list only the keys and source IDs that exist for this topic, so Gemini can't cite a source that isn't there or invent a key name.

This is an excerpt of the schema that build_schema produces for the TPU7x system architecture topic. JSON Schema keywords such as enum, required, minItems and additionalProperties are all supported in the Gemini API9. Google extended JSON Schema support to all actively supported Gemini models in November 202510.

// build_schema output (excerpt) · topic type: reference · run 0417
{
  "type": "object", "additionalProperties": false,
  "required": ["title", "shortdesc", "specs", "sections", "questions"],
  "properties": {
    "specs": { "type": "array", "minItems": 11, "maxItems": 11,
      "items": { "type": "object", "required": ["label", "key"],
        "properties": {
          "label": { "type": "string" },
          "key":   { "enum": ["tpu7x-chips-per-pod", "tpu7x-bf16-tflops", "tpu7x-hbm-capacity", …] } } } },
    "sections": { "type": "array", "items": { … "paras": [{ "text", "sources": { "enum": ["S1","S2","S3"] }, "minItems": 1 }] } },
    "questions": { "type": "array", "items": { "properties": { "for": { "enum": ["engineer", "writer"] } } } }
  }
}

The row count in specs comes from the sibling tables, not from the model. The siblings decide the layout.

5.4 Agent tools

The agent has ten tools. One calls the model. The others give the same result every time. The grounding check and the question placeholder come from The Autonomous Author7. Reading structure before calling a model comes from KnowledgeFlow8.

1. Find sibling topics find_siblings

Reads the DITA map. Returns the topics of the same type in the same place for earlier generations. It uses the map, not a search index.

output: 2 or 3 sibling topics, with their topic type and position

2. Load the new values load_facts

Reads the launch facts file and the existing key definitions. Gives each fact a source ID and records its unit.

output: facts with source IDs (S1 to Sn), values and units

3. Build the schema build_schema

Maps the topic type to a JSON Schema (Figure 8). Fills its enums with this run's keys and source IDs. Fixes table rows and columns from the siblings.

output: one JSON Schema for this topic and run, with a hash

4. Draft the topic generate_topic model

Calls Gemini 3.8 Flash with structured output. The prompt holds the siblings, the facts and the house style. On a revision, it holds only the failed fields and the reasons.

output: JSON that matches the schema (gate 1)

5. Write the XML serialise_dita

Turns the JSON into a DITA topic. Writes a keyref for every key. Replaces any literal value that matches a key. Writes questions as <draft-comment> and new keys as a map change.

output: one .dita file and, if needed, a key definition proposal

6. Validate the grammar validate_grammar gate 2

Validates the topic with lxml against the OASIS DITA RELAX NG grammars1112. Any error fails the run.

output: pass, or errors with line numbers

7. Check house rules check_rules check

Runs the information architect's Schematron rules with lxml.isoschematron13, then Vale with the Google package15. Some rules carry a safe automatic fix, such as sentence case in a title.

output: rule IDs that fail, fixes applied, Vale warnings

8. Check every value check_grounding check

Every number, unit, flag and command in the topic must match a fact or a key. Units must match as well as numbers, so GiB against GB fails.

output: grounded values out of all values, with each mismatch

9. Open the pull request open_pr

Opens a draft pull request with the report (§7). Assigns each question to the engineer or writer it names. The agent's account can't merge.

output: draft pull request and report comment

10. Log the edit log_pair

Runs on merge. Stores the agent's draft as rejected and the merged text as chosen, field by field, in the original AlignR pair format5. Adds the edit ratio and cost.

output: rows in the BigQuery edit log

5.5 Agent loop

The model decides how to fill each field. The checks decide whether it is done. Deterministic fixes run before any model revision, so the model is called again only for what code can't fix.

Figure 9
Agent loop for one topic
Siblings, facts, schema Gemini fills the schema Serialise and apply safe fixes All checks pass? Draft PR yes no Gemini revises failed fields only up to 2 times still failing after 2 Field becomes a draft comment safe fixes, no model call: literal value → keyref title case → sentence case id → house pattern
A field is never dropped silently. It either passes, or it reaches the writer as a marked question. The limit of two revisions keeps the cost of one topic bounded.

5.6 One topic, end to end

Figure 10 follows one topic from the issue to the edit log. It is the run in the third simulator scenario (§6).

Figure 10
Sequence: the TPU7x training task topic
Writer GitHub AlignR agent Gemini Checks Engineer 1 issue: new set TPU7x 2 Actions trigger 3 siblings · facts · schema 4 prompt + schema 5 JSON · gate 1 ok 6 serialised XML 7 grammar ok · 1 value ungrounded 8 revise steps[4] 9 JSON 10 re-check 11 all pass · 1 open question 12 draft PR + report 13 question with line link 14 edit · merge 15 merge → log_pair
Two model calls for this topic: one draft and one revision of one field. The engineer receives one question, not a page to review.

5.7 Sample output

This is the start of the topic that AlignR writes for the TPU7x system architecture. The values are key references, so the table and the prose can't disagree. The one fact that the sources don't settle is a question, not a guess.

Figure 11
Generated reference topic, as the writer sees it in the pull request
<reference id="tpu7x-system-architecture" xml:lang="en-US">
  <title>TPU7x system architecture</title>
  <shortdesc>Each TPU7x chip contains two TensorCores and four SparseCores.</shortdesc>
  <prolog><metadata>
    <othermeta name="generated-by" content="alignr 0.1 · run 0417 · schema 3f9c"/>
  </metadata></prolog>
  <refbody>
    <properties id="specs">
      <prophead><proptypehd>Specification</proptypehd><propvaluehd>TPU7x</propvaluehd></prophead>
      <property><proptype>Number of chips per Pod</proptype>
        <propvalue><ph keyref="tpu7x-chips-per-pod"/></propvalue></property>
      <property><proptype>HBM capacity per chip</proptype>
        <propvalue><ph keyref="tpu7x-hbm-capacity"/></propvalue></property>
      <!-- 9 more rows, same layout as the v6e sibling -->
    </properties>
    <section id="memory"><title>Memory hierarchy</title>
      <p>Each chip has <ph keyref="tpu7x-hbm-capacity"/> of high-bandwidth memory (HBM).</p>
      <draft-comment author="alignr" disposition="open">
        For the engineer: S1 gives HBM as 192 GiB. S2 gives 192 GB. Which unit is correct?
      </draft-comment>
    </section>
  </refbody>
</reference>
Key referencesGemini chose the key from an enum. Python wrote the <ph keyref>. The prose and the table read one value.
Draft commentA real conflict in the sources becomes a question for the engineer. Processors don't render it by default14.
ProvenanceThe run ID and schema hash link the topic to its row in the edit log.
Abridged. The sources S1 and S2 are the specification table and the memory hierarchy section of the TPU7x page2. A release build can fail on any open draft comment, so a question can't ship by accident.

5.8 The edit log

The original AlignR design spent weeks collecting preference pairs before it could train5. In v0.1 the pairs collect themselves. Every merged pull request is a set of pairs: the agent's draft field and the writer's final field. The log is used first for cheap fixes, and only later, if ever, for training.

Figure 12
The edit log loop: repeated edits become rules first
Draft pull request gated · sourced Writer edits and merges the chosen text log_pair rejected · chosen · edit ratio Weekly IA review edits seen 3 or more times Rule, example or note Schematron · sibling · prompt fewer fixes next time Edit log BigQuery · one row per field Tuning gate (§5.9)
The cheap path closes the loop: a rule costs nothing to run and never drifts. The dashed branch to tuning stays closed until the rule in §5.9 opens it.

Each row keeps the original AlignR preference pair fields and adds what a documentation team needs to trace a change.

// edit log row · same prompt / chosen / rejected core as AlignR v1
{
  "pair_id":        "run0417-sections-1-paras-0",
  "topic_id":       "tpu7x-system-architecture",  "topic_type": "reference",
  "field":          "sections[1].paras[0].text",
  "rejected":       "Each chip includes HBM for large batch sizes.",
  "chosen":         "Each chip has {tpu7x-hbm-capacity} of HBM. The large capacity allows large batch sizes.",
  "edit_ratio":     0.41,  "reviewer_role": "technical_writer",
  "model_id":       "gemini-3.8-flash",  "schema_hash": "3f9c",  "attempts": 1,
  "tokens_in":      11840,  "tokens_out": 1960
}

5.9 When tuning pays

Fine-tuning is not a later phase of this MVP. It is a decision the edit log can make, with a fixed rule. Until the rule says yes, the cheaper fixes in §5.8 come first.

Tuning rule
  1. The log holds at least 12,000 accepted pairs for one topic type. This is the floor the original AlignR design set for one domain5.
  2. The edit ratio for that topic type has stopped falling for three launches in a row, after new rules and examples.
  3. A replay of held-out pairs shows that a tuned model saves more writer hours than its training and serving cost.
Figure 13
The tuning rule as a decision
Monthly: read the log 12,000+ pairs for one type? Edit ratio flat 3 launches? Replay saves more than cost? Propose tuning replay as evidence sponsor decides yes yes yes nonono Stay on constrained generation add Schematron rules · better sibling examples · prompt notes next month
Three gates, in order of cost to check. The first is a count, the second is a trend, and only the third needs a training experiment.

The numbers show why the rule is strict. Figure 14 models the cost of one draft attempt with today's prices. Tuning does not make a draft cheaper here. The newest Flash model with supervised fine-tuning on the price list is Gemini 3.5 Flash, and a tuned endpoint for Gemini 3 and later costs 1.5 times its base model19. Tuning can pay only through writer time.

Figure 14
Model cost of one draft attempt (modelled)
Gemini 3.8 Flash, intro price 12k in + 2k out · to 31 Dec 2026 $0.017 Gemini 3.8 Flash, standard price 12k in + 2k out · from 1 Jan 2027 $0.033 Tuned Gemini 3.5 Flash endpoint 3k in + 2k out · 1.5 × base price $0.034 tuned path also needs, upfront: ~$1,800 training + 1,920 annotator hours
Prices from the Agent Platform pricing page19. Token counts are assumptions. A tuned prompt is shorter because it needs no sibling examples. Training: 12,000 examples × 5,000 tokens × 3 epochs at $0.01 per 1,000 tokens20. Annotator hours from AlignR v15.
Figure 15
How far one launch gets toward the tuning floor (modelled)
■ 1 launch ≈ 12 topics × 5 edited fields = 60 pairs □ 200 launches = 12,000 pairs, the tuning floor the TPU docs cover 7 generations, v2 to TPU7x
The pairs per launch are an assumption, and other product sets add to the same log. The point is the order of magnitude: the floor is far away, so the log, not a forecast, should decide.
Price used in Figure 14ValueSource
Gemini 3.8 Flash, per 1M tokens in / out$0.75 / $3.75 to 31 December 2026; $1.50 / $7.50 from 1 January 2027Agent Platform pricing19
Gemini 3.5 Flash, per 1M tokens in / out$1.50 / $9.00Agent Platform pricing19
Tuned endpoint, Gemini 3 and later1.5 times the base model priceAgent Platform pricing19
Supervised fine-tuning, Gemini 3.5 Flash$0.01 per 1,000 training tokensAgent Platform pricing19
Preference tuning, Gemini 2.5 Flash$0.005 per 1,000 training tokensAgent Platform pricing19
Training tokensDataset tokens × number of epochsSupervised fine-tuning docs20

5.10 Where it runs

PartTechnologyFree tier or licence
Agent frameworkAgent Development Kit16Apache 2.0
ModelGemini 3.8 Flash (gemini-3.8-flash), structured outputGemini API free tier for the POC21
Grammar gateOASIS DITA RELAX NG grammars, validated with lxml1112Open standard; open source
House rules and stylelxml.isoschematron13, Vale with the Google package15Open source; package under CC BY 4.0
Build checkDITA-OT 4.4, as in MANIFEST6Apache 2.0
Trigger and review gateGitHub Actions, protected branches25Actions free for public repositories24
Agent runtimeCloud Run job, one run per issue180,000 vCPU-seconds a month22
Edit logBigQuery10 GiB storage and 1 TiB of queries a month23

Data handling. The sample content is public, so the Gemini API free tier is acceptable for the POC. Content sent on the free tier is used to improve Google products, so unreleased launch values need the paid tier21.

Known limits. AlignR drafts what has a sibling. A concept that is new in this generation, such as the TPU7x dual-chiplet design, gets a stub with questions, not prose. lxml checks the grammar but doesn't apply the default attribute values that DITA processing needs, so DITA-OT still builds every topic in CI.

§6 Draft Simulator

Three runs from the TPU7x set. The first passes cleanly. In the second, code fixes everything. In the third, Gemini fixes one field and the engineer gets one question.

alignr · draft simulator
idle
Sample topic
// choose a sample topic and run it
Scripted demo. No live model call. Token counts, costs and timings are illustrative.

§7 Success Metrics

The MVP is measured on the TPU7x holdout. Each metric comes from a check result or the edit log, not from a judgement.

MetricHow it is measuredMVP target
Drafts that reach a writer and fail the DITA grammarDraft pull requests opened with a failing validate_grammar result0
Values with a sourcecheck_grounding: values with a key or source ID, divided by all values100%
Specification values written as keysSchematron rule spec-values-use-keys100%
Edit ratio on topics with a siblingChanged tokens between the draft and the published TPU7x text2Under 40%
Time to first draftIssue opened to draft pull requests for the whole setUnder 15 minutes
Open questions at mergeDraft comments left open when a pull request merges0
Model calls per topicRun log in BigQuery1.5 or fewer on average, falling as rules grow
Runs with logged costRuns with tokens and cost in the edit log100%
Figure 16
Sample report on a draft pull request
Draft PR #320 · issue #212 · tpu7x-train (task)ready for review
validDITA grammar
11/11values grounded
2model calls
1open question
Built from
  • Siblings: v6e-training, v5e-training (task, same map position)
  • S1 TPU7x page: machine type tpu7x-standard-4t, JAX and PyTorch supported
  • Schema c41e: task, 4 to 8 steps, commands checked after generation
Checks
  • Grammar: valid against the DITA task grammar
  • Rules: 6 of 6 pass. Vale: 0 errors, 2 warnings (Google.Parens, Google.Will)
  • Grounding: step 4 had --version=v2-alpha-tpu7x with no source. The pattern came from the v6e sibling. Revised once.
Questions
  • For the engineer: which TPU software version does TPU7x use? (step 4, line 38)
Cost

2 calls · 15,290 tokens in · 2,600 tokens out · $0.021 at the introductory price

The report tells the writer where to look. Here: one step and one question. The rest of the topic has passed every gate.
Figure 17
Holdout scorecard: how much of each TPU7x draft survives review
0%25%50%75%100% System architecturereference 70% 25% Supported configurationsreference 75% 20% TPU7x VMreference 80% 20% Hyperdiskreference 55% 35% 10% Train a model using TPU7xtask 50% 40% 10% Memory hierarchyconcept 25% 45% 30% Supported modelsconcept 30% 40% 30% Dual-chiplet architectureconcept 15% 85% NEW IN THIS GENERATION · NO SIBLING HAS A SIBLING
kept as draftededited by the writerwritten by the writer
Sample scorecard format with illustrative values, not results. It shows the expected shape: topics with a sibling keep most of the draft, and the writer's time moves to what is new in the generation.

Sources

Checked on 19 October 2026.

  1. Google Developers, "Site Policies." developers.google.com/site-policies
  2. Google Cloud, "TPU7x (Ironwood)," last updated 13 October 2026. docs.cloud.google.com/tpu/docs/tpu7x
  3. Google Cloud, "TPU v6e," last updated 13 October 2026. docs.cloud.google.com/tpu/docs/v6e
  4. Google Cloud, "TPU v5p," last updated 13 October 2026. docs.cloud.google.com/tpu/docs/v5p
  5. Siddharth Rao, "AlignR: Behavioural Alignment for Regulated Industry AI," design document v1.0.0. github.com/siddharthraop/AlignR
  6. Siddharth Rao, "MANIFEST," design document. siddharthraop.github.io/Manifest
  7. Siddharth Rao, "The Autonomous Author," design document. siddharthraop.github.io/The-Autonomous-Author
  8. Siddharth Rao, "KnowledgeFlow," design document. siddharthraop.github.io/KnowledgeFlow
  9. Google AI for Developers, "Structured outputs," last updated 13 October 2026. ai.google.dev/gemini-api/docs/structured-output
  10. Google, "Improving Structured Outputs in the Gemini API," 5 November 2025. blog.google/technology/developers/gemini-api-structured-outputs
  11. DITA 1.3 specification, "Recognized XML-document grammar mechanisms." dita-lang.org/1.3/dita/archspec/base/recognizedconstraintmechanisms
  12. lxml, "Validation with lxml." lxml.de/validation.html
  13. lxml, "lxml.isoschematron package." lxml.de/apidoc/lxml.isoschematron.html
  14. DITA language reference, "draft-comment." dita-lang.org/dita/langref/base/draft-comment
  15. Vale, "Google" style package. vale.sh/hub/google
  16. Google, Agent Development Kit documentation README. github.com/google/adk-docs
  17. Google AI for Developers, "Gemini models." ai.google.dev/gemini-api/docs/models
  18. Google AI for Developers, "Function calling with the Gemini API." ai.google.dev/gemini-api/docs/function-calling
  19. Google Cloud, "Agent Platform pricing: Generative AI." cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing
  20. Google Cloud, "About supervised fine-tuning for Gemini models." docs.cloud.google.com/.../tuning/supervised-tuning
  21. Google AI for Developers, "Gemini Developer API pricing." ai.google.dev/gemini-api/docs/pricing
  22. Google Cloud, "Cloud Run pricing." cloud.google.com/run/pricing
  23. Google Cloud, "BigQuery pricing." cloud.google.com/bigquery/pricing
  24. GitHub Docs, "How use of GitHub Actions is measured." docs.github.com/en/billing/concepts/product-billing/github-actions
  25. GitHub Docs, "About protected branches." docs.github.com/.../about-protected-branches