Imported from wesley-dean/mktext (
AGENTS.md). Install upstream withnpx skills add wesley-dean/mktext. Copyright stays with the author.
AGENTS.md
This file provides guidance for AI coding agents working in this repository.
Use this file together with README.md. The README is the human-facing project
overview. This file is the agent-facing operational map.
Project Overview
mktext is a tiny deterministic Bash text-substitution library.
Given template text and a named Bash associative-array context, it replaces
recognized macros such as {TITLE} or {NUMBER4} with literal values.
The project deliberately does less than a general template engine. Acquisition
and transformation belong to callers. mktext performs rendering only.
The canonical maintained implementation is src/mktext.bash. A prepared
make build generates three sourceable/executable release flavors under dist/:
mktext.dev.bash, mktext.bash, and mktext.min.bash, plus one .sha256
SHA-256 checksum file for each. All three Bash artifacts embed the same version,
source-revision timestamp, and commit metadata and preserve the same public API.
Read the ADRs and Specification First
The ADR collection is the canonical source of architectural intent.
Before making significant changes, review doc/decisions.md for the concise
architectural map and then read the relevant files under doc/adr/. Also review
doc/mktext-spec.md when the change can affect public behavior. Checksum
companion naming and historical-read compatibility are defined by ADR-018, while
ADR-019 governs the generated ADR landing page and offline documentation boundary.
The doc/adr/README.md file used as the generated Doxygen landing page is not
maintained source and may not exist in a fresh checkout. Its maintained framing
lives in doc/adr/README.intro.md and doc/adr/README.outro.md; the linked ADR
list is generated from the ADR corpus by the pinned adrctl dependency.
In particular, preserve these foundational boundaries:
mktextperforms substitution, not acquisition or transformation;- templates are untrusted data and are never executed or shell-expanded;
- context values are inserted literally;
- rendering is nonrecursive;
- unknown and malformed macro text is preserved by default;
- the public API is intentionally small and stable;
- deterministic, inspectable behavior is preferred over convenience features.
Clarify Before Acting
When a request is ambiguous or incomplete, identify whether two reasonable answers would produce meaningfully different software.
If they would, ask the minimum question necessary to resolve the architectural ambiguity unless existing ADRs, the specification, tests, or repository context already answer it.
If they would not, choose the conventional answer, state a material assumption when useful, and continue.
Do not invent rationale when the repository does not establish it.
Architectural Principles
- Bash 4.3+ is the minimum runtime.
src/mktext.bashis the maintained implementation.dist/mktext.dev.bashis the generated flavor retaining maintained comments.dist/mktext.bashis the conventional generated flavor with full-line maintained comments removed.dist/mktext.min.bashis generated by minifyingdist/mktext.bashwith the pinned Bash-Minifier dependency.- Each generated Bash artifact has a corresponding
.sha256SHA-256 checksum file. - Build metadata is injected at build time and does not add runtime Git access.
make buildis network-free and consumes an already-preparedvendor/bash-minifier.bash; it does not synchronize or verify dependencies.make allexplicitly synchronizes dependencies before invokingmake build.- Make directly bootstraps only the pinned released
vendor/bashdeps.bashused by dependency management. - Ordinary externally acquired repository artifacts are declared in
dependencies.txtand synchronized by bashdeps. - The manifest currently manages the commit-pinned Bash-Minifier used by
make build, the Bash Doxygen filter used bymake docs, and the pinnedadrctlrelease used bymake adr-indexandmake docs. make docsconsumes prepared dependency state and must not synchronize or repair dependencies.doc/adr/README.mdis generated, ignored documentation input; its maintained framing is stored separately from the adrctl-generated linked ADR list.- One public
mktextfunction dispatches context, rendering, help, and version forms. - Callers own Bash associative-array contexts.
- Keys are normalized to uppercase and follow the documented ASCII grammar.
- Rendering is lexical, literal, single-pass, and nonrecursive.
- Templates and values are never evaluated as shell code.
- Rendering reads standard input and writes standard output.
- Exact line termination is part of the public behavior.
- No implicit external state is acquired by ordinary rendering operations.
- Keep the core intentionally small.
Technology Stack
Runtime:
- Bash 4.3+
- Bash builtins and language features
Development:
- Make
- Bats
- ShellCheck
- shfmt
- bashdeps for exact external build/development artifacts
- Bash-Minifier for the generated minified release flavor
- bash-doxygen for Bash reference extraction
- adrctl for generated linked ADR navigation
- Doxygen-compatible source documentation
- GitHub Actions
Coding Guidelines
Prefer small, readable Bash functions with explicit responsibilities.
Avoid eval categorically in rendering or context handling.
Do not source template, context, or dependency-manifest data.
Do not use command substitution, parameter expansion, or shell parsing to interpret template content.
Quote expansions deliberately. Preserve arbitrary context values literally.
Validate a context name and its associative-array type before creating a nameref.
Do not add external runtime commands when Bash builtins can implement the required behavior clearly and safely.
Private helpers and metadata variables use the reserved __mktext_ namespace.
Do not expand the public namespace without an architectural decision.
Build and Release Boundaries
Treat src/mktext.bash as the single maintained implementation. Do not edit
files under dist/ directly.
The build pipeline deliberately creates three representations of the same release:
src/mktext.bash
-> dist/mktext.dev.bash
-> dist/mktext.bash
-> dist/mktext.min.bash
The development flavor retains complete maintained comments. The conventional flavor removes full-line maintained comments according to the documented build contract. The minified flavor is derived from the conventional flavor with the manifest-managed Bash-Minifier. Do not make minified output a maintained source of truth.
make build SHALL remain network-free and SHALL NOT invoke make deps,
bashdeps sync, or bashdeps verify. It requires an already-prepared regular
vendor/bash-minifier.bash and fails with guidance when that dependency is absent.
This separation keeps network and repository mutation out of a plain build.
make all is the fresh-checkout convenience path. It SHALL run dependency
synchronization first and invoke make build only after synchronization succeeds;
keep this ordering explicit under parallel Make.
External build/development inputs are prepared through:
make deps
which may bootstrap the pinned released vendor/bashdeps.bash and synchronize the
committed dependencies.txt manifest. Make directly owns only that one bootstrap
artifact. The bootstrap is pinned by immutable release URL and SHA-256 digest and
is deliberately excluded from its own consumer manifest.
make deps-check
verifies the already-present bootstrap and manifest-managed dependency state
without network access or repair. Do not make deps-check depend on the bootstrap
file target, because that would silently turn verification into acquisition.
The current manifest contains vendor/bash-minifier.bash for builds,
vendor/doxygen-bash.awk for Bash reference documentation, and
vendor/adrctl.bash for generated ADR navigation. Bash-Minifier remains
commit-pinned; the documentation tools remain release-pinned as recorded in the
manifest, and all are authorized by committed SHA-256 digests. Do not reintroduce
project-specific download policy for manifest-managed artifacts.
make docs SHALL remain network-free and non-repairing after dependency
preparation. It consumes the prepared Bash Doxygen filter and adrctl artifact,
generates doc/adr/README.md atomically from maintained framing plus the current
ADR corpus, and then invokes Doxygen. Use make deps explicitly before make docs when prepared documentation state is absent. Routine documentation
generation must not add an ADR relationship graph.
Treat dependencies.txt as reviewed project source and vendor/ as ignored,
generated dependency state. Digest equality, not the destination filename,
defines acceptable external bytes. Download candidates must be verified before
publication, and acquisition failure must not replace an existing file with
unverified bytes.
Release versions come from the semantic-version workflow and are passed explicitly to Make. Development builds use the documented development version. Commit metadata and the build date are derived from the source revision when Git metadata is available.
The release workflow SHALL prepare and verify dependencies before its release build, validate all three generated flavors and checksum files, and publish these six assets:
mktext.dev.bash
mktext.dev.bash.sha256
mktext.bash
mktext.bash.sha256
mktext.min.bash
mktext.min.bash.sha256
New releases publish only .sha256 checksum companions. Historical .256
companions remain valid for releases that already contain them. A consumer that
explicitly retrieves checksum sidecars may fall back from .sha256 to .256
only when the preferred asset is confirmed absent. Transport, authorization,
server, malformed-content, and checksum-verification failures remain failures and
must not trigger legacy fallback.
This sidecar compatibility rule does not alter the project's trust model. The
Make-owned bashdeps bootstrap and manifest-managed dependencies remain authorized
by SHA-256 digests committed in repository source; a live .sha256 or .256
sidecar is not a replacement for committed trust data.
All released Bash artifacts must remain independent of bashdeps,
dependencies.txt, Bash-Minifier, bash-doxygen, adrctl, Doxygen, and vendor/ at
runtime.
Scope Discipline
Unless explicitly requested otherwise, produce the smallest correct change that satisfies the requested behavior and the existing architecture.
Do not expand the project into a general template language.
Features such as filters, expressions, loops, conditionals, includes, slugification, case conversion, numeric padding, date generation, UUIDs, Git queries, environment acquisition, and plugin systems belong outside the core unless a later ADR deliberately changes that boundary.
Do not perform unrelated refactoring, formatting, renaming, or documentation changes in a focused patch.
If additional improvement opportunities are discovered, report or record them separately rather than silently broadening the change.
Documentation-only requests must preserve executable behavior exactly.
Documentation Standards
Follow the documentation-driven philosophy established by the ADRs.
Documentation should explain intent, assumptions, constraints, safety posture, observable behavior, and non-goals where appropriate.
Source-code documentation follows ADR-011. Documentation generation and external
dependency lifecycle follow ADR-015, ADR-016, ADR-017, and ADR-019. Checksum
companion naming and compatibility follow ADR-018. doc/decisions.md is the
concise architectural map; full ADRs remain authoritative for their reasoning and
supersession history. When a documentation-only source change is requested,
preserve executable lines verbatim and verify that only comments changed.
When intent cannot be established confidently, expose the ambiguity rather than writing plausible-sounding rationale.
Testing
The project follows documentation-driven, test-second development.
Documentation establishes intent. Implementation realizes it. Automated tests then verify observable behavior.
Use Bats for the primary behavior suite and for repository build/dependency boundary regression coverage.
Tests should exercise the public mktext function rather than private helper
structure whenever practical.
Run the behavior suite against maintained src/mktext.bash and all three generated
Bash artifacts. Every generated artifact is a product artifact and must not be
assumed correct merely because another representation passed tests.
Generated-artifact validation SHALL also verify executable mode, interpreter
placement, direct execution under each supported artifact basename, identical
embedded version/build metadata, Bash 4.3 compatibility, and the corresponding
.sha256 checksum file.
Build/dependency tests should verify observable Make contracts rather than private bashdeps, Bash-Minifier, or adrctl internals. Protect at least these boundaries:
- a plain clean-checkout
make builddoes not acquire dependencies and fails when the minifier is absent; make buildsucceeds from prepared minifier state without using the network or dependency manifest;make allsynchronizes dependencies before building;deps-checkremains offline and non-repairing;make docsfails rather than acquiring dependencies when required prepared documentation state is absent;- stale/tampered managed dependency bytes are detected and converged by the proper target;
- generated ADR navigation and Doxygen output remain ignored build state; and
- all literal generated consumer artifacts remain functional after the dependency tree and manifest are removed.
Every functional change should prompt these questions:
- What observable behavior changed?
- Which documented contract governs that behavior?
- How can the behavior be verified automatically?
- Does the change affect final-newline, quoting, literal-value, usage, version, direct-execution, artifact, checksum, or return-status semantics?
Bug fixes should add or update a regression test that would have failed before the fix.
A behavioral change is normally incomplete when corresponding documentation or tests are missing.
Validation
When practical:
- review the resulting diff;
- run the relevant Make targets;
- run Bash syntax validation;
- run Bats tests against source and all generated artifacts;
- verify all three
.sha256files; - run the build/dependency boundary tests;
- run ShellCheck and shfmt checks on maintained source;
- use
make depsandmake deps-checkwhen build/documentation dependency state is in scope; - generate or validate documentation when documentation inputs change;
- verify a clean
make builddoes not download or repair dependencies; - verify
make allprepares dependencies before building; - verify
make docsdoes not download or repair dependencies; - verify the generated ADR landing page and Doxygen output are ignored state;
- verify generated artifact metadata when build behavior changes;
- verify documentation-only source changes did not alter executable lines.
If a validation tool is unavailable, do not invent its result. Report only what was actually verified.
Common Failure Modes
Avoid:
- editing generated
dist/files as though they were maintained source; - treating generated
doc/adr/README.mdas maintained documentation source; - releasing artifact flavors carrying stale or mismatched version metadata;
- generating
mktext.min.bashdirectly from maintained source instead of from the conventional stripped artifact; - allowing plain
make buildto acquire or repair dependencies; - allowing
make docsto acquire or repair dependencies; - making
make deps-checkbootstrap, download, or repair dependency state; - placing
vendor/bashdeps.bashindependencies.txtand creating a bootstrap cycle; - reintroducing direct Makefile acquisition for manifest-managed dependencies;
- using moving dependency URLs when an immutable release, tag, or commit is available;
- trusting a dependency filename or version label instead of the committed digest;
- adding automatic ADR relationship-graph generation to the routine docs path;
- failing to test all published artifact flavors and checksum files;
- adding transformations because they appear convenient;
- using
evalor shell expansion for substitution; - rescanning replacement values and accidentally making rendering recursive;
- deleting or normalizing unknown macro text;
- buffering the entire template and losing trailing-newline fidelity;
- accepting malformed context names before creating namerefs;
- treating
-and_as equivalent keys; - writing diagnostics to standard output;
- returning success for invalid API usage;
- calling
exitfrom ordinary library error paths; - testing private implementation details as though they were public contracts;
- inventing architectural rationale;
- silently expanding scope.
Final Principle
mktext knows how to replace names with values and almost nothing else.
Every change should preserve that clarity and leave the repository easier for the next contributor to understand.
Shared Coding Standards
This repository adopts the complete pinned coding_standards@v1.0.9 snapshot under doc/standards/; .codingstandardrc records its verified release digest. Applicable imported standards are governing requirements unless an accepted local ADR or explicit policy refines them. Presence does not imply applicability, examples remain illustrative, imported standards are not edited locally, and upgrades are reviewed repository changes rather than automatic synchronization.
Authoritative shared documentation standards used here:
- Bash:
doc/standards/bash/documentation-standard.md
