Skip to content

Commit a43496d

Browse files
committed
docs: add formal LaTeX ADR and update test harness
- Introduce `docs/adr-tex-2/`: A comprehensive Architecture Decision Record implemented in LaTeX. - Document core architectural principles: "Zero-Database," "Empty Tree" commits, and the `@git-stunts/*` module decomposition. - Add TikZ figures for System Context, Component Decomposition, Runtime Sequences, and Deployment Topologies. - Add `Makefile` for PDF compilation and update `.gitignore` for LaTeX build artifacts. - Update `test/setup.bats`: Adjust assertions to match specific prompt text ("Would you like me to clone it now") and enable debug logging for the setup script test.
1 parent 7e1ee2f commit a43496d

26 files changed

Lines changed: 789 additions & 1 deletion

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ cms-chunks-*/
77
cms-upload-*/
88
git-cms-test-*/
99
.obsidian/
10+
11+
# LaTeX artifacts
12+
*.aux
13+
*.log
14+
*.out
15+
*.toc

docs/adr-tex-2/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
all: main.pdf
2+
3+
main.pdf: main.tex $(wildcard sections/*.tex) $(wildcard figures/*.tex)
4+
pdflatex -interaction=nonstopmode main.tex
5+
pdflatex -interaction=nonstopmode main.tex
6+
7+
clean:
8+
rm -f *.aux *.log *.out *.toc *.pdf

docs/adr-tex-2/figures/context.tex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
\begin{tikzpicture}[node distance=2cm, auto]
2+
\node [block] (Author) {Author\\(Human)};
3+
\node [block, below=1cm of Author] (GitCMS) {\textbf{git-cms}\\(Node.js App)};
4+
\node [block, right=2cm of GitCMS] (Stargate) {git-stargate\\(Git Gateway)};
5+
\node [block, below=1cm of GitCMS] (LocalRepo) {.git/objects/\\(Local Repository)};
6+
\node [block, right=2cm of Stargate] (PublicMirror) {Public Mirror\\(GitHub/GitLab)};
7+
8+
\draw [line] (Author) -- node [align=center, scale=0.8] {CLI/HTTP API} (GitCMS);
9+
\draw [line] (GitCMS) -- node [scale=0.8] {git push} (Stargate);
10+
\draw [line] (GitCMS) -- node [scale=0.8] {read/write} (LocalRepo);
11+
\draw [line] (Stargate) -- node [scale=0.8] {mirror} (PublicMirror);
12+
\end{tikzpicture}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
\begin{tikzpicture}[node distance=1.5cm, auto]
2+
\node [blockshaded] (CMS) {\textbf{CmsService}\\\texttt{src/lib}};
3+
\node [block, above left=1cm and 0.5cm of CMS] (CLI) {CLI\\\texttt{bin/git-cms.js}};
4+
\node [block, above right=1cm and 0.5cm of CMS] (HTTP) {HTTP Server\\\texttt{src/server}};
5+
\node [draw, dashed, thick, inner sep=10pt, fit=(CLI) (HTTP) (CMS)] (AppLayer) {};
6+
\node [anchor=south] at (AppLayer.north) {\small\textbf{Application Layer}};
7+
8+
\node [block, below=1.5cm of CMS] (Graph) {Graph\\empty-graph};
9+
\node [block, left=0.5cm of Graph] (Codec) {Codec\\trailer-codec};
10+
\node [block, right=0.5cm of Graph] (CAS) {CAS\\cas};
11+
\node [block, below=1cm of Graph] (Plumbing) {Plumbing\\git-protocol};
12+
\node [block, right=0.5cm of CAS] (Vault) {Vault\\secrets};
13+
\node [draw, dashed, thick, inner sep=10pt, fit=(Plumbing) (Codec) (Graph) (CAS) (Vault)] (LegoLayer) {};
14+
\node [anchor=north] at (LegoLayer.south) {\small\textbf{Lego Blocks (@git-stunts)}};
15+
16+
\draw [line] (CLI) -- (CMS); \draw [line] (HTTP) -- (CMS);
17+
\draw [line] (CMS) -- (Codec); \draw [line] (CMS) -- (Graph);
18+
\draw [line] (CMS) -- (CAS); \draw [line] (CMS) -- (Vault);
19+
\draw [line] (CMS) -- (Plumbing); \draw [line] (Graph) -- (Plumbing);
20+
\draw [line] (CAS) -- (Plumbing);
21+
\end{tikzpicture}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
\begin{tikzpicture}[node distance=3cm, auto]
2+
\node (Author) {Author};
3+
\node [right=of Author] (CLI) {CLI};
4+
\node [right=of CLI] (CMS) {Service};
5+
\node [right=of CMS] (PL) {Plumbing};
6+
7+
\foreach \n in {Author, CLI, CMS, PL} {
8+
\draw [dashed] (\n) -- ++(0,-6.5);
9+
}
10+
11+
\draw [->] ($(Author)+(0,-1)$) -- node [scale=0.7] {draft hello-world} ($(CLI)+(0,-1)$);
12+
\draw [->] ($(CLI)+(0,-1.5)$) -- node [scale=0.7] {saveSnapshot()} ($(CMS)+(0,-1.5)$);
13+
\draw [->] ($(CMS)+(0,-2.2)$) -- node [scale=0.7] {revParse(ref)} ($(PL)+(0,-2.2)$);
14+
\draw [<-] ($(CMS)+(0,-3)$) -- node [scale=0.7] {null} ($(PL)+(0,-3)$);
15+
\draw [->] ($(CMS)+(0,-4)$) -- node [scale=0.7] {createNode()} ($(CMS)+(0.8,-4.2)$);
16+
\draw [->] ($(CMS)+(0,-5)$) -- node [scale=0.7] {updateRef()} ($(PL)+(0,-5)$);
17+
\draw [<-] ($(CLI)+(0,-6)$) -- node [scale=0.7] {OK} ($(CMS)+(0,-6)$);
18+
\end{tikzpicture}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
\begin{tikzpicture}[node distance=1cm, auto, font=\small]
2+
\node [blockshaded, text width=2.5cm, minimum height=8cm] (CMS) {\textbf{CmsService}};
3+
\node [block, text width=4.5cm, right=3cm of CMS.north, anchor=north] (PL) {\textbf{@git-stunts/plumbing}\\execute, revParse};
4+
\node [block, text width=4.5cm, below=0.5cm of PL] (TC) {\textbf{@git-stunts/trailer-codec}\\encode, decode};
5+
\node [block, text width=4.5cm, below=0.5cm of TC] (EG) {\textbf{@git-stunts/empty-graph}\\createNode, readNode};
6+
\node [block, text width=4.5cm, below=0.5cm of EG] (CAS) {\textbf{@git-stunts/cas}\\storeFile, retrieveFile};
7+
\node [block, text width=4.5cm, below=0.5cm of CAS] (V) {\textbf{@git-stunts/vault}\\resolveSecret};
8+
9+
\draw [line] (CMS.east |- PL.west) -- (PL.west);
10+
\draw [line] (CMS.east |- TC.west) -- (TC.west);
11+
\draw [line] (CMS.east |- EG.west) -- (EG.west);
12+
\draw [line] (CMS.east |- CAS.west) -- (CAS.west);
13+
\draw [line] (CMS.east |- V.west) -- (V.west);
14+
15+
\draw [dashed-line] (EG.east) -- ++(0.5,0) |- (PL.east);
16+
\draw [dashed-line] (CAS.east) -- ++(0.5,0) |- (PL.east);
17+
\end{tikzpicture}

docs/adr-tex-2/main.pdf

333 KB
Binary file not shown.

docs/adr-tex-2/main.tex

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
\documentclass[11pt,a4paper]{article}
2+
3+
% --- Packages ---
4+
\usepackage[utf8]{inputenc}
5+
\usepackage[T1]{fontenc}
6+
\usepackage{lmodern}
7+
\usepackage[margin=1in, headheight=14pt]{geometry}
8+
\usepackage{titlesec}
9+
\usepackage{titletoc}
10+
\usepackage{fancyhdr}
11+
\usepackage{graphicx}
12+
\usepackage{booktabs}
13+
\usepackage{longtable}
14+
\usepackage{array}
15+
\usepackage{enumitem}
16+
\usepackage{float}
17+
\usepackage{listings}
18+
\usepackage{xcolor}
19+
\usepackage{tikz}
20+
\usetikzlibrary{shapes, arrows.meta, positioning, fit, backgrounds, calc, shadows, trees}
21+
\usepackage{amssymb}
22+
\usepackage{xurl}
23+
\usepackage[hidelinks]{hyperref}
24+
25+
% --- Typography & Layout ---
26+
\hypersetup{
27+
colorlinks=false,
28+
pdftitle={Architecture Decision Record: Git CMS},
29+
pdfauthor={James Ross}
30+
}
31+
32+
\usepackage{parskip}
33+
\setlength{\parindent}{0pt}
34+
\setlength{\parskip}{0.8em}
35+
36+
\pagestyle{fancy}
37+
\fancyhf{}
38+
\fancyhead[L]{\nouppercase{\leftmark}}
39+
\fancyhead[R]{Git CMS ADR}
40+
\fancyfoot[C]{\thepage}
41+
42+
\titleformat{\section}{\Large\bfseries\sffamily}{\thesection}{1em}{}
43+
\titleformat{\subsection}{\large\bfseries\sffamily}{\thesubsection}{1em}{}
44+
\titleformat{\subsubsection}{\bfseries\sffamily}{\thesubsubsection}{1em}{}
45+
46+
\lstset{
47+
basicstyle=\ttfamily\small,
48+
breaklines=true,
49+
frame=single,
50+
numbers=left,
51+
numberstyle=\tiny\color{gray},
52+
captionpos=b,
53+
keepspaces=true,
54+
showstringspaces=false,
55+
keywordstyle=\bfseries,
56+
commentstyle=\itshape,
57+
stringstyle=,
58+
}
59+
60+
% --- Textbook B&W TikZ Styles ---
61+
\tikzset{
62+
base/.style={draw=black, thick, font=\sffamily\small, align=center, inner sep=8pt},
63+
block/.style={base, rectangle, rounded corners=2pt, fill=white},
64+
blockshaded/.style={base, rectangle, rounded corners=2pt, fill=gray!10},
65+
line/.style={draw=black, thick, -Latex},
66+
dashed-line/.style={draw=black, thick, dashed, -Latex}
67+
}
68+
69+
\begin{document}
70+
71+
\input{meta}
72+
73+
\begin{titlepage}
74+
\centering
75+
\vspace*{3cm}
76+
{\fontsize{30}{36}\selectfont \textbf{Architecture Decision Record}\\[0.5em]}
77+
{\fontsize{20}{24}\selectfont \textit{Git CMS}\\[1.5cm]}
78+
79+
\rule{\textwidth}{1pt}\\[0.5cm]
80+
{\Large Database-Free Content Management via Git Plumbing}\\[3cm]
81+
82+
\textbf{Author:} James Ross \\
83+
\textbf{Version:} 1.0.0 \\
84+
\textbf{Date:} 2026-01-11
85+
86+
\vfill
87+
\textit{Prepared for Engineering Review}
88+
\vspace{2cm}
89+
\end{titlepage}
90+
91+
\tableofcontents
92+
\newpage
93+
94+
\input{sections/01-introduction}
95+
\input{sections/02-constraints}
96+
\input{sections/03-context}
97+
\input{sections/04-solution}
98+
\input{sections/05-building-blocks}
99+
\input{sections/06-runtime}
100+
\input{sections/07-deployment}
101+
\input{sections/08-crosscutting}
102+
\input{sections/09-decisions}
103+
\input{sections/10-quality}
104+
\input{sections/11-risks}
105+
\input{sections/12-glossary}
106+
107+
\appendix
108+
\input{sections/A-commands}
109+
\input{sections/B-structure}
110+
\input{sections/C-related}
111+
\input{sections/D-references}
112+
113+
\end{document}

docs/adr-tex-2/meta.tex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
% meta.tex
2+
\title{\textbf{Architecture Decision Record: Git CMS}\\ \large Database-Free Content Management via Git Plumbing}
3+
\author{James Ross}
4+
\date{Version 1.0.0 -- Last Updated: 2026-01-11}
5+
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
\section{Introduction \& Goals}
2+
3+
\subsection{Project Overview}
4+
5+
\textbf{git-cms} is a serverless, database-free Content Management System that treats Git's object store as a distributed, cryptographically verifiable document database. Instead of storing content in traditional databases (SQL or NoSQL), it leverages Git's Merkle DAG to create an append-only ledger for articles, metadata, and encrypted assets.
6+
7+
The fundamental innovation: \texttt{git push} becomes the API endpoint.
8+
9+
\subsection{Fundamental Requirements}
10+
11+
\subsubsection{FR-1: Zero-Database Architecture}
12+
The system MUST NOT depend on external database systems (SQL, NoSQL, or key-value stores). All persistent state resides within Git's native object store (\texttt{.git/objects}).
13+
14+
\textbf{Rationale:} Eliminates operational complexity, deployment dependencies, and schema migration challenges inherent to traditional database-backed CMSs.
15+
16+
\subsubsection{FR-2: Cryptographic Verifiability}
17+
Every content mutation MUST be recorded as a Git commit with cryptographic integrity guarantees via SHA-1 hashing (with optional GPG signing for non-repudiation).
18+
19+
\textbf{Rationale:} Provides immutable audit trails and tamper detection without additional infrastructure.
20+
21+
\subsubsection{FR-3: Fast-Forward Only Publishing}
22+
The publish operation MUST enforce strict linear history (fast-forward only) to prevent rewriting published content.
23+
24+
\textbf{Rationale:} Guarantees provenance and prevents content manipulation after publication.
25+
26+
\subsubsection{FR-4: Client-Side Encryption}
27+
All uploaded assets MUST be encrypted client-side (AES-256-GCM) before touching the repository.
28+
29+
\textbf{Rationale:} Achieves row-level security without database-level access controls. The Git gateway receives only opaque encrypted blobs.
30+
31+
\subsubsection{FR-5: Infinite Point-in-Time Recovery}
32+
Users MUST be able to access any historical version of any article without data loss.
33+
34+
\textbf{Rationale:} Git's DAG structure provides this naturally; the CMS simply exposes it as a first-class feature.
35+
36+
\subsection{Quality Goals}
37+
38+
\begin{table}[H]
39+
\centering
40+
\small
41+
\begin{tabular}{llp{5cm}p{4cm}}
42+
\toprule
43+
\textbf{Prio} & \textbf{Attribute} & \textbf{Description} & \textbf{Measurement} \\
44+
\midrule
45+
1 & Security & Cryptographic integrity, signed commits & GPG verification, AES-256 strength \\
46+
2 & Simplicity & Minimal dependencies, composable architecture & Lines of code, dependency count \\
47+
3 & Auditability & Complete provenance of all content changes & Git log completeness \\
48+
4 & Performance & Sub-second reads for blog workloads & Response time for \texttt{readArticle()} \\
49+
5 & Portability & Multi-runtime support (Node, Bun, Deno) & Test suite pass rate \\
50+
\bottomrule
51+
\end{tabular}
52+
\caption{Quality goals and their measurements.}
53+
\end{table}
54+
55+
\subsection{Non-Goals}
56+
57+
This system is \textbf{intentionally NOT designed for}:
58+
59+
\begin{itemize}[noitemsep]
60+
\item \textbf{High-velocity writes:} Content publishing happens in minutes/hours, not milliseconds.
61+
\item \textbf{Complex queries:} No SQL-like JOINs or aggregations. Queries are limited to ref enumeration and commit message parsing.
62+
\item \textbf{Large-scale collaboration:} Designed for single-author or small-team blogs.
63+
\item \textbf{Real-time updates:} Publishing is atomic but not instantaneous.
64+
\end{itemize}

0 commit comments

Comments
 (0)