How to Auto-Fix Section Order Issues with the Fixer Handler in DESIGN.md
The Fixer API automatically reorders DESIGN.md sections to match the canonical specification defined in spec-config.yaml, preserving prelude content at the top and appending unknown sections to the end.
The google-labs-code/design.md repository provides a deterministic Fixer API that eliminates manual reordering of markdown sections. By leveraging the fixSectionOrder handler implemented in packages/cli/src/linter/fixer/handler.ts, you can programmatically enforce canonical section ordering while maintaining content integrity. This guide explains how to auto-fix section order issues using the TypeScript handler and its associated configuration files.
Understanding the Fixer API Architecture
The Fixer API serves as a thin wrapper around the section-order logic used by the DESIGN.md linter. It receives a document split into discrete sections, re-orders those sections to match the canonical order defined in the spec, and returns a new document string together with optional ordering details.
Unlike simple text replacement, the handler respects the semantic structure of DESIGN.md files, handling front matter, section aliases, and custom content intelligently.
How the Section Order Fixer Works
The fixSectionOrder function in packages/cli/src/linter/fixer/handler.ts executes a five-step pipeline to normalize document structure:
Input Validation with FixerInputSchema
The request body must match FixerInputSchema, which expects the original document (content) and an array of parsed sections (sections). Each section contains a heading (the section title) and its raw content according to the Zod schema defined in packages/cli/src/linter/fixer/spec.ts.
Prelude Preservation
A section whose heading is an empty string is treated as the "prelude" and is always kept at the very top of the output. This ensures that introductory material, front matter, or free-form text preceding the first heading remains intact as implemented in handler.ts at line 21.
Known vs Unknown Section Separation
The handler separates sections into two categories:
- Known sections are those whose canonical heading appears in
CANONICAL_ORDER(the ordered list derived fromspec-config.yaml). The helperresolveAliasexpands any alias to its canonical form before the check, as seen inpackages/cli/src/linter/spec-config.tsat line 60. - Unknown sections are left untouched but are appended after all known sections, implemented in
handler.tsat lines 28-32.
Canonical Sorting Logic
The sorter uses the index of each canonical heading inside CANONICAL_ORDER to produce the correct sequence. This deterministic sorting occurs in handler.ts at lines 33-36, ensuring every run yields consistent output regardless of input order.
Document Reassembly
Sections are concatenated (prelude → sorted known → unknown) using a single newline between each piece. The result string is returned as fixedContent. Optional details contain the original and final heading orders for debugging, as defined in handler.ts at lines 51-60.
Implementation Examples
Programmatic Usage with fixSectionOrder
Import the handler directly to integrate auto-fixing into build scripts or editor plugins:
import { fixSectionOrder } from
'@design-md/cli/src/linter/fixer/handler.js';
import type { FixerInput } from
'@design-md/cli/src/linter/fixer/spec.js';
// Assume `parseDesignMd` is a helper that splits a DESIGN.md file
// into the `sections` shape required by the fixer.
const { sections, rawContent } = parseDesignMd('my-design.md');
const input: FixerInput = {
content: rawContent,
sections,
};
const result = fixSectionOrder(input);
if (result.success) {
console.log('✅ Fixed document:');
console.log(result.fixedContent);
// Optional debugging info:
console.log('Before order:', result.details?.beforeOrder);
console.log('After order: ', result.details?.afterOrder);
} else {
console.error('❌ Fix failed:', result.error);
}
CLI Command
The CLI bundles the fixer under the design-md fix command. This command internally parses the file into sections, calls fixSectionOrder, and writes the output:
design-md fix path/to/DESIGN.md --output fixed.md
Web Service Integration
Expose the fixer as an HTTP endpoint using Express:
import express from 'express';
import { fixSectionOrder } from
'@design-md/cli/src/linter/fixer/handler.js';
import type { FixerInput } from
'@design-md/cli/src/linter/fixer/spec.js';
const app = express();
app.use(express.json());
app.post('/api/fix-section-order', (req, res) => {
const input: FixerInput = req.body;
const result = fixSectionOrder(input);
res.json(result);
});
app.listen(3000, () => console.log('API listening on :3000'));
The endpoint expects the same FixerInput shape and returns either a successful fixedContent or an error payload.
Key Source Files
packages/cli/src/linter/fixer/handler.ts: Contains the corefixSectionOrderimplementation that orchestrates the reordering logic.packages/cli/src/linter/fixer/spec.ts: Defines the Zod schema for the fixer request and result types, includingFixerInputSchema.packages/cli/src/linter/spec-config.ts: DefinesCANONICAL_ORDER, the alias map, and theresolveAliashelper used to normalize section headings before sorting.packages/cli/src/linter/linter/rules/section-order.ts: Implements the linter rule that detects out-of-order sections, sharing the same canonical ordering logic as the fixer.packages/cli/src/linter/fixer/handler.test.ts: Provides the test suite demonstrating expected behavior for various input scenarios.
Summary
- The Fixer API in
google-labs-code/design.mdprovides deterministic reordering through thefixSectionOrderhandler. - Prelude safety guarantees that content with empty headings remains at the document top.
- Alias resolution via
resolveAliasensures that alternative section titles map to their canonical positions. - Unknown section preservation means custom or future sections are never dropped, only relegated to the end.
- Validation occurs through
FixerInputSchema, requiring both original content and parsed section arrays.
Frequently Asked Questions
What is the FixerInputSchema?
FixerInputSchema is a Zod validation schema defined in packages/cli/src/linter/fixer/spec.ts that enforces the structure of fixer requests. It requires the original document string (content) and an array of parsed sections, where each section contains a heading and its raw content. This schema ensures type safety before the reordering logic executes.
How does the fixer handle unknown sections?
Unknown sections—those whose headings do not appear in CANONICAL_ORDER—are preserved but appended after all known sections. This behavior, implemented in handler.ts at lines 28-32, ensures that custom or experimental sections are not deleted during the fix process, only moved to the end of the document.
What happens to the prelude section?
The prelude, defined as any section with an empty string heading, is automatically identified and pinned to the top of the output document. According to the source code in handler.ts at line 21, this ensures that front matter, metadata, or introductory text remains in the leading position regardless of sorting operations.
How are section aliases resolved?
The fixer uses the resolveAlias helper from packages/cli/src/linter/spec-config.ts (line 60) to expand alternative section titles to their canonical forms before checking against CANONICAL_ORDER. This allows documents using abbreviated or alternative headings to be correctly sorted according to the official specification.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →