Technical Specification

The purpose of a technical specification is to define the technical approach, architecture, technologies, infrastructure, interfaces, and implementation constraints required to deliver the solution. It translates the requirements and functional expectations into a technical framework that guides development, integration, deployment, and ongoing support, while ensuring alignment with relevant standards and technical constraints. It provides a reference for delivery teams to ensure the solution is implemented consistently, reliably, and in accordance with the agreed technical approach.

Data Definitions

Exact definitions for the project data structures are listed in this section. The definitions are provided in an open data definition language: JSON Schema vocabularies (type, required, properties, $ref) written in YAML and organised in the OpenAPI Specification v3.1.0 component style, with reusable definitions under components/schemas.

Definition files are kept in assets/ so that they are readable by Hugo’s resource filesystem. The print-code shortcode resolves its file parameter relative to assets/, which is how each definition is reproduced in this document.

Kanban Card Front Matter

Kanban cards are markdown files with front matter in YAML format. The YAML defines the metadata used by the Kanban system for tracking the progress of tasks.

assets/kanban-card-front-matter.yaml is the definition that card front matter must conform to. Every card under content/kanban/ is validated against it by pnpm run test:kanban, which reports every deviation and fails the build when there is one. The definition was repaired by the is-front-matter-schema-invalid card; the validation it now makes possible is described under Schema Validation.

Copy

kanban-card-front-matter.yaml
kanban-card:
  type: object
  required:
    - title
    - description
    - date
    - author
    - params
  additionalProperties: false
  properties:
    title:
      $ref: '#/components/schemas/title'
    description:
      $ref: '#/components/schemas/description'
    date:
      $ref: '#/components/schemas/date'
    author:
      $ref: '#/components/schemas/author'
    params:
      $ref: '#/components/schemas/params'
components:
  schemas:
    title:
      type: string
      maxLength: 40
    description:
      type: string
      maxLength: 50
    date:
      type: string
      format: date
      example: '2026-10-01'
    author:
      type: string
      maxLength: 40
    params:
      type: object
      required:
        - completionDate
        - due
        - dependencies
        - references
      additionalProperties: false
      properties:
        completionDate:
          type:
            - string
            - 'null'
          format: date
          example: '2026-10-01'
        due:
          type:
            - string
            - 'null'
          format: date
          example: '2026-10-01'
        references:
          type:
            - array
            - 'null'
          items:
            type: string
        dependencies:
          type:
            - array
            - 'null'
          items:
            type: string

Card Identity

A card is identified by its filename, not by a field inside it. The filename is p<priority>-<prefix>-<slug>.md, for example p2-us-html-check.md:

The <prefix>-<slug> pair is the card’s id. It must be unique across every stage folder, because the documentation shortcodes select cards by the pattern p[0-9]-<prefix>-.* and present that id to the reader. Card filenames carry no serial numbers: a serial number has to be reassigned whenever a card is reordered, and it collides between kinds, so it carries no information that the id does not already carry. The kan tool searches all stages and refuses to write a card whose id is taken.

The kind of a card is recorded by the filename prefix and by the archetype it was created with (hugo new content <path> --kind <kind>), never by a front matter field. The same is true of priority and of status.

Card Kinds

The kind of a card fixes three things at once: the two letter prefix in its filename, the Hugo archetype it is scaffolded from, and the --kind value that archetype is named by. The table below is the single source of truth for that mapping; the board, the documentation shortcodes and the kan tool all read it from here.

PrefixKind (--kind)TypeExampleDescription
idideationIdeationp1-id-initial-conceptIdeas and concept capture
spspecificationsSpecificationp1-sp-write-specsSpecs (requirements, tech, test, design)
dldeliverablesDeliverablesp2-dl-write-the-pdfProduced deliverables
sfscaffoldScaffoldp2-sf-repo-skeletonServices, repo, skeleton, scaffolding
epepicsEpicsp2-ep-kanban-systemBundles of related features
ftfeaturesFeaturesp2-ft-image-handlingMajor and minor features
acacceptanceAcceptancep2-ac-board-is-readableCriteria required for acceptance
dpdeploymentDeploymentp2-dp-publish-to-pagesSteps required to deploy the app
isissuesIssuesp1-is-menu-overlapDefects in the code and content
rlreleasesReleasesp2-rl-v1-launchVersioned releases, tied to GitHub
tstestingTestingp2-ts-html-validationTest and QA work
ususer-storiesUser storiesp2-us-read-a-postRequirements expressed as user stories

A kind with no cards is normal: the summary document keeps one table per kind, so kinds that are not in use render their empty table on purpose.

Adding a kind means adding the three things the kind owns, together: themes/be-best/archetypes/<name>.md, a row in the table above, and the same row in the kinds list in cmd/kan/main.go. Removing one drops all three, and the cards of that kind are reclassified rather than deleted.

Status And Stage

A card’s status is the stage folder that holds it: 01-to-do, 02-in-progress, whose work may be grouped into the topic sub-folders design, features and ui, or 03-done. There is no status and no stage front matter field, so moving the file between stage folders with git mv is the status change, and the version control history of the file is the audit trail. Completing a card additionally sets params.completionDate to the date of completion.

Front Matter Fields

Cards carry the following fields. The order listed by the archetypes is the order new cards are written in.

FieldTypeNotes
titlestringDisplay title, at most 40 characters. Archetypes title-case the slug into it.
descriptionstringOne line summary, kept to 50 characters, used by cards and tables.
datedateCreation date, in the site date format.
authorstringIvan Hawkes, quoted inconsistently across the cards.
paramsobjectThe tracking block described below.
params.completionDatedate or nullDate of completion; null until the card is done.
params.duedate or nullTarget date; null where no date is set.
params.dependenciesarray of ids or nullIds of cards that must be completed first.
params.referencesarray of ids or nullIds of related cards.

A card carries no other keys. layout is derived by Hugo from the content type, lastmod is derived from the git history, and priority, status and kind come from the filename and the stage folder, so none of them is front matter. The card archetypes emit exactly the keys above, and the definition forbids the rest, so a card that grows a layout, lastmod, priority, categories or tags key fails validation.

The body of a card follows the front matter: a short description of 50 characters or less, then the `

anchor, then a body that lists the work and anAcceptance Criteria` section.

Schema Validation

scripts/validate-kanban-front-matter.mjs reads every card under content/kanban/, parses its YAML front matter, and validates it against assets/kanban-card-front-matter.yaml with the JSON Schema keywords type, required, properties, additionalProperties, maxLength and format. A duplicate mapping key is reported by the YAML parser itself, so a key pasted twice cannot hide behind the last definition. The script is run by pnpm run test:kanban, and its current result is:

Copy

22 cards validated against assets/kanban-card-front-matter.yaml: no deviations

Every card sets all four params keys, and no card uses params.dependencies or params.references for anything but null: the keys are documented, and unused so far.

The definition was repaired by the is-front-matter-schema-invalid card, which resolved the deviations below.

DeviationResolution
The definition required a layout field that no card has.Dropped from required: Hugo derives the layout from the content type.
The definition described params.completed; the cards use params.completionDate.Renamed to params.completionDate, the key the cards and the card layout use.
Three numeric blocks were pasted inside params.due, leaving a date field as number.Deleted, so due is a date again.
The definition described priority and lastmod as card fields; no card has them.Both removed: priority is a filename component, and Hugo derives lastmod from the git history.
The archetypes emitted categories and tags, which the definition does not describe.Removed from the card archetypes and from the one card that kept them; kind is the prefix.
The definition allowed a description of 80 characters; the convention is 50.maxLength is 50, and the two longer descriptions were shortened.
The definition said nothing about keys a card must not carry.additionalProperties: false on the card and on params, whose four keys are required.

Card Names And Content Types

Content is organised as Hugo page bundles under content/. The content types in use are post, recipe, kanban, specification, project, profile, dashboard, about and bare, each with a layout directory in themes/be-best/layouts/ and an archetype in themes/be-best/archetypes/. A bundle keeps the markdown of a page together with the images and other assets it uses, which is what makes page-relative resources, featured images and per-bundle caches possible.

Recipes add a sidecar file, ingredients.yaml, next to the bundle’s index.md. The recipe front matter carries cuisine, categories, tags, portions, prep-time and cook-time, the last two as ISO 8601 durations such as PT5M.

Dashboard pages are development scratch pages. They are listed as links; the dashboard/section.html layout does not embed their content in a listing, because embedding a whole page nests main, nav and footer landmarks inside the listing page.

Presentation Layer

The theme is themes/be-best, a git submodule that is published on its own repository’s features branch. The parent repository records the theme commit as a submodule pointer, so a theme change is committed and pushed inside the submodule before the parent records it.

Styling is Tailwind CSS v4. The theme’s source stylesheet is themes/be-best/assets/css/main.css; the compiled stylesheet is assets/css/main.css, which is build output and is not versioned. Hugo writes assets/jsconfig.json for Tailwind v4 editor intelligence during the CSS build, and hugo_stats.json records the classes in use; both are build output. The theme’s scripts are TypeScript in themes/be-best/assets/js (menu, sidebar, dark mode, and the codeblock copy button), compiled by Hugo into one small bundle.

Interfaces

Content reaches the presentation layer through three documented contracts.

Markdown render hooks, in themes/be-best/layouts/_markup/:

Shortcodes, in themes/be-best/layouts/_shortcodes/:

The Kanban board itself is the kanban section, rendered by themes/be-best/layouts/kanban/section.html as a responsive grid of card partials.

Tooling

Kanban Card Scaffolder

cmd/kan is a Go program that scaffolds cards. It takes a kind and the words of a title, resolves the kind from its name, its prefix, or an alias, normalises the slug, refuses an id that is already used in any stage, and writes the new card into 01-to-do from the matching archetype. Cards are created there by default because that is where new work starts. The tool is used through go run ./cmd/kan, and its unit tests run with go test ./cmd/kan.

The command line is:

Copy

kan [flags] <kind> <title words...>
FlagDefaultMeaning
-stage, -s01-to-dodestination stage: 1/2/3, to-do, done, …
-priority, -p2priority digit, written into the filename
-dry-run, -noffprint the hugo command, create nothing

The behaviour the tool must provide:

The card is written by executing Hugo, so the archetype stays the only source of card front matter. Each kind maps to one Hugo command, and the tool must be able to execute every variation, with the --kind value taken from the Card Kinds table:

Copy

hugo new content kanban/01-to-do/p2-ac-test.md --kind acceptance
hugo new content kanban/01-to-do/p2-dl-test.md --kind deliverables
hugo new content kanban/01-to-do/p2-dp-test.md --kind deployment
hugo new content kanban/01-to-do/p2-ep-test.md --kind epics
hugo new content kanban/01-to-do/p2-ft-test.md --kind features
hugo new content kanban/01-to-do/p2-id-test.md --kind ideation
hugo new content kanban/01-to-do/p2-is-test.md --kind issues
hugo new content kanban/01-to-do/p2-rl-test.md --kind releases
hugo new content kanban/01-to-do/p2-sf-test.md --kind scaffold
hugo new content kanban/01-to-do/p2-sp-test.md --kind specifications
hugo new content kanban/01-to-do/p2-ts-test.md --kind testing
hugo new content kanban/01-to-do/p2-us-test.md --kind user-stories

Front Matter Validator

scripts/validate-kanban-front-matter.mjs validates the front matter of every Kanban card against assets/kanban-card-front-matter.yaml, using Ajv with ajv-formats, and reports each deviation. yaml parses the front matter, so a duplicated key is an error rather than a silent overwrite.

The kan tool is built and installed for interactive use with go build -o ~/.local/bin/kan ./cmd/kan; the source of the program is the repository, and the installed binary is a convenience copy of it.

Build And Publishing

The scripts in package.json are the entry points: test:css, test:hugo, test:html and test:kanban are combined by pnpm run test, which is also the local equivalent of the publish pipeline. pnpm run build compiles the CSS and the minified production site, pnpm run css-watch runs the Tailwind watcher, and pnpm run serve builds the CSS once and then serves the site.

The publish workflow, .github/workflows/hugo.yml, builds the CSS, builds the site with --environment production --gc --minify --ignoreCache --cleanDestinationDir, validates the generated HTML, and only then uploads the Pages artifact. Its Hugo cache is keyed on the Hugo version, the pinned theme commit and a hash of the site configuration, so a theme or configuration change starts a cold build.

Implementation constraints: