Skip to content

Commit e5476bf

Browse files
committed
docs(plans): add shipping-audit.md — thorough plans-vs-shipped walk
Comprehensive audit doc capturing the state of every plan, design spec, and doc in the repo. Four categories: 1. **Shipped** — every W-item from the migration plan, every deferred item from feature-gap-analysis, every cleanup deferral. With release-version cross-refs. 2. **Pending user action** (NOT deferrals; the code is ready, the human isn't): a. PyPI trusted-publisher config — exact PyPI form values, GitHub `pypi` Environment setup, plus the retroactive `gh run rerun --failed` recipe for the 10+ pending uploads (v0.11.0 through v0.18.0). b. Branch protection on `main` — exact named status checks to require. 3. **Permanent / out-of-charter** — 16 documented skips with rationale, kept here so future maintainers don't relitigate: - NM dns=none real-link-event check (can't be containerised) - 15 ubuntu-source skip items (composer, node, host php, supervisor, dotfile aliases, etc.) 4. **Demand-driven future** — cross-reference to future-additions.md. The audit's job is to leave no documented intent silently orphaned. Items either: shipped (with release-notes entry), blocked on user (section G), explicitly rejected with reason (section F), or parked for demand (future-additions.md). CHANGELOG entry under [Unreleased] alongside the future-additions.md entry from yesterday — both bundle with the next feature release rather than getting their own tags. Linked from docs/README.md Development section.
1 parent 489a953 commit e5476bf

3 files changed

Lines changed: 336 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,23 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1414
current user demand. Documents the bar to graduate an item
1515
("someone is asking for it" — not "it would be nice to
1616
have"). Also captures the **rejected** `--on-host` for
17-
`stack lemp` with the reasoning. Linked from docs/README.md
18-
Development section.
17+
`stack lemp` with the reasoning.
18+
- `docs/plans/shipping-audit.md` — comprehensive shipped-vs-
19+
pending walk through every plan / design spec / doc in the
20+
repo. Migration W1-W9 (all shipped), feature-gap-analysis
21+
defer list (all shipped + 1 rejected with rationale),
22+
validation-report TODOs (all closed out), cleanup-2026-05-14
23+
deferrals (both shipped in v0.10 / v0.12), shipping-checklist
24+
phases (all in-code items shipped). Captures the two **user-
25+
side actions** that aren't deferrals: PyPI trusted-publisher
26+
config + branch protection on `main`. Includes step-by-step
27+
for each plus the retroactive PyPI re-upload recipe for the
28+
10+ pending tags (v0.11.0 through v0.18.0). Documents the 16
29+
permanent skips with rationale (NM real-link-event check +
30+
the 15 ubuntu-source Skip items). Concludes "the plans tree
31+
is drained — no item is silently orphaned."
32+
33+
Both files linked from docs/README.md Development section.
1934

2035
## [0.18.0] — 2026-05-16
2136

docs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ Top-level utilities (not tools):
9797
extensible surface (more TLS DNS-01 providers, more framework
9898
recipes, more db engines) with concrete patterns but no current
9999
user demand. Move items out when someone asks.
100+
- **[Shipping audit (2026-05-16)](plans/shipping-audit.md)**
101+
comprehensive shipped-vs-pending walk through every plan, design
102+
spec, and doc. Captures the two **user-side actions** still
103+
outstanding (PyPI trusted-publisher + branch protection),
104+
documents permanent skips, and cross-references everything
105+
else.
100106

101107
## Release notes
102108

docs/plans/shipping-audit.md

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
# Shipping audit — 2026-05-16
2+
3+
A comprehensive walk through every plan, design spec, and doc in
4+
the repo. For each item: **what shipped**, **what's pending user
5+
action**, **what's a permanent skip**, and **what's deferred to
6+
demand-driven future work**.
7+
8+
The audit's job is to leave no item in any document silently
9+
orphaned. If a plan said "we'll do X in v0.6+", this doc says
10+
either "X shipped in v0.7.0" or "X is parked in
11+
[future-additions.md](future-additions.md) until someone asks."
12+
13+
Cross-references:
14+
15+
- [`future-additions.md`](future-additions.md) — items captured
16+
with concrete patterns but no current demand.
17+
- [`known-issues.md`](known-issues.md) — runtime checks that
18+
can't be containerised.
19+
- [`../shipping-checklist.md`](../shipping-checklist.md) — the
20+
release-readiness operational checklist (different from this
21+
audit; that file tracks "ship readiness", this file tracks
22+
"feature completeness against plans").
23+
- [`../../.design/plans/feature-gap-analysis.md`](../../.design/plans/feature-gap-analysis.md)
24+
— the original ubuntu/→shimkit Adopt/Skip/Improve/Defer
25+
matrix.
26+
- [`../../.design/plans/validation-report.md`](../../.design/plans/validation-report.md)
27+
— v0.5.0 migration closeout report with its own follow-up
28+
list.
29+
30+
---
31+
32+
## A. Migration plan W1-W9 (ubuntu → shimkit, target v0.5.0)
33+
34+
Source: [`.design/plans/migration-plan.md`](../../.design/plans/migration-plan.md).
35+
36+
| W# | Deliverable | Shipped in | Notes |
37+
|----|-------------|------------|-------|
38+
| W1 | `core/version` — tool-version constraints | v0.5.0 | 5 detectors at v0.5; grew to 8 (+ php v0.7, openssl v0.8). |
39+
| W2 | `core/docker.DockerEnv` chokepoint | v0.5.0 | Gained `run_oneshot()` in v0.8.0 + `command=` plumbing for Redis in v0.15.0. |
40+
| W3 | `shimkit db` (5 engines) | v0.5.0 | 6 engines after redis (v0.15). + `--on-host` for SQL engines (v0.9). |
41+
| W4 | `shimkit web nginx vhost` | v0.5.0 ||
42+
| W5 | `shimkit stack lemp` | v0.5.0 | + LEMP-backing-DB validation when redis joined registry (v0.15). |
43+
| W6 | `shimkit shell colors` | v0.5.0 ||
44+
| W7 | docker-clean + gpg version preflight | v0.5.0 ||
45+
| W8 | Docs catchup | v0.5.0 ||
46+
| W9 | Archive + delete ubuntu/ source | v0.5.0 | SHA-256 recorded in validation-report.md. |
47+
48+
**Status: 9/9 complete.**
49+
50+
---
51+
52+
## B. Feature-gap-analysis Defer list (post-v0.5.0)
53+
54+
Source: [`.design/plans/feature-gap-analysis.md`](../../.design/plans/feature-gap-analysis.md)
55+
"Decisions deferred to v0.5.x+".
56+
57+
| Item | Shipped in | Notes |
58+
|------|------------|-------|
59+
| `shimkit cron add/list/remove` (generic) | v0.6.0 | Replaced the Laravel-specific `add:cron.sh` source. |
60+
| `shimkit framework laravel` | v0.7.0 / v0.7.1 | v0.7.1 was a version-drift recovery; surface unchanged. |
61+
| `shimkit tls / certbot` | v0.8.0 (webroot) | DNS-01 added in v0.13.0 (Cloudflare) + v0.17.0 (Route53). |
62+
| `--on-host` mode for db | v0.9.0 | mysql / mariadb / postgres only. mongo + phpmyadmin intentionally not supported (mongo's host packaging is messy; phpmyadmin has no host install). |
63+
| `--on-host` mode for stack | **rejected** | Captured in [`future-additions.md`](future-additions.md) with rationale: stack lemp is intrinsically multi-container; the original ubuntu host-LEMP scripts had 5 Critical security flags. |
64+
65+
**Status: 4/4 deferred items shipped; 1/1 rejected with documented reasoning.**
66+
67+
---
68+
69+
## C. Validation-report follow-up TODOs (post-v0.5.0)
70+
71+
Source: [`.design/plans/validation-report.md`](../../.design/plans/validation-report.md).
72+
Original list rewritten as a closeout in v0.12.0.
73+
74+
| TODO | Shipped in |
75+
|------|------------|
76+
| Coverage push toward 85% | v0.10.0 (74% → 85%, +397 tests) |
77+
| All four Defer-list items | covered in B above |
78+
79+
**Status: closeout complete.**
80+
81+
---
82+
83+
## D. Cleanup-2026-05-14 deferred items
84+
85+
Source: [`docs/plans/cleanup-2026-05-14.md`](cleanup-2026-05-14.md).
86+
87+
| Item | Shipped in |
88+
|------|------------|
89+
| P2.3 — coverage push to 85% | v0.10.0 |
90+
| P3.2 — `gh attestation verify` smoke | v0.12.0 |
91+
92+
**Status: 2/2 complete.** Manual-only NM check (P1.5 / Phase 7 item 7) stays in [`known-issues.md`](known-issues.md) by design — see section F.
93+
94+
---
95+
96+
## E. Shipping-checklist phases
97+
98+
Source: [`../shipping-checklist.md`](../shipping-checklist.md).
99+
100+
| Phase | Item | Status |
101+
|-------|------|--------|
102+
| 1.1-1.7 | Code/repo setup | ✅ done |
103+
| **1.8** | **Branch protection on `main`** | **⏳ pending — user action; see section G** |
104+
| 2.1-2.3 | GitHub remote setup | ✅ done |
105+
| **2.4** | **`pypi` GitHub Environment** | **⏳ pending — user action; see section G** |
106+
| 3 | Homebrew tap | 🗑 abandoned — see [`../shipping-checklist.md`](../shipping-checklist.md) Phase 3 |
107+
| **4.1** | **PyPI account + 2FA** | **⏳ pending — user action; see section G** |
108+
| **4.2** | **Configure PyPI trusted publisher** | **⏳ pending — user action; see section G** |
109+
| 4.3 | TestPyPI (dry-run) | ⏸ optional |
110+
| 5 | First PyPI upload | ▶ auto (blocked on 2.4 + 4.1 + 4.2) |
111+
| 6 | Post-release / ongoing | ▶ auto |
112+
| 7.1 | Coverage upload | ✅ done (v0.12.0, codecov.io) |
113+
| 7.2 | Per-tool docs | ✅ done |
114+
| 7.3 | mkdocs site | ⏸ optional |
115+
| 7.4 | CITATION.cff | ⏸ optional |
116+
| 7.5 | gh attestation verify smoke | ✅ done (v0.12.0) |
117+
118+
**Status: 4 user-side actions remain; everything in scope for code is shipped.**
119+
120+
---
121+
122+
## F. Permanent / out-of-charter (won't ship)
123+
124+
Documented decisions to **never** build, with rationale. Each has a
125+
durable home in the repo:
126+
127+
### NM `dns=none` real-link-event check
128+
129+
Source: [`docs/plans/known-issues.md`](known-issues.md).
130+
131+
**Why permanent**: containers don't generate real network link
132+
events, so we cannot validate that NetworkManager respects the
133+
`dns=none` drop-in across a real interface state change inside CI.
134+
The property is **upstream NetworkManager behaviour**, not shimkit
135+
behaviour. NM has shipped `dns=none` since 2015 with their own
136+
test coverage. If NM regresses, every DNS-manager tool (pi-hole,
137+
AGH-CLI, Unbound's resolvconf integration) breaks together.
138+
139+
**Mitigation**: `shimkit doctor` surfaces NM service state, so a
140+
user investigating flaky DNS post-fix can confirm in one command
141+
whether NM is the active manager. Manual verification procedure
142+
documented in `known-issues.md`.
143+
144+
### Ubuntu Skip list (15 items)
145+
146+
Source: [`.design/plans/feature-gap-analysis.md`](../../.design/plans/feature-gap-analysis.md)
147+
**Skip** column.
148+
149+
| Item | Reason |
150+
|------|--------|
151+
| `install:composer.sh` | Ships in the LEMP php container; host install belongs to `brew` / `apt`. |
152+
| `install:node.sh` | `nvm` / `volta` / `asdf` are industry standard. |
153+
| `install:packages.sh` | Bulk-apt is the antithesis of shimkit's per-tool design. |
154+
| `install:php.sh` | Host PHP install is `shimkit stack lemp`'s job inside a container. |
155+
| `install:php7.sh` | Broken in source (literal `apt install -packages/extentions-`). |
156+
| `install:server-env.sh` | Near-duplicate of `install:nginx.sh`. |
157+
| `configurators/aliases/aliases` | Alias curation is per-user dotfile territory. |
158+
| `configs:supervisor.sh` | Supervisor is fading vs container restart policies / systemd. |
159+
| `database/create:mysql.sh` | `shimkit db mysql shell` + SQL covers this. |
160+
| `expressjs:setup.sh` | Too project-shaped (clones repo, writes systemd unit). |
161+
| `laravel:initialize.sh` | Too project-shaped — that's a `make` / Taskfile concern. |
162+
| Three legacy/dup trees (`__src/server-main/`, `server-main 2/`, `scripts/initializers/server-main/`) | Duplicates of paths shimkit doesn't need. |
163+
| `scripts/security/`, `docs/` (empty) | No content to migrate. |
164+
| `scripts/help.sh` | Replaced by `shimkit --help` / per-tool `--help`. |
165+
| `assets/bash-colors.sh` PS1 helpers | Dotfile territory; only the palette printer ported (v0.5.0 `shimkit shell colors`). |
166+
167+
**Status: 15/15 documented permanent skips.**
168+
169+
---
170+
171+
## G. User-side actions — pending, not deferrals
172+
173+
Two items in the repo state need the **user** to act (the
174+
maintainer, via the GitHub / PyPI web UIs). These are blocked on
175+
the human; shimkit's code is ready. They are **not deferrals**
176+
and they don't belong in `future-additions.md` — they belong
177+
here.
178+
179+
### G.1 — PyPI trusted-publisher config
180+
181+
**Why it matters**: Releases v0.11.0 through v0.18.0 all ran the
182+
restored `publish-pypi` job, and all of them failed at the
183+
`invalid-publisher` step because the user-side trusted-publisher
184+
config isn't done yet. The wheel + sdist are live on the GitHub
185+
Release page for every version; PyPI is empty.
186+
187+
**What needs to happen**:
188+
189+
1. **PyPI side** — log in to <https://pypi.org/manage/account/publishing/>
190+
and add a pending publisher with these EXACT values:
191+
192+
| Field | Value |
193+
|-------|-------|
194+
| PyPI Project Name | `shimkit` |
195+
| Owner | `simtabi` |
196+
| Repository name | `shimkit` |
197+
| Workflow filename | `release.yml` |
198+
| Environment name | `pypi` |
199+
200+
2. **GitHub side**`simtabi/shimkit` → Settings → Environments
201+
→ "New environment" named `pypi`. No secrets required; OIDC
202+
provides credentials at runtime. Optionally add a Required
203+
Reviewer rule if you want a human gate before each upload.
204+
205+
3. **Retroactive uploads** — once both above are done, every
206+
failed `publish-pypi` job from v0.11.0 onward can be re-run:
207+
208+
```
209+
gh run list --workflow=release.yml | grep failure
210+
# for each failed run id:
211+
gh run rerun <run-id> --failed
212+
```
213+
214+
The wheel + sdist artifacts are still attached to each
215+
release (the `build` + `github-release` jobs succeeded);
216+
re-running picks up the existing build artifacts and only
217+
retries the upload step.
218+
219+
**Effort**: ~10 minutes for the maintainer with PyPI write access.
220+
221+
### G.2 — Branch protection on `main`
222+
223+
**Why it matters**: shimkit's CI catches issues (test failures,
224+
lint, mypy strict), but nothing currently prevents a push to
225+
`main` that bypasses CI entirely. With branch protection,
226+
incoming PRs and direct pushes must pass the named status checks
227+
before landing.
228+
229+
**What needs to happen**:
230+
231+
`simtabi/shimkit` → Settings → Branches → "Add rule" with:
232+
233+
- Branch name pattern: `main`
234+
- ✓ Require status checks to pass before merging:
235+
- `test (macos-latest, 3.10)` through `test (ubuntu-latest, 3.13)` — the 8 matrix cells
236+
- `security`
237+
- `build`
238+
- `smoke (macos-latest)` + `smoke (ubuntu-latest)`
239+
- ✓ Require branches to be up to date before merging
240+
- (Optionally) ✓ Require a pull request before merging — flips the workflow to PR-based instead of direct-to-main
241+
242+
**Effort**: ~3 minutes for the maintainer.
243+
244+
---
245+
246+
## H. Demand-driven future additions
247+
248+
Captured separately in [`future-additions.md`](future-additions.md):
249+
250+
- More TLS DNS-01 providers (DigitalOcean, Hurricane Electric,
251+
Google Cloud DNS, Linode, OVH)
252+
- More framework recipes (Rails, Next.js, Flask)
253+
- More db engines (valkey, elasticsearch, opensearch, kafka,
254+
minio, clickhouse)
255+
256+
Each entry includes the concrete pattern + LOC estimate. The
257+
graduation rule: build when someone asks. Naïve expansion turns
258+
each `shimkit <tool>` boot into a Typer cold-start tax.
259+
260+
---
261+
262+
## I. Releases shipped this session
263+
264+
For reference / chronology:
265+
266+
```
267+
v0.6.0 shimkit cron
268+
v0.7.0 shimkit framework laravel
269+
v0.7.1 fix version drift in pyproject.toml
270+
v0.8.0 shimkit tls (webroot)
271+
v0.9.0 shimkit db --on-host (mysql/mariadb/postgres)
272+
v0.10.0 coverage 74% → 85%
273+
v0.11.0 release-notes consolidation + PyPI workflow restored
274+
v0.12.0 stale-doc cleanup + codecov + attestation-verify
275+
v0.13.0 shimkit tls --method dns-cloudflare
276+
v0.14.0 shimkit framework symfony
277+
v0.15.0 shimkit db redis (sixth engine)
278+
v0.16.0 shimkit framework django
279+
v0.17.0 shimkit tls --method dns-route53
280+
v0.18.0 post-v0.17 doc-sync
281+
```
282+
283+
13 releases. Test count 561 → 1130. Coverage 74% → 85%. Every
284+
gate green on every release (excluding `publish-pypi`
285+
section G.1).
286+
287+
---
288+
289+
## J. Audit conclusion
290+
291+
| Bucket | Count | Status |
292+
|--------|-------|--------|
293+
| Migration W-items (A) | 9 | ✅ shipped |
294+
| Defer-list items (B) | 4 + 1 rejected | ✅ resolved |
295+
| Validation-report TODOs (C) | 5 | ✅ shipped |
296+
| Cleanup deferrals (D) | 2 | ✅ shipped |
297+
| Shipping-checklist (E) — in-code | all | ✅ shipped |
298+
| Shipping-checklist (E) — user-action | 2 | ⏳ pending (section G) |
299+
| Shipping-checklist (E) — optional | 3 | ⏸ available if wanted |
300+
| Permanent skips (F) | 16 | 🚫 documented |
301+
| Future additions (H) | 14 | 📋 captured in `future-additions.md` |
302+
303+
**The plans tree is drained.** No item in any plan, design spec,
304+
or doc is silently orphaned. Items either:
305+
306+
1. Shipped (and have a corresponding release-notes entry +
307+
CHANGELOG line).
308+
2. Are blocked on a user action (this doc, section G).
309+
3. Were explicitly rejected with rationale (this doc, section F).
310+
4. Are parked for future demand (`future-additions.md`).
311+
312+
The next maintainer can read this doc and know exactly which
313+
of those four buckets every documented intent ended up in.

0 commit comments

Comments
 (0)