diff --git a/.gitignore b/.gitignore
index 01b68d7..a3a0c25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,7 @@ website-backup/
# DevContainer Toolbox - credentials folder (NEVER commit)
.devcontainer.secrets/
website/static/img/brand/tmp/
+
+# DevContainer Toolbox - devcontainer.json backups from dev-update
+.devcontainer/backup/
+.claude/
diff --git a/scripts/generate-docs-markdown.sh b/scripts/generate-docs-markdown.sh
index d3b51d1..b7a7b6e 100755
--- a/scripts/generate-docs-markdown.sh
+++ b/scripts/generate-docs-markdown.sh
@@ -81,6 +81,7 @@ for i in $(seq 0 $((template_count - 1))); do
description=$(jq -r ".templates[$i].description" "$REGISTRY")
category=$(jq -r ".templates[$i].category" "$REGISTRY")
install_type=$(jq -r ".templates[$i].install_type" "$REGISTRY")
+ context=$(jq -r ".templates[$i].context" "$REGISTRY")
abstract=$(jq -r ".templates[$i].abstract" "$REGISTRY")
tools=$(jq -r ".templates[$i].tools" "$REGISTRY")
readme=$(jq -r ".templates[$i].readme" "$REGISTRY")
@@ -106,8 +107,14 @@ for i in $(seq 0 $((template_count - 1))); do
continue
fi
- # Install command — always dev-template now (unified command)
- local_install_cmd="dev-template $tid"
+ # Install command — route by template context (Phase 1 task 1.1)
+ # context: dct → dev-template (DCT devcontainer command)
+ # context: uis → uis template install (UIS provision-host command, available in DCT via the uis shim from DCT v1.7.34+)
+ if [[ "$context" == "uis" ]]; then
+ local_install_cmd="uis template install $tid"
+ else
+ local_install_cmd="dev-template $tid"
+ fi
# Build tags array for MDX component
local_tags_mdx=$(jq -r ".templates[$i].tags | @json" "$REGISTRY")
@@ -119,7 +126,8 @@ for i in $(seq 0 $((template_count - 1))); do
"
done < <(jq -r ".templates[$i].tags[]" "$REGISTRY")
- # Write MDX file
+ # Write MDX file (Phase 1 task 1.2: no separate ## Summary section —
+ # the TemplateHeader description + README intro carry the content)
cat > "$page_file" <
-## Summary
-
-$summary
-
----
-
MDXEOF
# Embed README content
diff --git a/scripts/validate-rules.conf b/scripts/validate-rules.conf
index 8766c41..cd3d5eb 100644
--- a/scripts/validate-rules.conf
+++ b/scripts/validate-rules.conf
@@ -18,10 +18,13 @@ README-*.md|required_heading|Quick Start|error
README-*.md|required_heading|Prerequisites|error
README-*.md|required_heading|Project Structure|error
README-*.md|required_heading|Development|warn
-README-*.md|required_heading|Docker Build|warn
-README-*.md|required_heading|Kubernetes Deployment|warn
README-*.md|required_heading|CI/CD|warn
+# "Docker Build" and "Kubernetes Deployment" sections were dropped in
+# Phase 4 of PLAN-p1-tmp-template-docs-fixes.md — they describe a manual
+# flow that bypasses GitHub Actions + ArgoCD. New templates should use a
+# single "Deploy" section that references the GitOps workflow instead.
+
# ============================================================
# All markdown — MDX compatibility
# ============================================================
diff --git a/templates/python-basic-webserver-database/.gitignore b/templates/python-basic-webserver-database/.gitignore
new file mode 100644
index 0000000..35f9528
--- /dev/null
+++ b/templates/python-basic-webserver-database/.gitignore
@@ -0,0 +1,24 @@
+# Environment files (credentials — never commit)
+.env
+.env.*
+
+# Python virtualenv (created by `uv venv` or `python -m venv`)
+.venv/
+
+# Python bytecode and caches
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+*.egg-info/
+
+# Test/build artifacts
+.pytest_cache/
+.coverage
+htmlcov/
+dist/
+build/
+
+# IDE/editor (but keep .vscode/settings.json which the template ships)
+.idea/
+*.swp
diff --git a/templates/python-basic-webserver-database/README-python-basic-webserver-database.md b/templates/python-basic-webserver-database/README-python-basic-webserver-database.md
index ab1a5fd..decb4ad 100644
--- a/templates/python-basic-webserver-database/README-python-basic-webserver-database.md
+++ b/templates/python-basic-webserver-database/README-python-basic-webserver-database.md
@@ -1,98 +1,235 @@
# Python Basic Webserver with Database
-A minimal Flask web server that connects to PostgreSQL and reads from a `tasks` table. This template demonstrates the full producer/consumer flow:
+A minimal Flask web server that connects to PostgreSQL and reads from a `tasks` table. The full producer/consumer flow:
-- **Producer (UIS):** `uis template install postgresql-demo` deploys PostgreSQL to the cluster
-- **Consumer (this template):** `dev-template configure` creates a per-app database, runs the init SQL, and writes `DATABASE_URL` to `.env`
-- **App:** reads `DATABASE_URL` from the environment and queries the `tasks` table
+- **PostgreSQL** runs in your UIS-managed Kubernetes cluster (deployed once during cluster setup, or via `uis deploy postgresql`).
+- **`dev-template-configure`** creates a per-app database and user, applies the init SQL, and writes `DATABASE_URL` to `.env` for local dev.
+- **The Flask app** reads `DATABASE_URL` from `.env`, connects to PostgreSQL via `host.docker.internal:35432` (the local port forward UIS exposes), and serves the seeded data.
+
+## What this is
+
+A small but complete Flask application:
+
+| Endpoint | Method | Returns |
+|---|---|---|
+| `/` | GET | Plain-text greeting with the template name and current time |
+| `/tasks` | GET | JSON list of rows from the `tasks` table (the seeded data, plus anything you've added) |
+| `/health` | GET | `{"status": "ok", "database": "connected"}` if the DB is reachable, or a 503 if not |
+
+The app **requires** `DATABASE_URL` and exits immediately with a clear error if it's missing — there's no fallback. This is intentional: the template demonstrates the producer/consumer pattern where credentials always come from `dev-template-configure`.
+
+## Prerequisites
+
+This template uses UIS to configure PostgreSQL. Verify the UIS provision-host container is running:
+
+```bash
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
+```
+
+You should see `Up X minutes`. If not, start UIS from the `urbalurba-infrastructure` repo. Inside DCT (devcontainer-toolbox v1.7.34 or later) you also have the `uis` shim, which routes `uis ...` commands to the provision-host automatically.
+
+If PostgreSQL isn't deployed in your cluster, **don't worry** — `dev-template-configure` will detect it in step 4 and tell you exactly what to run (`uis deploy postgresql`).
## Quick Start
-### 1. Deploy PostgreSQL (once per environment)
+### 1. Install the template
+
+```bash
+dev-template python-basic-webserver-database
+```
+
+DCT downloads the template from the registry and copies all files to your current project directory, including `app/`, `manifests/`, `Dockerfile`, `requirements.txt`, `.gitignore`, `template-info.yaml`, and `config/init-database.sql`.
+
+### 2. Edit `template-info.yaml`
+
+Open `template-info.yaml` and find the `params:` section near the bottom. Set values for your app:
+
+```yaml
+params:
+ app_name: "my-cool-app"
+ database_name: "my_cool_app_db"
+```
+
+The defaults (`my-app`, `my_app_db`) work, but pick names that match your project — these become the PostgreSQL user and database names.
+
+The full `template-info.yaml` declares the PostgreSQL dependency in the `requires:` section:
+
+```yaml
+params:
+ app_name: "my-app"
+ database_name: "my_app_db"
+
+requires:
+ - service: postgresql
+ config:
+ database: "{{ params.database_name }}"
+ init: "config/init-database.sql"
+```
+
+DCT reads this file when you run `dev-template-configure` in the next step. The `{{ params.database_name }}` reference is substituted with the value you set above.
+
+### 3. (Optional) Customise `config/init-database.sql`
+
+This file is the schema and seed data UIS applies to your database. The default creates a `tasks` table with 3 rows:
+
+```sql
+CREATE TABLE IF NOT EXISTS tasks (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255) NOT NULL,
+ status VARCHAR(20) DEFAULT 'pending',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
+
+INSERT INTO tasks (title, status) VALUES
+ ('Set up the database connection', 'done'),
+ ('Build something with Flask + PostgreSQL', 'pending'),
+ ('Deploy to Kubernetes via ArgoCD', 'pending')
+ON CONFLICT DO NOTHING;
+```
+
+All statements are idempotent (`IF NOT EXISTS`, `ON CONFLICT DO NOTHING`) so re-running configure is safe. UIS applies the file with `psql --set ON_ERROR_STOP=on`, so any syntax error fails fast with a clear message.
+
+For your real schema, edit this file to add your own tables, indexes, and seed data.
-If PostgreSQL isn't running in your UIS cluster yet, deploy it via the `postgresql-demo` UIS stack template:
+### 4. Run `dev-template-configure`
```bash
-uis template install postgresql-demo
+dev-template-configure
+```
+
+What happens:
+
+1. DCT reads `template-info.yaml` and validates that the `params:` are filled in
+2. DCT calls `uis configure postgresql --app --database --init-file -` via the bridge, piping in the substituted SQL
+3. UIS creates the database and user, applies the init SQL, and writes connection details
+4. UIS also creates a Kubernetes Secret in your app's namespace so the deployed pod can read `DATABASE_URL` later (when you `git push` and ArgoCD deploys)
+5. DCT writes `.env` to your project root (gitignored) with the local connection string
+
+If PostgreSQL isn't deployed in your cluster, this step fails with a clear error from UIS telling you to run `uis deploy postgresql`.
+
+You should see something like:
+
+```
+📦 Configuring postgresql...
+✅ postgresql — configured
+ → .env: DATABASE_URL=postgresql://my_cool_app:Xa7mP9...@host.docker.internal:35432/my_cool_app_db (local)
+ → K8s Secret: -db in namespace (cluster)
```
-### 2. Configure this app's database
+### 5. Verify the database
-This creates a new database + user in PostgreSQL, applies the init SQL (tasks table + seed data), and writes `DATABASE_URL` to `.env`:
+Inspect the seeded data without starting the app:
```bash
-dev-template configure
+uis connect postgresql my_cool_app_db
+```
+
+Inside psql:
+
+```sql
+SELECT * FROM tasks;
+\q
```
-You'll be prompted to fill in `params.app_name` and `params.database_name` in `template-info.yaml` first (or pass them via `--param`).
+You should see 3 rows. If they're there, the database is set up correctly and `DATABASE_URL` is in your `.env`.
-### 3. Install Python dependencies and run
+### 6. Run the app
+
+DCT ships with [`uv`](https://github.com/astral-sh/uv) for fast Python package management. Create a virtualenv, install dependencies, and run the app:
```bash
-pip install -r requirements.txt
+uv venv
+source .venv/bin/activate
+uv pip install -r requirements.txt
python app/app.py
```
-Then open:
-- http://localhost:3000 — home page
-- http://localhost:3000/tasks — list tasks from the database
-- http://localhost:3000/health — verify DB connectivity
+Or one-liner (no manual activation):
-The app **requires** `DATABASE_URL` and will exit immediately if it isn't set.
+```bash
+uv venv
+uv pip install -r requirements.txt
+uv run python app/app.py
+```
-## Prerequisites
+The Flask debug server starts on port 3000.
-Development tools are installed automatically by the devcontainer. If you need to reinstall, run `dev-setup`.
+**VS Code tip (optional):** if you see "Error refreshing packages" from VS Code's Python extension, add this to your workspace `.vscode/settings.json`:
-UIS must be running with PostgreSQL deployed (see step 1 above).
+```json
+{
+ "python-envs.alwaysUseUv": true
+}
+```
+
+The error happens because `uv venv` doesn't install `pip` into the venv (it doesn't need to), and VS Code's Python extension defaults to `pip list` for package enumeration. The setting tells it to use `uv` instead. If your project's `.vscode/settings.json` already exists with other keys, just add this one — don't replace the whole file.
+
+### 7. Open in your browser
+
+VS Code's "Ports" tab in the bottom panel auto-forwards port 3000. Click the globe icon next to it to open these URLs:
-## Project Structure
+- `http://localhost:3000/` — Home page
+- `http://localhost:3000/tasks` — JSON list of seeded rows
+- `http://localhost:3000/health` — DB connectivity check
-```plaintext
+If `/tasks` shows the 3 seeded rows, your producer/consumer chain is working end-to-end: Flask → DATABASE_URL → host.docker.internal → UIS port-forward → PostgreSQL pod in K8s.
+
+## Project structure
+
+After installation, your project contains:
+
+```
├── app/
│ └── app.py # Flask app reading from PostgreSQL
├── config/
-│ └── init-database.sql # Tasks table + seed data (applied by uis configure)
+│ └── init-database.sql # Schema + seed data (applied by uis configure)
├── manifests/
│ ├── deployment.yaml # K8s Deployment + Service (uses Secret for DATABASE_URL)
│ └── kustomization.yaml # ArgoCD configuration
├── .github/
│ └── workflows/
│ └── urbalurba-build-and-push.yaml # CI/CD pipeline
+├── .gitignore # Excludes .env*, .venv/, etc.
├── Dockerfile # Container build
-├── requirements.txt # Python dependencies
-├── template-info.yaml # Template metadata
+├── requirements.txt # Flask, psycopg2-binary, python-dotenv
+├── template-info.yaml # Template metadata (read by dev-template-configure)
└── README-python-basic-webserver-database.md # This file
```
## Development
-- Edit `app/app.py` — the Flask application
-- Edit `config/init-database.sql` to change the schema (re-run `dev-template configure` to apply)
-- Changes auto-reload in debug mode
+- Edit `app/app.py` — the main Flask application. Changes auto-reload in debug mode.
+- Edit `config/init-database.sql` to change the schema. Re-run `dev-template-configure` to apply the changes.
+- Edit `template-info.yaml` to change `params`. Re-run `dev-template-configure` afterward (it's idempotent — safe to run repeatedly).
-## Docker Build
+## Deploy to your local cluster
-```bash
-docker build -t python-basic-webserver-database .
-docker run -p 3000:3000 --env-file .env python-basic-webserver-database
-```
+The standard workflow uses GitHub Actions + ArgoCD — no manual `docker build` or `kubectl apply`:
-## Kubernetes Deployment
+1. **Push your code to GitHub**:
+ ```bash
+ git push
+ ```
+ GitHub Actions builds and pushes the container image to GitHub Container Registry. The image is **credential-free** — `DATABASE_URL` is injected at runtime from a Kubernetes Secret.
-Before deploying, create the `DATABASE_URL` secret using the **cluster** connection string from `uis configure` output:
+2. **Register the app with ArgoCD** (one-time per project, from your host machine):
+ ```bash
+ ./uis argocd register
+ ```
+ This creates an ArgoCD Application that watches your repo and auto-deploys updates on every push.
-```bash
-kubectl create secret generic -db \
- --from-literal=DATABASE_URL='postgresql://user:pass@postgresql.default.svc.cluster.local:5432/'
-```
+3. **Access the app** at `http://.localhost`. ArgoCD applies the deployment manifest, K8s injects `DATABASE_URL` from the Secret UIS created in step 4 above, and the pod connects to PostgreSQL via the cluster service DNS (`postgresql.default.svc.cluster.local`).
-Then apply the manifests:
+You don't need to create the Kubernetes Secret manually — `dev-template-configure` already created it in the right namespace. The deployment manifest references it via `secretKeyRef`.
-```bash
-kubectl apply -k manifests/
-```
+## Try this with
+
+This is the consumer side of the producer/consumer pattern. The producer side is:
+
+- [PostgreSQL Demo](../demo/postgresql-demo) — a UIS stack template that deploys PostgreSQL standalone, useful for verifying your UIS setup. You don't need to install it for `python-basic-webserver-database` to work — `dev-template-configure` handles everything.
## CI/CD
-The GitHub Actions workflow automatically builds and pushes the Docker image to GitHub Container Registry when changes are pushed to the main branch.
+The GitHub Actions workflow (`.github/workflows/urbalurba-build-and-push.yaml`) automatically builds and pushes the Docker image to GitHub Container Registry when changes are pushed to the main branch. ArgoCD picks up the new image and deploys it.
diff --git a/uis-stack-templates/postgresql-demo/.gitignore b/uis-stack-templates/postgresql-demo/.gitignore
new file mode 100644
index 0000000..7194e86
--- /dev/null
+++ b/uis-stack-templates/postgresql-demo/.gitignore
@@ -0,0 +1,7 @@
+# Environment files (credentials — never commit)
+.env
+.env.*
+
+# Editor swap files
+*.swp
+.idea/
diff --git a/uis-stack-templates/postgresql-demo/README-postgresql-demo.md b/uis-stack-templates/postgresql-demo/README-postgresql-demo.md
index 0101e1a..9bd269e 100644
--- a/uis-stack-templates/postgresql-demo/README-postgresql-demo.md
+++ b/uis-stack-templates/postgresql-demo/README-postgresql-demo.md
@@ -1,6 +1,6 @@
# PostgreSQL Demo
-A minimal UIS stack template that deploys PostgreSQL and creates a sample database with seed data. Use this to verify your UIS setup or as a starting point for your own stack templates.
+A minimal UIS stack template that deploys PostgreSQL and creates a sample database with seed data. Use it to verify your UIS setup, or as a starting point for your own UIS stack templates.
## What it deploys
@@ -8,18 +8,28 @@ A minimal UIS stack template that deploys PostgreSQL and creates a sample databa
## What it configures
-- Creates a per-app user (derived from `app_name` param)
-- Creates a database (from `database_name` param)
-- Applies the init SQL file — creates a `tasks` table with 3 seed rows
+- A per-app user (derived from `app_name` param)
+- A database (from `database_name` param)
+- The init SQL — creates a `tasks` table with 3 seed rows
-## Usage
+## Before you start
-From the UIS provision-host:
+This template uses UIS. Verify the UIS provision-host container is running:
+
+```bash
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
+```
+
+You should see `Up X minutes`. If not, start UIS from the `urbalurba-infrastructure` repo. Inside DCT (devcontainer-toolbox v1.7.34 or later) you also have the `uis` shim, which lets you run UIS commands directly.
+
+## Install
```bash
uis template install postgresql-demo
```
+This works from inside the DCT devcontainer (via the `uis` shim), from the host (via `./uis` from the urbalurba-infrastructure repo), and from inside the UIS provision-host. Same command, three contexts.
+
With custom params:
```bash
@@ -28,7 +38,7 @@ uis template install postgresql-demo --param app_name=myapp --param database_nam
## What you get
-After install, `uis template install` returns JSON with connection details:
+After install, `uis template install` returns JSON with connection details (passwords are randomly generated — your actual values will be different):
```json
{
@@ -37,42 +47,50 @@ After install, `uis template install` returns JSON with connection details:
"local": {
"host": "host.docker.internal",
"port": 35432,
- "database_url": "postgresql://demo_app:@host.docker.internal:35432/demo_db"
+ "database_url": "postgresql://demo_app:Xa7mP9...@host.docker.internal:35432/demo_db"
},
"cluster": {
"host": "postgresql.default.svc.cluster.local",
- "port": 5432,
- "database_url": "postgresql://demo_app:@postgresql.default.svc.cluster.local:5432/demo_db"
+ "port": 5432
},
"database": "demo_db",
"username": "demo_app",
- "password": ""
+ "password": "Xa7mP9...",
+ "secret_name": "-db",
+ "secret_namespace": ""
}
```
-## Verify it worked
-
-Expose the service and connect:
+The `local` URL works from your DCT devcontainer (Flask, psql, etc.) via the host port-forward.
+The `cluster` connection is what K8s pods use — UIS also creates a Kubernetes Secret in your app's namespace so deployments can read it via `secretKeyRef`.
-```bash
-uis expose postgresql
-```
+## Verify it worked
-Then from any container with psql:
+The simplest way to inspect the seeded data:
```bash
-psql -h host.docker.internal -p 35432 -U demo_app -d demo_db
-# Enter the password from the JSON output above
+uis connect postgresql demo_db
```
-Query the tasks table:
+Inside psql:
```sql
SELECT * FROM tasks;
+\q
```
You should see 3 rows. Re-running `uis template install postgresql-demo` is safe — it detects the existing database and returns `already_configured`.
+## Try this with
+
+Once PostgreSQL is running and you've installed this demo template, scaffold a Flask app on top with the consumer-side template:
+
+```bash
+dev-template python-basic-webserver-database
+```
+
+That template's `dev-template-configure` step will create its own per-app database (separate from `demo_db`) and write `DATABASE_URL` to `.env` for local dev. Run the app with `uv run python app/app.py` and curl `/tasks` to see the full producer/consumer chain working end-to-end.
+
## Extending this template
This template is the minimum viable example. To build your own:
@@ -81,3 +99,5 @@ This template is the minimum viable example. To build your own:
2. Edit `template-info.yaml` — change `id`, `name`, add more services to `provides`
3. Add more init files in `config/` (SQL, Authentik blueprints, Grafana dashboards)
4. Reference UIS stacks (like `observability`) in `provides.stacks` to include multi-service stacks
+
+See the [contributor docs](https://tmp.sovereignsky.no/docs/contributors/creating-a-template) for the full template authoring guide.
diff --git a/uis-stack-templates/postgresql-demo/template-info.yaml b/uis-stack-templates/postgresql-demo/template-info.yaml
index cefe842..9a29e07 100644
--- a/uis-stack-templates/postgresql-demo/template-info.yaml
+++ b/uis-stack-templates/postgresql-demo/template-info.yaml
@@ -25,7 +25,8 @@ summary: >
and creates a sample database with a tasks table and seed data. Shows the
uis template flow from registry to deployed, configured service. Use it to
verify your UIS setup or as a starting point for your own stack templates.
-related: []
+related:
+ - python-basic-webserver-database
params:
app_name: "demo-app"
diff --git a/website/docs/ai-developer/plans/active/PLAN-p1-tmp-template-docs-fixes.md b/website/docs/ai-developer/plans/active/PLAN-p1-tmp-template-docs-fixes.md
new file mode 100644
index 0000000..39628f1
--- /dev/null
+++ b/website/docs/ai-developer/plans/active/PLAN-p1-tmp-template-docs-fixes.md
@@ -0,0 +1,230 @@
+# Plan: Phase 1 — TMP fixes for templates with UIS services
+
+> **IMPLEMENTATION RULES:** Before implementing this plan, read and follow:
+> - [WORKFLOW.md](../../WORKFLOW.md) - The implementation process
+> - [PLANS.md](../../PLANS.md) - Plan structure and best practices
+
+## Status: Completed (pending PR merge)
+
+**Completed**: 2026-04-09
+
+**Goal**: Ship the TMP-side Phase 1 work from `INVESTIGATE-improve-template-docs-with-services.md`. Fix the bugs real-user testing surfaced in `python-basic-webserver-database` and `postgresql-demo`, plus the cross-cutting MDX generator and template hygiene issues.
+
+**Investigation**: [INVESTIGATE-improve-template-docs-with-services.md](../backlog/INVESTIGATE-improve-template-docs-with-services.md)
+
+**Cross-team dependencies**:
+- **DCT**: shipping `PLAN-p1-dct-shim.md` (items 1.8 + 1.9 in the investigation) — DCT shim + `--namespace`/`--secret-name-prefix` pass-through
+- **UIS**: shipping `PLAN-p1-uis-secret-namespace.md` (item 1.10 + 1.11 coordination) — `uis configure --namespace` + K8s Secret creation
+
+**Priority**: High
+
+**Last Updated**: 2026-04-09
+
+---
+
+## Overview
+
+Three workstreams, sequenced by external dependencies:
+
+1. **Independent (start immediately)**: generator fixes, template hygiene files. Items 1.1–1.4 from the investigation. No external dependencies.
+2. **Blocked on DCT shim**: `postgresql-demo` README rewrite, `readme-structure.md` updates. Items 1.6 + 1.7 from the investigation.
+3. **Blocked on DCT shim AND UIS minimum D5**: `python-basic-webserver-database` README rewrite, including the Verify section and the new `uis configure` flow. Items 1.5 + 1.12 + 1.13 from the investigation.
+
+The plan is structured so that workstream 1 ships first as a single PR, then 2 and 3 ship as the upstream dependencies land.
+
+**Coordination items not implemented in this plan** (tracked in the investigation, owned cross-team):
+- 1.11 Secret name convention — TMP confirmed Option A (use `{{REPO_NAME}}` placeholder), no template changes needed. UIS implements per `PLAN-p1-uis-secret-namespace.md`.
+
+---
+
+## Phase 1: Independent TMP fixes (start immediately) — DONE
+
+No external dependencies. Can ship as a single PR while DCT and UIS work in parallel.
+
+### Tasks
+
+- [x] 1.1 Update `scripts/generate-docs-markdown.sh` to route the install command by template `context` ✓
+- [x] 1.2 Stop the generator from emitting a duplicated "## Summary" section ✓
+
+- [x] 1.3 Add `.gitignore` to `templates/python-basic-webserver-database/`
+- [x] 1.4 Add `.gitignore` to `uis-stack-templates/postgresql-demo/`
+- [~] 1.5 ~~Add `.vscode/settings.json` to `templates/python-basic-webserver-database/`~~ — **REVERTED**
+ - Initial implementation shipped a `.vscode/settings.json` with `python-envs.alwaysUseUv: true`. This was reverted because it would risk overwriting the user's existing `.vscode/settings.json` (or `extensions.json`) which contains the devcontainer recommendation needed for the project to start.
+ - **New approach:** the README documents the setting as a one-line manual addition users can make to their workspace settings if they hit the VS Code "Error refreshing packages" cosmetic error. Doesn't risk overwriting anything.
+ - **DCT follow-up:** ideally DCT ships `python-envs.alwaysUseUv: true` as a global devcontainer-level setting in the base image (per the open question in B6 of the investigation). Then no template needs to ship it. Tracked as a DCT ask.
+ - Solves: B6 (via documentation, not via template file)
+- [x] 1.6 Verify the `dev-template install` copy logic includes `.vscode/` and `.gitignore`
+ - **Finding (still relevant for future templates):** Reviewed `helpers-no/devcontainer-toolbox/.devcontainer/manage/dev-template.sh` lines 88-131:
+ - ✅ `.gitignore` is handled explicitly (lines 107-131) — DCT merges it intelligently
+ - ✅ `.github/` is handled explicitly (lines 98-105)
+ - ✅ `template-info.yaml` is copied explicitly (lines 93-96)
+ - ❌ **`.vscode/` is NOT handled** — the bulk copy `cp -r "$TEMPLATE_PATH/"* "$CALLER_DIR/"` uses a glob `*` which by default does NOT match hidden directories.
+ - ⚠️ **Even if DCT fixes the hidden-directory bug**, naively copying `.vscode/` would overwrite the user's existing `.vscode/extensions.json` with the devcontainer recommendation, breaking the project. DCT would need a JSON merge strategy similar to the `.gitignore` line-merge.
+ - **DCT follow-up:** if DCT wants to support `.vscode/` in templates, they need both (a) include hidden directories in the bulk copy AND (b) implement JSON-merge for `.vscode/*.json` files (preserving existing keys). Until then, templates can't safely ship `.vscode/` files.
+ - **Decision for this PR:** don't ship `.vscode/` from any template. Document VS Code settings in the README as manual additions instead.
+
+- [ ] 1.7 Regenerate the registry and docs locally
+ - `bash scripts/generate-registry.sh`
+ - `bash scripts/generate-docs-markdown.sh --force`
+ - `npm run build --prefix website` to verify no build errors
+ - Confirm the postgresql-demo page now shows `uis template install postgresql-demo` as the install command (1.1)
+ - Confirm the duplicated `## Summary` is gone (1.2)
+
+### Validation — DONE
+
+- [x] `bash scripts/validate-metadata.sh` passes — 5 categories, 10 templates
+- [x] `bash scripts/validate-docs.sh` passes — 0 errors, 4 warnings (optional headings only)
+- [x] `npm run build --prefix website` succeeds in devcontainer
+- [x] postgresql-demo MDX page shows `install="uis template install postgresql-demo"` (1.1 verified)
+- [x] python-basic-webserver-database MDX page uses `install="dev-template python-basic-webserver-database"` (existing behavior preserved)
+- [x] No `## Summary` section emitted by generator (1.2 verified)
+- [ ] User confirms Phase 1 complete before moving to dependent phases
+
+---
+
+## Phase 2: README rewrite — `postgresql-demo` — DONE
+
+**Blocked on**: DCT 1.8 (uis shim) shipping. ✓ DCT v1.7.34 is live.
+
+### Tasks
+
+- [x] 2.1 Drop the misleading "From the UIS provision-host:" prefix throughout the README ✓
+- [x] 2.2 Populate `related:` in `template-info.yaml` with `[python-basic-webserver-database]` — auto-generated "Related Templates" section now appears ✓
+- [x] 2.3 Add a "Before you start" prerequisite section ✓
+- [x] 2.4 Update the "Verify it worked" section to use `uis connect postgresql demo_db` ✓
+- [x] 2.5 Regenerate docs and verify the build ✓ (validate-docs passes, npm run build SUCCESS)
+
+### Validation
+
+- `bash scripts/generate-docs-markdown.sh --force` regenerates the postgresql-demo MDX page
+- `npm run build --prefix website` succeeds
+- The rendered postgresql-demo page no longer references "From the UIS provision-host:"
+- The "Related Templates" section appears, linking to python-basic-webserver-database
+- User confirms phase is complete
+
+---
+
+## Phase 3: README rewrite — `python-basic-webserver-database` — DONE
+
+**Blocked on**: DCT 1.8 (uis shim) AND UIS 1.10 (minimum D5) shipping. ✓ Both live (DCT v1.7.34, UIS PR #121).
+
+### Tasks
+
+- [x] 3.1 Add a "What this is" section near the top with endpoints table ✓
+- [x] 3.2 Add a "Prerequisites" section (renamed from "Before you start" — uses the existing convention enforced by validate-rules.conf) ✓
+- [x] 3.3 Replace Quick Start with the canonical 7-step workflow ✓
+- [x] 3.4 Embed `template-info.yaml` content inline ✓
+- [x] 3.5 Embed `config/init-database.sql` content inline ✓
+- [x] 3.6 Remove "Docker Build" and "Kubernetes Deployment" sections, replace with single "Deploy" section ✓
+- [x] 3.7 Quick Start uses `uv venv` + `uv pip install` + `python app/app.py` ✓
+- [x] 3.8 Verify and regenerate ✓ (validate-docs passes, npm run build SUCCESS)
+
+### Validation
+
+- `bash scripts/validate-docs.sh` passes
+- `bash scripts/generate-docs-markdown.sh --force` succeeds
+- `npm run build --prefix website` succeeds
+- Spot-check the rendered python-basic-webserver-database page: every step in the canonical workflow is present, in order, with the right commands
+- **Real-user re-test**: install the template into a scratch project, follow the README literally, confirm the original walls (uv warnings, missing dev-template step, wrong postgresql-demo prerequisite, etc.) are all fixed
+- User confirms phase is complete
+
+---
+
+## Phase 4: Cross-cutting docs — `readme-structure.md` — DONE
+
+### Tasks
+
+- [x] 4.1 `readme-structure.md` documents the "Prerequisites" / UIS-aware prerequisite requirements for templates with `requires` ✓
+- [x] 4.2 `readme-structure.md` documents the "Verify it worked" requirement (`uis connect `) ✓
+- [x] 4.3 Remove "Docker Build" and "Kubernetes Deployment" from the suggested sections list ✓
+ - Also removed them from `validate-rules.conf` (no more spurious warnings)
+ - Updated wording to discourage manual `docker build` / `kubectl apply` patterns
+- [x] 4.4 `readme-structure.md` documents the requirement to embed `template-info.yaml` and init file contents inline ✓
+- [ ] 4.5 (Deferred) Extend `validate-docs.sh` to enforce new requirements per-template — out of scope for this PR. The rules in `validate-rules.conf` apply globally to `README-*.md`; per-template enforcement would need template-info.yaml awareness in `validate-docs.sh`. Tracked as follow-up.
+
+---
+
+## Acceptance Criteria
+
+- [x] Generator routes install commands by `context` (postgresql-demo shows `uis template install`)
+- [x] Generator no longer emits duplicated `## Summary` sections
+- [x] Both templates have `.gitignore` files preventing `.env*` from being committed
+- [x] python-basic-webserver-database README documents the `python-envs.alwaysUseUv` setting as a manual workspace addition (not shipped as a file — see 1.5 reverted)
+- [x] postgresql-demo README uses bare `uis ...` commands (no "From the UIS provision-host:" prefix)
+- [x] postgresql-demo README links to python-basic-webserver-database via `related:`
+- [x] python-basic-webserver-database README follows the canonical workflow from the investigation
+- [x] python-basic-webserver-database README has "What this is", "Prerequisites", and "Verify the database" sections
+- [x] python-basic-webserver-database README embeds `template-info.yaml` and `config/init-database.sql` content inline
+- [x] python-basic-webserver-database README's Quick Start uses `uv` (not `pip`)
+- [x] python-basic-webserver-database README's "Docker Build" / "Kubernetes Deployment" sections are gone, replaced by a "Deploy" section using GitHub Actions + ArgoCD
+- [x] `readme-structure.md` documents the new section requirements for templates with `requires`
+- [x] `validate-rules.conf` no longer warns on missing `## Docker Build` / `## Kubernetes Deployment` headings
+- [x] `bash scripts/validate-metadata.sh` passes
+- [x] `bash scripts/validate-docs.sh` passes (0 errors, 2 warnings — unrelated `plan-based-workflow` README)
+- [x] `npm run build --prefix website` passes inside the devcontainer
+- [ ] CI pipeline is green after merge
+
+---
+
+## Files to Modify
+
+### Phase 1 (independent)
+
+**Generator:**
+- `scripts/generate-docs-markdown.sh` — install command routing (1.1) + drop duplicated Summary (1.2)
+
+**Templates (new files):**
+- `templates/python-basic-webserver-database/.gitignore` (1.3)
+- `templates/python-basic-webserver-database/.vscode/settings.json` (1.5)
+- `uis-stack-templates/postgresql-demo/.gitignore` (1.4)
+
+### Phase 2 (postgresql-demo)
+
+- `uis-stack-templates/postgresql-demo/README-postgresql-demo.md` (rewrite)
+- `uis-stack-templates/postgresql-demo/template-info.yaml` (populate `related:`)
+
+### Phase 3 (python-basic-webserver-database)
+
+- `templates/python-basic-webserver-database/README-python-basic-webserver-database.md` (full rewrite around canonical workflow)
+
+### Phase 4 (cross-cutting docs)
+
+- `website/docs/contributors/readme-structure.md` (new section requirements)
+- `scripts/validate-docs.sh` (optional — extend to enforce new requirements)
+- `scripts/validate-rules.conf` (optional — add new rule entries)
+
+### Auto-regenerated by CI (don't hand-edit)
+
+- `website/src/data/template-registry.json`
+- `website/docs/templates/demo/postgresql-demo.mdx`
+- `website/docs/templates/basic-web-server-database/python-basic-webserver-database.mdx`
+- `website/docs/ai-developer/plans/*/index.md`
+
+---
+
+## Implementation Notes
+
+**Branch strategy**: each Phase becomes its own PR. Phase 1 is the "ship now" PR. Phases 2, 3, 4 ship as their dependencies land.
+
+**Phase 1 PR title**: `Phase 1 — Independent TMP fixes for templates with UIS services`
+**Phase 2 PR title**: `Phase 2 — postgresql-demo README rewrite (after DCT shim)`
+**Phase 3 PR title**: `Phase 3 — python-basic-webserver-database README rewrite (after DCT shim + UIS Secret)`
+**Phase 4 PR title**: `Phase 4 — readme-structure.md updates`
+
+**Test in the devcontainer, not on the host.** All script runs (`generate-docs-markdown.sh`, `validate-docs.sh`, `npm run build`) should run inside the dev-templates devcontainer to match the CI environment.
+
+**Real-user re-test for Phase 3.** After Phase 3 ships, install python-basic-webserver-database into a fresh scratch project and walk through every step of the README literally. The original user testing found 8+ issues by doing this — a re-test is the only way to confirm Phase 3 actually works.
+
+**Don't add features beyond what the investigation specifies.** Phase 2 and 3 issues like "deduplicate the README intro paragraph against the Summary section" are tempting but out of scope (they're tracked as Phase 2 work in the investigation). Stay strictly within Phase 1.
+
+---
+
+## Cross-references
+
+- **Investigation**: [INVESTIGATE-improve-template-docs-with-services.md](../backlog/INVESTIGATE-improve-template-docs-with-services.md)
+- **DCT plan** (cross-team): `helpers-no/devcontainer-toolbox` → `PLAN-p1-dct-shim.md`
+- **UIS plan** (cross-team): `helpers-no/urbalurba-infrastructure` → `PLAN-p1-uis-secret-namespace.md`
+- **Coordination items in the investigation**:
+ - 1.8 (DCT shim) — blocks Phases 2, 3, 4
+ - 1.10 (UIS minimum D5) — blocks Phase 3
+ - 1.11 (secret name convention) — TMP confirmed Option A, no template changes needed
diff --git a/website/docs/ai-developer/plans/backlog/INVESTIGATE-github-actions-node24-migration.md b/website/docs/ai-developer/plans/backlog/INVESTIGATE-github-actions-node24-migration.md
new file mode 100644
index 0000000..a556ae6
--- /dev/null
+++ b/website/docs/ai-developer/plans/backlog/INVESTIGATE-github-actions-node24-migration.md
@@ -0,0 +1,61 @@
+# Investigate: GitHub Actions Node.js 24 Migration
+
+> **IMPLEMENTATION RULES:** Before implementing this plan, read and follow:
+> - [WORKFLOW.md](../../WORKFLOW.md) - The implementation process
+> - [PLANS.md](../../PLANS.md) - Plan structure and best practices
+
+## Status: Backlog
+
+**Goal**: Migrate GitHub Actions workflows from Node.js 20 actions to Node.js 24-compatible versions before the forced migration on June 2nd, 2026.
+
+**Priority**: Medium
+
+**Last Updated**: 2026-04-06
+
+---
+
+## Context
+
+Every CI run produces this warning:
+
+```
+Node.js 20 actions are deprecated. The following actions are running on Node.js 20
+and may not work as expected: actions/checkout@v4, actions/setup-node@v4,
+actions/upload-artifact@v4, actions/deploy-pages@v4.
+
+Actions will be forced to run with Node.js 24 by default starting June 2nd, 2026.
+Node.js 20 will be removed from the runner on September 16th, 2026.
+```
+
+### Deadlines
+
+- **June 2, 2026** — Node.js 24 becomes default. Actions forced to run on Node.js 24 unless opted out.
+- **September 16, 2026** — Node.js 20 removed entirely. No opt-out possible.
+
+### Affected Workflow
+
+`.github/workflows/deploy-docs.yml` — uses these actions:
+
+| Action | Current Version | Job |
+|--------|----------------|-----|
+| `actions/checkout` | `@v4` | generate, build |
+| `actions/setup-node` | `@v4` | generate, build |
+| `actions/upload-pages-artifact` | `@v3` | build |
+| `actions/deploy-pages` | `@v4` | deploy |
+
+---
+
+## Questions to Answer
+
+1. Are there `@v5` versions of these actions that support Node.js 24?
+2. Can we opt into Node.js 24 early with `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true` to test before the forced migration?
+3. Does the `setup-node` action's Node.js 20 runtime (which runs the action itself) conflict with the Node.js 20 we install for our build step? (Likely no, but verify.)
+4. Are there any breaking changes in the new action versions?
+
+---
+
+## Next Steps
+
+- [ ] Check for updated action versions supporting Node.js 24
+- [ ] Test with `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true` environment variable
+- [ ] Update action versions in `deploy-docs.yml`
diff --git a/website/docs/ai-developer/plans/backlog/INVESTIGATE-improve-template-docs-with-services.md b/website/docs/ai-developer/plans/backlog/INVESTIGATE-improve-template-docs-with-services.md
new file mode 100644
index 0000000..e7777d9
--- /dev/null
+++ b/website/docs/ai-developer/plans/backlog/INVESTIGATE-improve-template-docs-with-services.md
@@ -0,0 +1,904 @@
+# Investigate: Improve Template Docs for Templates with UIS Services
+
+> **IMPLEMENTATION RULES:** Before implementing this plan, read and follow:
+> - [WORKFLOW.md](../../WORKFLOW.md) - The implementation process
+> - [PLANS.md](../../PLANS.md) - Plan structure and best practices
+
+## Status: Backlog
+
+**Goal**: Fix documentation, bugs, and rough edges for templates that depend on UIS services, in a way that actually ships. Real-user testing of `python-basic-webserver-database` surfaced concrete problems. This investigation scopes the minimum viable fix and defers speculation.
+
+**Priority**: High
+
+**Last Updated**: 2026-04-09
+
+### Contributors
+
+- **TMP**: dev-templates maintainer (primary author, drove the user testing)
+- **UIS**: urbalurba-infrastructure maintainer (comments prefixed with **UIS:** — e.g., **1UIS:**, **2UIS:**)
+- **DCT**: devcontainer-toolbox maintainer (comments prefixed with **DCT:** — e.g., **1DCT:**, **2DCT:**)
+
+> **TMP:** This investigation covers follow-up work on the unified template system ([INVESTIGATE-unified-template-system.md](../completed/INVESTIGATE-unified-template-system.md), completed). Real-user testing of the first DCT app template with `requires` (`python-basic-webserver-database`) and the first UIS stack template (`postgresql-demo`) surfaced concrete problems. The Phase 1 scope includes work from all three teams. Please review Section 2 (Phase 1) and add your comments inline. Questions for UIS on sections 1.10 and 1.11. Questions for DCT on sections 1.8 and 1.9.
+
+### Messages
+
+Short, concise action items between contributors. Format: `NMSG: FROM → TO: message` or `NMSG: done by WHO`.
+
+> **1MSG: TMP → DCT+UIS:** Investigation restructured into phases. Phase 1 (ship now) includes work for all three teams. Please review Part 2 (Phase 1) and confirm you agree with the scope, timing, and division of work. Specific asks:
+> - **DCT:** Items 1.8 (uis shim) and 1.9 (pass `--namespace` to UIS). Is the 15-line shim design in 1.8 acceptable? Any concerns about the new `uis_bridge_run_tty` function?
+> - **UIS:** Items 1.10 (minimum D5 — `uis configure --namespace` + K8s Secret) and 1.11 (secret name convention). Is Option A (secret name from `{{REPO_NAME}}` passed via `--secret-name-prefix`) acceptable?
+>
+> **2MSG: TMP → DCT:** On the secret-name convention (1.11): TMP prefers Option A (use `{{REPO_NAME}}` placeholder, which the current template manifest already uses) because it requires zero changes to templates. Option B (new `{{APP_NAME}}` placeholder) is cleaner long-term but requires touching every template. Deferring Option B to Phase 2. Does DCT agree?
+>
+> **3MSG: TMP → UIS:** On the minimum D5 scope (1.10): TMP proposes shipping PostgreSQL only in Phase 1. Other configurable services (Redis, MongoDB, Authentik) can follow the same pattern when a template actually needs them. The JSON response change removes `cluster.database_url` as a string and adds `secret_name`/`secret_namespace`/`env_var`. Does UIS agree with the minimal scope?
+>
+> **4MSG: TMP → ALL:** Phase 2 and Phase 3 are explicitly deferred until Phase 1 ships and we have real feedback. Future items (D7 env.yaml, D8 data libraries, `dev-template verify`) are explicitly out of scope for this investigation.
+>
+> **5MSG: UIS → TMP+DCT:** Reviewed Phase 1. UIS scope (1.10, 1.11) is clear and minimal. Agreed on shipping PostgreSQL only.
+>
+> **1UIS: On Item 1.10 (minimum D5).** Confirmed scope is right. UIS will:
+> - Add `--namespace ` flag (creates namespace if missing)
+> - Add `--secret-name-prefix ` flag (defaults to `` if not given)
+> - Create K8s Secret named `-db` in the namespace with key `DATABASE_URL`
+> - JSON response: keep `local.database_url` (for `.env` local dev), **remove** `cluster.database_url` string, add `secret_name`/`secret_namespace`/`env_var: "DATABASE_URL"`
+> - Idempotent: re-run updates the secret in place via `kubectl apply` (or `create --dry-run=client -o yaml | kubectl apply -f -`)
+> - Backward compat: when `--namespace` is NOT passed, behavior is unchanged (no secret created, response still includes `cluster.database_url` for existing callers like `uis template install postgresql-demo`)
+>
+> **2UIS: On Item 1.11 (secret name convention).** I checked the actual `python-basic-webserver-database/manifests/deployment.yaml`. It uses `{{REPO_NAME}}-db` (with `-db` suffix, not `-postgresql` suffix). The doc has a small inconsistency: Item 1.10 says `my-app-postgresql` but Item 1.11 and the actual manifest say `-db`. The manifest is the source of truth — UIS will use **`-db`**.
+>
+> Agreed on Option A: DCT passes `--secret-name-prefix ` and UIS creates `-db`. Zero template changes needed.
+>
+> **3UIS: One question for DCT.** When `dev-template-configure` calls `uis configure`, it will pass both `--namespace ` (for the K8s namespace) and `--secret-name-prefix ` (for the secret name). These are usually the same value but conceptually different: namespace is "where to deploy the app", secret prefix is "how the manifest references the secret". Confirm DCT will pass both? Or do you want UIS to default `--secret-name-prefix` to the namespace value?
+>
+> **4UIS: One question for TMP about removing `cluster.database_url`.** This is a breaking change for any caller that reads it. Currently the only known callers are:
+> - DCT's `uis-bridge.sh` (will be updated as part of 1.9)
+> - UIS's own `uis template install` (postgresql-demo template — doesn't use the field, just displays the JSON)
+>
+> If both are unblocked, removing it is fine. But to be safe, I'd suggest **deprecating** instead: keep `cluster.database_url` in the response for one cycle with a `_deprecated_cluster_database_url` warning field, then remove in Phase 2. That way any third-party caller doesn't break silently. Thoughts?
+>
+> **Estimated time for 1.10:** 1 day. Will create `PLAN-001-uis-secret-namespace.md` (note: this is the third "PLAN-001" in the UIS repo since plans are numbered per active sequence — will use a longer name to avoid confusion). Ready to start as soon as the questions in 3UIS and 4UIS are answered.
+>
+> **6MSG: DCT → TMP+UIS:** Reviewed Phase 1. DCT scope (1.8, 1.9) is small and ready to start. Answers to outstanding questions:
+>
+> **1DCT: On 1MSG (shim design + `uis_bridge_run_tty`).** The 15-line shim is acceptable. One implementation note for `uis_bridge_run_tty()`:
+>
+> ```bash
+> uis_bridge_run_tty() {
+> docker exec -it "$UIS_CONTAINER" uis "$@"
+> }
+> ```
+>
+> The `-i` plus `-t` requires both stdin and stdout to be TTYs. The shim's TTY check `[ -t 0 ] && [ -t 1 ]` covers this. For piped input (`echo "..." | uis configure --init-file -`) the shim correctly routes to `uis_bridge_run_stdin` which uses `docker exec -i` only — no TTY allocated, no surprise. Tested this pattern in Phase B already, works on Mac via Rancher Desktop.
+>
+> One question back to TMP: should `uis help` work without the UIS container running? Currently `uis_bridge_check` exits 1 if the container isn't running. For `uis help` (which is local-only output) this is annoying. Suggest the shim has a special-case fast path for `--help`/`-h`/`help` that bypasses the bridge check and prints DCT-specific usage with a "to use other commands, ensure UIS container is running" note. Acceptable, or do you want `uis help` to be a hard error when the container is down?
+>
+> **2DCT: On 2MSG (Option A for secret name).** Agreed — Option A. Zero template changes is the right call for Phase 1. We already have `{{REPO_NAME}}` substitution working in `dev-template.sh`. Adding a new placeholder (`{{APP_NAME}}`) would touch every existing template manifest and break our "templates work as-is after install" property. Defer Option B until a real conflict surfaces.
+>
+> **3DCT: On 3UIS (pass both flags or default one?).** DCT will pass **both `--namespace` and `--secret-name-prefix` explicitly**. Reasons:
+> - The values can legitimately differ. `params.subdomain` is the user-facing app name (e.g., `volunteer-tracker`); `REPO_NAME` is whatever the developer named their git repo (e.g., `vt-2026-rebuild`). The deployment manifest references the secret by repo name, but we want the namespace to be the friendly subdomain.
+> - Implicit defaults hide intent. Future readers of the bridge code shouldn't have to remember "if you don't pass X, it defaults to Y from earlier".
+> - Cost is one extra arg in DCT, zero cost in UIS.
+>
+> Concrete plan: `dev-template-configure` will resolve:
+> - `namespace` = `params.subdomain` ?? `params.app_name` ?? `${REPO_NAME}`
+> - `secret_name_prefix` = `${REPO_NAME}` (matches the deployment manifest's existing `{{REPO_NAME}}-db`)
+>
+> **4DCT: On 4UIS (deprecate vs remove `cluster.database_url`).** This is TMP's call but DCT has a preference: **deprecate for one cycle**. Reasons:
+> - Decouples DCT 1.9 from UIS 1.10 timing. If UIS removes the field on day 1 and DCT 1.9 ships on day 3, our `uis-bridge.sh` is broken for 2 days for anyone who upgrades UIS first.
+> - DCT will update the bridge to use `secret_name`/`secret_namespace`/`env_var` regardless. The deprecation period just gives us safety on the rollout order.
+> - The cost is one extra field in the JSON for one release cycle.
+>
+> If TMP overrules and wants immediate removal, DCT can still ship 1.9 — the bridge update is small. But deprecation is the lower-risk path.
+>
+> **5DCT: Estimated time for DCT work.** ~1 day total:
+> - 1.8 (uis shim): 0.5 day — write shim, add `uis_bridge_run_tty`, add `uis` symlink to `image/Dockerfile`, test all four input modes (TTY, stdin pipe, no TTY, container down)
+> - 1.9 (`--namespace` + `--secret-name-prefix` in configure): 0.5 day after UIS 1.10 lands
+>
+> DCT will create `PLAN-p1-dct-shim.md` covering both items.
+>
+> **Ready to start 1.8 immediately.** It has zero external dependencies and unblocks TMP's README rewrites.
+>
+> **7MSG: TMP → DCT+UIS:** Responses to outstanding questions. Both teams are **unblocked** — start work in parallel.
+>
+> **On 1DCT (`uis help` fast path):** **Agreed.** `uis help`, `uis --help`, `uis -h`, and `uis` (no args) should bypass `uis_bridge_check` and print local DCT-specific usage. It's local output; failing because the container is down is friction without value. Suggested implementation: special-case the first arg in the shim, before sourcing the bridge for the bypass case OR before calling `uis_bridge_check` for the general case. DCT decides which is cleaner.
+>
+> **On 4UIS + 4DCT (`cluster.database_url` deprecation):** **Agreed — deprecate for one cycle, remove in Phase 2.** Both teams' reasoning is sound:
+> - Decouples DCT 1.9 from UIS 1.10 shipping order
+> - Cost is one extra field in JSON for one release
+> - Lower-risk rollout with no behavior change for any unknown caller
+>
+> Concrete: UIS 1.10 keeps `cluster.database_url` in the response **and** adds the new fields (`secret_name`, `secret_namespace`, `env_var`). DCT 1.9 reads only the new fields. Phase 2 includes a task to remove `cluster.database_url`.
+>
+> **On 3UIS + 3DCT (pass both flags explicitly):** **Agreed with DCT's plan.** `--namespace` and `--secret-name-prefix` are conceptually different and can legitimately have different values. DCT passes both explicitly; UIS doesn't default one to the other.
+>
+> **On 2UIS (doc inconsistency):** **Confirmed — the manifest is the source of truth.** Item 1.10 has been corrected: secrets are named `-db`, not `-postgresql`. The `-db` suffix is hardcoded for now (Phase 1 only ships PostgreSQL). When more services are added in Phase 2, we'll revisit naming (probably `-` like `-db`, `-cache`, `-auth`).
+>
+> **3UIS: done by TMP** — answered.
+> **4UIS: done by TMP** — answered.
+> **1DCT: done by TMP** — answered.
+> **4DCT: done by TMP** — answered.
+>
+> **8MSG: UIS → DCT+TMP:** **Item 1.10 shipped** — helpers-no/urbalurba-infrastructure#121 merged.
+>
+> What's now in `uis-provision-host:local` (and ghcr.io/helpers-no/uis-provision-host:latest after CI):
+> - `uis configure postgresql --app --database --namespace --secret-name-prefix --json` — creates namespace if missing, creates `-db` secret with `DATABASE_URL` key
+> - JSON response includes `secret_name`, `secret_namespace`, `env_var: "DATABASE_URL"` when `--namespace` is set
+> - `cluster.database_url` retained for one deprecation cycle (per 4DCT)
+> - Backward compat: existing callers without the flags still work
+> - Idempotent: re-run rotates the password and updates the secret in place
+> - Both flags required together; missing one returns `phase: usage` error
+>
+> **Tests:** 9 unit + 19 integration tests, all passing. Tester verification round still pending.
+>
+> **DCT — you are unblocked for Item 1.9.** Pass both flags via `uis-bridge configure`:
+> - `--namespace ` (DCT computes from `params.subdomain ?? params.app_name ?? REPO_NAME` per 3DCT)
+> - `--secret-name-prefix ` (matches the existing `{{REPO_NAME}}-db` template manifest convention)
+>
+> The new JSON fields (`secret_name`, `secret_namespace`, `env_var`) are ready for `uis-bridge.sh` to consume. `cluster.database_url` is still there during the deprecation period if you need it as a fallback.
+>
+> **TMP — Item 1.10 done.** README rewrites that depend on the new flags can proceed.
+>
+> **9MSG: UIS → DCT:** **Tester verification complete — all 6 steps PASS** (talk.md). DCT Item 1.9 is formally unblocked.
+>
+> Verified end-to-end on a live cluster:
+> 1. Backward compat: existing callers without the flags still work (no `secret_*` fields in JSON)
+> 2. New flags emit `secret_name`, `secret_namespace`, `env_var: "DATABASE_URL"` in JSON
+> 3. Namespace + `-db` secret actually created in K8s with cluster URL in `DATABASE_URL`
+> 4. Idempotent re-run rotates the password AND updates the secret (`already_configured` envelope still includes the secret fields)
+> 5. Missing one flag → `{"status":"error","phase":"usage",...}` JSON on stdout, exit non-zero
+> 6. Namespace auto-created when it doesn't exist
+>
+> **For DCT 1.9:** the contract you need is:
+> ```bash
+> uis-bridge configure postgresql \
+> --app \
+> --database \
+> --namespace \
+> --secret-name-prefix \
+> --init-file - --json
+> ```
+> Read `secret_name`/`secret_namespace`/`env_var` from the JSON response. `cluster.database_url` is still there during the deprecation period as a safety net.
+>
+> **All Phase 1 work is unblocked.** Recommended start order:
+> - **Day 1 (parallel):** TMP starts 1.1-1.4 + 1.7. DCT starts 1.8. UIS starts 1.10.
+> - **Day 2-3:** DCT 1.8 lands → TMP starts 1.6 (postgresql-demo README rewrite). UIS 1.10 lands → DCT starts 1.9 + 1.11 coordination.
+> - **Day 3-5:** TMP rewrites python-basic-webserver-database README (1.5, 1.12, 1.13) using the now-available shim and updated configure flow.
+>
+> Each team should create their own PLAN file in their own repo, referencing this investigation. TMP creates the TMP plan in this repo.
+>
+> **10MSG: DCT → TMP+UIS:** **DCT Items 1.8 and 1.9 shipped in v1.7.34** — PR helpers-no/devcontainer-toolbox#90 merged.
+>
+> **What's in v1.7.34:**
+>
+> **1.8 — `uis` shim:**
+> - New `/usr/local/bin/uis` (symlink to `.devcontainer/manage/uis.sh`) — bare `uis ...` commands work from inside DCT
+> - Routes through `uis-bridge.sh` to `uis-provision-host` container
+> - Auto-detects TTY/stdin/no-TTY mode:
+> - Interactive (`uis status`, `uis connect postgresql mydb`) → `docker exec -it`
+> - Piped stdin (`echo SQL | uis configure --init-file -`) → `docker exec -i`
+> - Non-TTY no stdin (`uis status > out.txt`) → plain `docker exec`
+> - **Help fast path**: `uis`, `uis help`, `uis --help`, `uis -h` all work without `uis-provision-host` running. If the container is up, forwards to real `uis help`. If not, prints local DCT-flavoured help with a "container not running" hint.
+> - New `uis_bridge_run_tty()` function added to `lib/uis-bridge.sh`
+>
+> **1.9 — `dev-template-configure` passes the new flags:**
+> - Sources `lib/git-identity.sh`, calls `detect_git_identity` early to populate `GIT_REPO`
+> - Resolves namespace per 3DCT: `${PARAMS[subdomain]:-${PARAMS[app_name]:-$GIT_REPO}}`
+> - Passes both `--namespace ` and `--secret-name-prefix $GIT_REPO` when `GIT_REPO` is set
+> - `uis_bridge_configure` parses new JSON fields into globals: `UIS_SECRET_NAME`, `UIS_SECRET_NAMESPACE`, `UIS_SECRET_ENV_VAR`
+> - Reset on each call so callers never read stale values
+> - Parses fields from both `ok` and `already_configured` responses
+> - **Backward compat**: if no git remote (no `GIT_REPO`), the new flags are not passed and UIS works in legacy mode (no K8s secret created). The completion message falls back to writing `.env.cluster`.
+>
+> **New output from `dev-template-configure`:**
+> ```
+> 📦 Configuring postgresql...
+> ✅ postgresql — configured
+> → .env: DATABASE_URL=postgresql://...@host.docker.internal:35432/mydb (local)
+> → K8s Secret: my-app-db in namespace my-app (cluster)
+> ```
+>
+> **Cleaned up while we were in there:**
+> - Removed outdated `install-tool-docker-cli.sh` reference in `uis_bridge_check` error message — Docker is provided by the `docker-outside-of-docker` devcontainer feature now.
+>
+> **DCT Phase 1 status: code complete, ships in v1.7.34.** CI is building the image now. Phase 3 of `PLAN-p1-dct-shim.md` (E2E test in a fresh devcontainer with TMP's rewritten templates) is the only remaining DCT work — blocked on TMP's README rewrites (1.5, 1.6).
+>
+> **TMP — you are unblocked for the README rewrites.** The shim is in v1.7.34. Once that image is on ghcr.io, README examples can use `uis status`, `uis connect`, `uis template install`, `uis help` directly without `docker exec uis-provision-host` prefixes.
+>
+> **UIS — no further DCT asks for Phase 1.** Thanks for the fast turnaround on PR #121.
+
+---
+
+## Context
+
+Real user testing of `python-basic-webserver-database` (with `postgresql-demo` inspected during the same session) confirmed that the full architecture works end-to-end: DCT → uis-bridge → UIS → PostgreSQL. The Flask app runs, `/tasks` returns the seeded rows from the cluster database.
+
+But the developer experience has many rough edges. The current READMEs assume the reader is inside the UIS provision-host (wrong — they're in DCT). `dev-template-configure` writes credential files to the project root (risky). Several walkthroughs break when followed literally.
+
+This investigation captures the problems and proposes a phased fix plan.
+
+**Live pages:**
+- https://tmp.sovereignsky.no/docs/templates/basic-web-server-database/python-basic-webserver-database (the one tested)
+- https://tmp.sovereignsky.no/docs/templates/demo/postgresql-demo (inspected during testing)
+
+---
+
+## How to read this investigation
+
+Four parts, in order:
+
+1. **Decision: What ships when** — the phase plan
+2. **Phase 1: Foundation (ship now)** — confirmed fixes + enabling tooling
+3. **Phase 2: Improvements (after Phase 1)** — quality-of-life, composability
+4. **Phase 3: Polish** — cosmetic
+5. **Future considerations** — explicitly out of scope for this investigation
+6. **Reference material** — the canonical workflow, deploy-time data flow, and the original full issue list
+
+The reader looking for "what should we do first?" only needs parts 1 and 2.
+
+---
+
+# Part 1: Decision — What Ships When
+
+## The phase plan
+
+The problems and proposals fall into three shipping phases plus a "future" bucket. Phase 1 is the minimum viable fix that unblocks real users. Phase 2 adds quality-of-life. Phase 3 is cosmetic. Future is explicitly deferred.
+
+| Phase | Scope | Why this phase |
+|---|---|---|
+| **Phase 1** | Fix the bugs users actually hit. Ship the minimum tooling that unlocks clean README rewrites. | Real-user testing confirmed all Phase 1 issues. Blocking or broken without the fix. |
+| **Phase 2** | Improve the workflow with features that aren't blocking but matter for daily use. | Depends on Phase 1. Not urgent. |
+| **Phase 3** | Polish — logos, tags, cosmetics. | Nice to have, can ship whenever. |
+| **Future** | Speculation. Generalised env var systems, reusable data libraries. | No concrete use case yet. Defer until a real need surfaces. |
+
+## What's not in scope for this investigation
+
+These are noted but explicitly deferred:
+
+- **Generalised env var / secrets file** (`config/env.yaml`-style declaration) — premature. We have one template with `requires`. Design it when we have three.
+- **Reusable data libraries** (cross-template data sets like the Authentik blueprint with 11 users) — pure speculation. No template needs this yet.
+- **`dev-template verify` command** — nice idea, but `uis connect ` via the shim covers 80% of the value with zero new code.
+- **Deep subcommand refactors** (`dev-template list`, `dev-template info`, etc.) — the current commands work; rename later when a second subcommand is needed.
+
+If real needs surface during or after Phase 1, we'll plan them then.
+
+## The foundation decision: ship D1 (uis shim) first
+
+**D1 (the `uis` shim in DCT) is the single most impactful change in Phase 1.** It's ~15 lines of bash. It unlocks:
+
+- **A3** (postgresql-demo README rewrite using bare `uis ...` commands)
+- **B2** (Verify section for python-basic-webserver-database)
+- **B8** (prerequisite check that actually works from DCT)
+- **C2** (the central "uis: command not found" UX problem)
+- **C5** (UIS container running check)
+
+Without D1, every README rewrite needs verbose `docker exec uis-provision-host uis ...` everywhere, which we then have to rewrite again when D1 ships. **Ship D1 first, then rewrite the READMEs.**
+
+## The deployment decision: minimum D5
+
+The python-basic-webserver-database template already has a `secretKeyRef` in its deployment.yaml pointing at a `-db` secret. The secret doesn't exist in the cluster, so the pod would crash-loop if deployed.
+
+**Phase 1 ships the minimum fix**: `uis configure` accepts a `--namespace` flag and creates a single secret per service in that namespace. DCT passes `--namespace ` when calling configure. The secret name is deterministic from `params.app_name`.
+
+**Phase 2 generalises** — if multiple templates need different secret structures, revisit.
+
+Full details are in Part 2.
+
+---
+
+# Part 2: Phase 1 — Foundation (ship now)
+
+The goal: a real user can install `python-basic-webserver-database`, run `dev-template-configure`, develop locally, push to GitHub, and see the app running at `.localhost` — without hitting any of the walls user testing found.
+
+Work is grouped by team so PRs can ship in parallel. Within each team, items are ordered by dependency.
+
+## Phase 1 — TMP work (can start immediately, no external dependencies)
+
+### 1.1 Fix the MDX generator's install-command routing (D6, was A2)
+
+`scripts/generate-docs-markdown.sh` hardcodes `dev-template ` for every template's install command. Wrong for UIS stack templates (`context: uis`) which need `uis template install `.
+
+**Fix**: one `if [[ "$context" == "uis" ]]` branch in the generator.
+
+**Solves**: A2 (postgresql-demo TemplateHeader showing the wrong command).
+
+### 1.2 Fix MDX generator's duplicated abstract/summary (C1)
+
+The generator embeds both the TemplateHeader description and a separate "## Summary" section. Then the README adds its own intro paragraph. Same content three times.
+
+**Fix**: drop the separate "## Summary" section from the generator; let TemplateHeader + README intro do the job.
+
+### 1.3 Add `.gitignore` to templates with `requires` (C6)
+
+Neither of our current templates has a `.gitignore`. With `dev-template-configure` writing `.env*` files to the project root, credentials could end up in git.
+
+**Fix**: add `.gitignore` to both templates with at minimum:
+
+```gitignore
+.env
+.env.*
+.venv/
+__pycache__/
+*.pyc
+```
+
+Every template with `requires` should ship a `.gitignore`. Add to `readme-structure.md` as a requirement.
+
+### 1.4 Add `.vscode/settings.json` for uv (B6)
+
+`uv venv` doesn't install `pip`. VS Code's Python extension defaults to `pip list` and shows an error. The extension log explicitly says to enable `python-envs.alwaysUseUv`.
+
+**Fix**: ship a `.vscode/settings.json` in every Python template:
+
+```json
+{
+ "python-envs.alwaysUseUv": true
+}
+```
+
+### 1.5 README rewrite — `python-basic-webserver-database` (B1, B3, B4, B5, B7, B8)
+
+This is the big one. The current README has:
+- No mention of `dev-template python-basic-webserver-database` (the first step). **B1**
+- No content from `template-info.yaml` or `config/init-database.sql` shown inline. **B3**
+- No description of what the app does, what endpoints exist, what you'll see. **B4**
+- Says `pip install` — should use `uv venv && uv pip install` (DCT ships `uv`). **B5**
+- Has "Docker Build" and "Kubernetes Deployment" sections that document a manual flow bypassing GitHub Actions + ArgoCD. **B7**
+- Tells users to run `uis template install postgresql-demo` as a prerequisite — wrong, and the command isn't available from DCT anyway. **B8**
+
+**Fix**: rewrite the README around the canonical workflow (see Part 6). Every issue above collapses into a single coordinated rewrite. Wait for D1 and minimum D5 to land before the final pass — the verify section depends on the shim.
+
+### 1.6 README rewrite — `postgresql-demo` (A3, A4)
+
+- Drops the "From the UIS provision-host:" prefix; commands work from DCT via the shim. **A3**
+- Adds a "Try this with" section linking to `python-basic-webserver-database`. **A4**
+
+Wait for D1 to land.
+
+### 1.7 Cross-cutting doc updates
+
+**C5 — "Before you start" section in templates with `requires`**:
+
+Every template with `requires` should have a prerequisite section:
+
+```markdown
+## Before you start
+
+This template uses UIS. Verify UIS is running:
+
+```bash
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
+```
+
+You should see `Up X minutes`. If not, start UIS from the urbalurba-infrastructure repo.
+```
+
+Required by `readme-structure.md` for all templates with `requires`.
+
+## Phase 1 — DCT work (blocks the final README pass, ~1 day)
+
+### 1.8 D1: `uis` shim in DCT
+
+Add `/usr/local/bin/uis` as a thin wrapper that calls `uis-bridge.sh`. ~15 lines of bash plus one new function in the bridge.
+
+**The shim**:
+
+```bash
+#!/bin/bash
+# /usr/local/bin/uis — DCT shim for the UIS CLI
+set -e
+
+# Fast path for help/usage — bypass bridge check (per 1DCT + 7MSG).
+# These are local-output commands; failing because UIS is down is friction
+# without value.
+case "${1:-}" in
+ ""|"help"|"--help"|"-h")
+ cat <<'HELP'
+uis — DCT shim for the Urbalurba Infrastructure CLI
+
+Routes to the uis CLI inside the uis-provision-host container via docker exec.
+All UIS commands work the same as inside the UIS container.
+
+Usage:
+ uis help Show this help
+ uis [args...] Run any uis command
+ uis template install Install a UIS stack template
+ uis configure Configure a service
+ uis connect [db] Open an interactive client (psql, etc.)
+ uis expose Manage service port-forwarding
+ uis status Check service deployment status
+
+Requirements:
+ - Docker CLI installed (DCT default)
+ - The uis-provision-host container running (start UIS first)
+
+To verify UIS is reachable: uis status
+HELP
+ exit 0
+ ;;
+esac
+
+source /opt/devcontainer-toolbox/manage/lib/uis-bridge.sh
+
+uis_bridge_check || exit 1
+
+if [ -t 0 ] && [ -t 1 ]; then
+ uis_bridge_run_tty "$@"
+elif [ ! -t 0 ]; then
+ uis_bridge_run_stdin "$@"
+else
+ uis_bridge_run "$@"
+fi
+```
+
+**New function in `lib/uis-bridge.sh`**:
+
+```bash
+uis_bridge_run_tty() {
+ docker exec -it "$UIS_CONTAINER" uis "$@"
+}
+```
+
+**Result**: bare `uis template install ...`, `uis connect ...`, `uis expose ...`, `uis help`, etc. all work from DCT with the same UX as inside UIS. READMEs can drop the `docker exec uis-provision-host` noise. `uis help` works even when the UIS container is down.
+
+**Solves**: C2 (directly) + unlocks A3, B2, B8, C5 (README rewrites).
+
+**Estimated effort**: 0.5 day (per 5DCT).
+
+### 1.9 `dev-template-configure` passes `--namespace` and `--secret-name-prefix` to `uis configure`
+
+Once UIS accepts the new flags (1.10), DCT updates `dev-template-configure` to pass both. Final spec after team review (3UIS + 3DCT + 7MSG):
+
+- `--namespace` = `params.subdomain` ?? `params.app_name` ?? `${REPO_NAME}` (the friendly app name — the K8s namespace ArgoCD will deploy into)
+- `--secret-name-prefix` = `${REPO_NAME}` (matches the existing `{{REPO_NAME}}-db` placeholder in deployment manifests)
+
+Both flags are passed explicitly — no implicit defaulting in the bridge layer.
+
+After UIS 1.10 ships with the deprecation period, DCT 1.9 also updates the bridge to read the new fields (`secret_name`, `secret_namespace`, `env_var`) instead of `cluster.database_url`.
+
+**Estimated effort**: 0.5 day after UIS 1.10 lands (per 5DCT).
+
+## Phase 1 — UIS work (blocks DCT 1.9 and the deployment story, ~1-2 days)
+
+### 1.10 Minimum D5: `uis configure --namespace` creates the K8s Secret
+
+Smallest change that fixes the deploy-time crash-loop. Final spec after team review (5MSG, 7MSG):
+
+1. `uis configure ` accepts a new `--namespace ` flag
+2. `uis configure ` accepts a new `--secret-name-prefix ` flag (DCT passes `${REPO_NAME}` here, per 3DCT)
+3. If `--namespace` is **not** passed, behavior is unchanged (no secret created, response unchanged) — preserves backward compat for `uis template install postgresql-demo` and any unknown caller (per 1UIS)
+4. If `--namespace` is passed:
+ - Create the namespace if it doesn't exist (idempotent via `--dry-run=client -o yaml | kubectl apply -f -`)
+ - Create the secret named `-db` in that namespace with key `DATABASE_URL`. Matches the existing `python-basic-webserver-database/manifests/deployment.yaml` which references `{{REPO_NAME}}-db`. The `-db` suffix is hardcoded for Phase 1 (PostgreSQL only); Phase 2 will revisit when more services are added (probably `-` like `-db`, `-cache`, `-auth`)
+ - JSON response **adds** new fields: `secret_name`, `secret_namespace`, `env_var: "DATABASE_URL"`
+ - JSON response **keeps** `cluster.database_url` for one cycle (deprecation, per 4UIS + 4DCT + 7MSG). Phase 2 removes it.
+
+**Idempotency**: re-running `uis configure` updates the secret in place. Pod restart on secret update is deferred to Phase 2 (for now, DCT prints "restart your deployment to pick up the new secret" at the end of a successful configure).
+
+**Scope deliberately small**: only PostgreSQL for now. Other configurable services (Redis, MongoDB, Authentik) can follow the same pattern when they're actually used by a template.
+
+**Estimated effort**: 1 day (per 1UIS).
+
+### 1.11 Fix the secret-name mismatch in the template manifest
+
+Coordinate with TMP: decide the secret name convention. Two options:
+
+- **Option A**: `<{{REPO_NAME}}>-` — matches current manifest, UIS uses the repo name passed via a new flag
+- **Option B**: `<{{APP_NAME}}>-` where `APP_NAME` is a new placeholder resolved from `params.app_name` — requires DCT to substitute `{{APP_NAME}}` during install
+
+**Recommendation**: Option A for Phase 1. Minimal change — the template manifest already uses `{{REPO_NAME}}-db`. UIS just needs to receive the repo name and use it for the secret name. DCT can pass `--secret-name-prefix ` to `uis configure`.
+
+Option B is cleaner long-term but requires a new placeholder everywhere. Defer to Phase 2.
+
+## Phase 1 — Final integration
+
+After 1.8, 1.9, 1.10, 1.11 have landed, TMP can do the final README rewrites:
+
+### 1.12 Add Verify sections using the shim (B2)
+
+```markdown
+## Verify the database is set up
+
+```bash
+uis connect postgresql
+```
+
+Inside psql:
+
+```sql
+SELECT * FROM tasks;
+\q
+```
+
+You should see the 3 seeded rows.
+```
+
+### 1.13 Update the canonical workflow in READMEs
+
+Use the workflow from Part 6 as the template for every README.
+
+## Phase 1 — Summary of what ships
+
+| Item | Team | Depends on | Changes |
+|---|---|---|---|
+| 1.1 Generator install-command routing (D6) | TMP | nothing | 1 line in `generate-docs-markdown.sh` |
+| 1.2 Generator dedup (C1) | TMP | nothing | small change to generator |
+| 1.3 Add `.gitignore` to templates (C6) | TMP | nothing | 2 files |
+| 1.4 Add `.vscode/settings.json` (B6) | TMP | nothing | 1 file |
+| 1.5 README rewrite — python-basic-webserver-database | TMP | 1.8, 1.10 | 1 file |
+| 1.6 README rewrite — postgresql-demo | TMP | 1.8 | 1 file |
+| 1.7 "Before you start" section / `readme-structure.md` | TMP | 1.8 | 2 files + spec |
+| 1.8 D1: uis shim | DCT | nothing | ~15 lines bash + 1 function |
+| 1.9 DCT passes `--namespace` to UIS | DCT | 1.10 | small change in configure flow |
+| 1.10 UIS minimum D5 | UIS | nothing | ~50 lines in `uis configure` |
+| 1.11 Secret-name convention | coord | 1.10 | small DCT + UIS coordination |
+| 1.12 Add Verify sections | TMP | 1.8, 1.10 | part of 1.5, 1.6 |
+| 1.13 Canonical workflow in READMEs | TMP | 1.8, 1.10 | part of 1.5, 1.6 |
+
+**Parallelism**: 1.1-1.4, 1.8, and 1.10 can all start at the same time (different teams, no cross-dependencies). 1.5-1.7, 1.9, 1.11-1.13 are blocked on those landing.
+
+**Minimum viable ship order**:
+1. Day 1: TMP starts on 1.1-1.4 (no dependencies). DCT starts on 1.8. UIS starts on 1.10.
+2. Day 2-3: DCT finishes 1.8 → TMP can start 1.6 and 1.7. UIS finishes 1.10 → DCT starts 1.9 and 1.11 coordination.
+3. Day 4-5: Integration — TMP rewrites READMEs using the shim (1.5, 1.12, 1.13).
+
+---
+
+# Part 3: Phase 2 — Improvements (after Phase 1 ships)
+
+These are not blocking but improve the daily experience. Plan them after Phase 1 is in production and we have real feedback.
+
+## 2.1 `dev-template` subcommand refactor (D2)
+
+Refactor `dev-template-configure` into `dev-template configure`. Add other subcommands as they're needed: `list`, `info`, `install`, `register`.
+
+**Why Phase 2**: the current hyphenated command name works. This is cleanup that touches every doc — do it once when there's a second reason to touch those docs.
+
+## 2.2 `dev-template register` (D3 + D4)
+
+New DCT subcommand that registers the app with ArgoCD using values auto-detected from `git remote` and `template-info.yaml`. Requires a new `params.subdomain` field.
+
+**Why Phase 2**: `./uis argocd register` from the host works today. This is UX polish, not a blocker.
+
+**Depends on**: D2 (subcommand refactor)
+
+## 2.3 Move `.env*` files to `.devcontainer.secrets/env-vars/` (C7)
+
+DCT writes `.env` to the canonical secrets folder instead of the project root. Plus a committed symlink `.env → .devcontainer.secrets/env-vars/.env` so standard `load_dotenv()` keeps working.
+
+**Why Phase 2**: the Phase 1 fix (`.env` in project root + `.gitignore`) is safe. This refinement is cleaner but not urgent. Also has cross-platform concerns (Windows symlinks) that need investigation.
+
+## 2.4 Pod restart on secret update
+
+Deployments need to restart when the secret changes (otherwise they keep using cached env vars). Options:
+- DCT runs `kubectl rollout restart` after `uis configure`
+- Deployment manifest has a checksum annotation updated by DCT
+- Use [stakater/reloader](https://github.com/stakater/reloader) as a cluster add-on
+
+**Why Phase 2**: in Phase 1, re-running `dev-template-configure` returns `already_configured` and doesn't rotate the password (14MSG — idempotent). Rotation is the only case where the pod needs a restart. Defer until rotation is actually supported.
+
+## 2.5 End-user doc: `using-templates.md` (C8)
+
+Top-level doc explaining the full workflow (install → edit params → configure → run → register). `developer-setup-guide.md` links to it.
+
+**Why Phase 2**: Phase 1 fixes each README. Phase 2 gives users a single top-level reference.
+
+## 2.6 Multi-service template support
+
+When a second template needs `requires: [postgresql, redis]`, generalise the Phase 1 approach:
+- Multiple secrets, one per service
+- Or one combined secret per app
+- Decide based on the real template
+
+**Why Phase 2**: no template needs this today. Don't design for hypotheticals.
+
+---
+
+# Part 4: Phase 3 — Polish
+
+Low-priority cosmetic fixes. Ship anytime.
+
+- **A1**: Missing `postgresql-demo-logo.svg` — create or use placeholder
+- **A5**: JSON example in postgresql-demo README uses literal `` — add explanatory text
+- **B10**: `python-basic-webserver-database` reuses `python-basic-webserver-logo.svg` — create its own
+- **C3**: Tag scheme inconsistent across templates — decide a convention, document in `naming-conventions.md`
+- **C4**: Missing logos for BASIC_WEB_SERVER_DATABASE category and new templates — create proper SVGs
+
+---
+
+# Part 5: Future considerations (not scope for this investigation)
+
+These were explored during the investigation but deferred. They need real use cases before they're worth designing.
+
+## Generalised `config/env.yaml` declaration
+
+A common file format for declaring all env vars/secrets a template needs, with typed sources (`from: requires.X`, `from: params.Y`, `generate: random`, `value: "..."`). Would subsume Phase 1's single-secret-per-service approach.
+
+**Why deferred**: we have one template with `requires`. Design this when three templates exist with different needs.
+
+**Placeholder for when we revisit**: the `init:` field in template-info.yaml should accept an object (not just a path string) so it can grow backward-compatibly:
+
+```yaml
+# Phase 1 (simple)
+init: "config/init-database.sql"
+
+# Future (extended — backward-compatible)
+init:
+ file: "config/init-database.sql"
+```
+
+## Reusable data libraries / data templates
+
+Shared organisational state across templates — test users, reference tables, dashboards. Motivated by UIS's `073-authentik-1-test-users-groups-blueprint.yaml` (727 lines of Authentik users that no template can reuse).
+
+**Why deferred**: pure speculation. No template needs shared data yet. If and when a real use case emerges, this gets its own investigation.
+
+## `dev-template verify` command
+
+A DCT command that validates the full connection chain (port-forward + connection from DCT). Nice to have but `uis connect ` via the shim covers 80% of the value.
+
+**Why deferred**: complex to implement correctly across languages and services. The manual `uis connect` path works today.
+
+## Multi-service / multi-env-var templates
+
+Design for templates with `requires: [postgresql, redis, authentik]`. Currently Phase 1 handles one secret per service.
+
+**Why deferred**: no template needs this today.
+
+## Deep subcommand restructure
+
+`dev-template list`, `info`, `install`, `uninstall`, `update`, etc. Phase 2 introduces `configure` and `register`; further subcommands come when they're needed.
+
+**Why deferred**: YAGNI.
+
+---
+
+# Part 6: Reference material
+
+## The canonical workflow (after Phase 1 ships)
+
+This is the "happy path" the docs should follow. Every UIS-dependent app template README should match this structure.
+
+### Step 1: Install the template
+
+```bash
+dev-template python-basic-webserver-database
+```
+
+What happens:
+- DCT fetches the template from the registry
+- Copies all files to the current project directory
+- Replaces `{{GITHUB_USERNAME}}` and `{{REPO_NAME}}` placeholders in manifests/workflows
+- Writes `template-info.yaml` to the project root
+
+### Step 2: Verify UIS is running
+
+```bash
+uis help # Confirms the shim + UIS container are working
+uis status postgresql # Confirms PostgreSQL is deployed
+```
+
+If PostgreSQL isn't deployed:
+
+```bash
+uis deploy postgresql
+```
+
+**You usually don't need this step** — `dev-template-configure` does an automatic `deploy_check` and tells you exactly what to run if anything's missing.
+
+### Step 3: Edit `template-info.yaml` and optionally `config/init-database.sql`
+
+Open `template-info.yaml`, find the `params:` section, set your values:
+
+```yaml
+params:
+ app_name: "my-cool-app"
+ database_name: "my_cool_app_db"
+```
+
+Optionally edit `config/init-database.sql` to customise the schema.
+
+The README should show both files inline (per **1.5** / B3).
+
+### Step 4: Run `dev-template-configure`
+
+```bash
+dev-template-configure
+```
+
+What happens:
+- Reads `template-info.yaml`, validates params
+- Substitutes `{{ params.* }}` in init files
+- Calls `uis configure postgresql --app --database --namespace --init-file -` via the bridge
+- UIS creates the DB, applies init SQL, creates the K8s Secret in the namespace, auto-exposes the port
+- DCT writes `.env` to the project root (gitignored)
+
+If anything fails, the structured JSON error from UIS tells you exactly what went wrong.
+
+### Step 5: Verify the database
+
+```bash
+uis connect postgresql my_cool_app_db
+```
+
+Inside psql:
+
+```sql
+SELECT * FROM tasks;
+\q
+```
+
+You should see the seeded rows.
+
+### Step 6: Run the app
+
+```bash
+uv venv
+source .venv/bin/activate
+uv pip install -r requirements.txt
+python app/app.py
+```
+
+Open in your browser (VS Code's Ports tab auto-forwards port 3000):
+
+- `http://localhost:3000/` — Home
+- `http://localhost:3000/tasks` — JSON list of seeded rows
+- `http://localhost:3000/health` — DB connectivity check
+
+### Step 7: Deploy to the cluster
+
+```bash
+git push # GitHub Actions builds the image
+./uis argocd register # From the host, one-time per project
+```
+
+App is at `http://.localhost`. ArgoCD auto-deploys on every push.
+
+(Phase 2 replaces step 7 with `dev-template register` — see 2.2.)
+
+## Deploy-time data flow (after minimum D5 ships)
+
+The K8s pod gets `DATABASE_URL` via a Kubernetes Secret that UIS created at configure time. The Docker image is credential-free.
+
+**Phase 1 (configure time):**
+
+```
+Developer's DCT devcontainer
+ └─ dev-template-configure
+ └─ uis_bridge_configure postgresql
+ --app my-app
+ --database my_app_db
+ --namespace my-app
+ --secret-name-prefix my-app
+ --init-file - < config/init-database.sql
+ │
+ └─ docker exec uis-provision-host uis configure ...
+
+UIS provision-host
+ ├─ deploy_check (verify PostgreSQL running)
+ ├─ Create database + user in PostgreSQL
+ ├─ Apply init SQL via psql
+ ├─ Auto-expose service port
+ ├─ kubectl create namespace my-app (idempotent)
+ ├─ kubectl create secret my-app-db in my-app namespace
+ └─ Return JSON with local URL + secret_name/namespace
+
+K8s cluster after Phase 1:
+ Namespace: my-app
+ └─ Secret: my-app-db
+ └─ DATABASE_URL=postgresql://...@postgresql.default.svc.cluster.local:5432/my_app_db
+ Namespace: default
+ └─ Pod: postgresql-0 (with new database + user)
+
+Developer's project:
+ └─ .env # Local URL for local dev (gitignored)
+```
+
+**Phase 2 (deploy time):**
+
+```
+git push
+ └─ GitHub Actions builds credential-free image → GHCR
+
+./uis argocd register my-app
+ └─ ArgoCD watches the repo
+
+ArgoCD detects new commit
+ ├─ Fetches manifests/deployment.yaml (has secretKeyRef: my-app-db)
+ └─ Applies to namespace my-app (already exists)
+
+Kubelet starts pod in my-app namespace
+ ├─ K8s injects DATABASE_URL from Secret into pod env
+ ├─ Flask reads os.environ['DATABASE_URL']
+ └─ Connects to postgresql.default.svc.cluster.local:5432
+
+Traefik routes my-app.localhost → pod
+```
+
+**Key point**: `./uis argocd register` (or `dev-template register` in Phase 2) **does not touch the connection string**. The Secret UIS created in configure-time is already in the namespace when ArgoCD deploys.
+
+## The issue list (original, for reference)
+
+Kept for traceability. Phase 1 includes items marked **P1**. Phase 2 items are **P2**. Phase 3 items are **P3**. Deferred items are **F** (Future).
+
+### Section A — postgresql-demo
+
+| ID | Issue | Phase |
+|---|---|---|
+| A1 | Missing logo file | P3 |
+| A2 | Wrong install command in TemplateHeader | **P1** (via 1.1) |
+| A3 | README assumes UIS provision-host context | **P1** (via 1.6) |
+| A4 | No link to consumer-side template | **P1** (via 1.6) |
+| A5 | JSON example uses literal `` | P3 |
+
+### Section B — python-basic-webserver-database
+
+| ID | Issue | Phase |
+|---|---|---|
+| B1 | No `dev-template install` step in README | **P1** (via 1.5) |
+| B2 | No "Verify it worked" section | **P1** (via 1.12) |
+| B3 | README doesn't show `template-info.yaml` / init SQL content | **P1** (via 1.5) |
+| B4 | README lacks description of what the Python program does | **P1** (via 1.5) |
+| B5 | README uses `pip install` instead of `uv` | **P1** (via 1.5) |
+| B6 | Missing `.vscode/settings.json` for `alwaysUseUv` | **P1** (via 1.4) |
+| B7 | "Docker Build" / "Kubernetes Deployment" sections describe wrong workflow | **P1** (via 1.5) |
+| B8 | README incorrectly tells users to install `postgresql-demo` as prerequisite | **P1** (via 1.5) |
+| B10 | Logo reused from `python-basic-webserver` | P3 |
+
+### Section C — Cross-cutting
+
+| ID | Issue | Phase |
+|---|---|---|
+| C1 | Duplicated abstract/summary across TemplateHeader, Summary section, README intro | **P1** (via 1.2) |
+| C2 | `uis: command not found` from DCT — central UX problem | **P1** (via 1.8 D1 shim) |
+| C3 | Tag scheme not consistent across templates | P3 |
+| C4 | Missing logos for new templates and category | P3 |
+| C5 | README doesn't say UIS provision-host must be running | **P1** (via 1.7) |
+| C6 | `.env*` files could end up in git | **P1** (via 1.3) |
+| C7 | `.env*` should live in `.devcontainer.secrets/env-vars/` | P2 (2.3) |
+| C8 | No end-user doc explains `template-info.yaml` editing | P2 (2.5) |
+
+### Section D — Architectural proposals
+
+| ID | Proposal | Phase |
+|---|---|---|
+| D1 | `uis` shim in DCT | **P1** (1.8 — foundation) |
+| D2 | `dev-template` subcommand refactor | P2 (2.1) |
+| D3 | `dev-template register` with auto-detection | P2 (2.2) |
+| D4 | `params.subdomain` in `template-info.yaml` | P2 (with 2.2) |
+| D5 minimum | `uis configure --namespace` creates K8s Secret | **P1** (1.10) |
+| D5 full | Multi-service, pod restart, full secret lifecycle | P2 (2.4, 2.6) |
+| D6 | MDX generator: install command by context | **P1** (1.1) |
+| D7 | Generalised `config/env.yaml` | **F** (Future) |
+| D8 | Reusable data libraries | **F** (Future) |
+
+### Cross-team summary (Phase 1 only)
+
+| Item | TMP | DCT | UIS |
+|---|---|---|---|
+| 1.1 Generator routing (D6) | ✅ owns | | |
+| 1.2 Generator dedup (C1) | ✅ owns | | |
+| 1.3 `.gitignore` (C6) | ✅ owns | | |
+| 1.4 `.vscode/settings.json` (B6) | ✅ owns | | |
+| 1.5 Python README rewrite | ✅ owns | | |
+| 1.6 postgresql-demo README rewrite | ✅ owns | | |
+| 1.7 "Before you start" (C5) | ✅ owns | | |
+| 1.8 D1 uis shim | | ✅ owns | |
+| 1.9 DCT passes `--namespace` | | ✅ owns | |
+| 1.10 Minimum D5 | | | ✅ owns |
+| 1.11 Secret name coordination | ✅ spec | ✅ impl | ✅ impl |
+| 1.12 Verify sections | ✅ owns | | |
+| 1.13 Canonical workflow in READMEs | ✅ owns | | |
+
+---
+
+## Next steps
+
+- [ ] Review this investigation and confirm the phase split
+- [ ] Create a separate PLAN for each of the three Phase 1 workstreams:
+ - **PLAN-p1-tmp-fixes.md** — TMP-only work (1.1-1.7, no external dependencies)
+ - **PLAN-p1-dct-shim.md** — DCT work (1.8, 1.9) — cross-team coordination with DCT
+ - **PLAN-p1-uis-secret.md** — UIS work (1.10, 1.11) — cross-team coordination with UIS
+- [ ] Once Phase 1 ships, plan Phase 2 based on real feedback
+- [ ] Phase 3 and Future items stay on backlog, no immediate planning
+
+**First PR targets**: 1.1, 1.3, 1.4 from TMP; 1.8 from DCT; 1.10 from UIS — all independent, all safe to ship in parallel.
diff --git a/website/docs/contributors/readme-structure.md b/website/docs/contributors/readme-structure.md
index 61ae09c..20692cf 100644
--- a/website/docs/contributors/readme-structure.md
+++ b/website/docs/contributors/readme-structure.md
@@ -12,9 +12,9 @@ These sections must be present (checked by `validate-docs.sh`):
| Section | Purpose |
|---------|---------|
-| **Quick Start** | Numbered copy-paste steps to run the app |
-| **Prerequisites** | What's needed (tools are auto-installed) |
-| **Project Structure** | Directory tree showing deployed layout |
+| **Quick Start** | Numbered copy-paste steps to install, configure, and run the template |
+| **Prerequisites** | What needs to exist before the template works (DCT, UIS provision-host running, services deployed in cluster) |
+| **Project Structure** | Directory tree showing the layout the user sees after `dev-template ` |
## Optional Sections
@@ -22,12 +22,29 @@ These are recommended but not enforced:
| Section | Purpose |
|---------|---------|
-| **Development** | How to edit, test, and debug |
-| **Docker Build** | How to build the container image |
-| **Kubernetes Deployment** | How to deploy to K8s |
+| **Development** | How to edit, test, and debug the app |
| **CI/CD** | How the GitHub Actions workflow works |
+| **Try this with** | Cross-references to related/companion templates |
-## Template
+## Required Sections for templates with `requires`
+
+If your template declares `requires:` in `template-info.yaml` (i.e., it needs UIS services like PostgreSQL, Redis, Authentik), the README must include these additional sections:
+
+| Section | Purpose |
+|---------|---------|
+| **What this is** | Brief description of the app — what it does, what endpoints it has, what the user will see when it runs |
+| **Prerequisites** (UIS-aware) | Verify the UIS provision-host container is running. Mention that DCT v1.7.34+ provides the `uis` shim so commands like `uis status` and `uis connect` work from inside DCT. |
+| **Inline file content** | Embed `template-info.yaml` (at minimum the `params:` and `requires:` sections) and the init file(s) (e.g., `config/init-database.sql`) directly in the README so users see the format without opening files |
+| **Verify it worked** | A DB-level (or service-level) verify command that doesn't require running the app — for PostgreSQL, `uis connect ` is the canonical pattern |
+
+## Removed sections
+
+These sections used to be optional but should NOT be added to new templates:
+
+- **~~Docker Build~~** — manual `docker build` and `docker run` bypass the GitHub Actions pipeline. New templates should not document the manual flow.
+- **~~Kubernetes Deployment~~** — manual `kubectl apply` bypasses ArgoCD. New templates should use a single "Deploy" section that walks through `git push` → GitHub Actions → ArgoCD.
+
+## Template — basic app (no `requires`)
```markdown
# Template Display Name
@@ -36,9 +53,9 @@ Brief one-line description of what this template provides.
## Quick Start
-1. Update your terminal (tools were installed):
+1. Create the project from this template:
```bash
- source ~/.bashrc
+ dev-template
```
2. Run the app:
@@ -77,30 +94,134 @@ After installation, your project contains:
- Describe hot reload behavior if applicable
- The `/` endpoint returns "Hello World" with template name and time/date
-## Docker Build
+## Deploy to your local cluster
+
+1. `git push` — GitHub Actions builds and pushes the image
+2. `./uis argocd register ` — register with ArgoCD (one-time)
+3. Access the app at `http://.localhost`
+```
+
+## Template — app with `requires` (database, auth, etc.)
+
+For templates that depend on UIS services, follow the pattern from `python-basic-webserver-database`:
+
+```markdown
+# Template Display Name
+
+Brief one-line description.
+
+## What this is
+
+A small but complete application:
+
+| Endpoint | Method | Returns |
+|---|---|---|
+| `/` | GET | ... |
+| `/items` | GET | ... |
+
+The app **requires** `` and exits if it's missing.
+
+## Prerequisites
+
+This template uses UIS to configure . Verify the UIS provision-host container is running:
```bash
-docker build -t .
-docker run -p :
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
```
-## Kubernetes Deployment
+Inside DCT v1.7.34+ you also have the `uis` shim. If `` isn't deployed, `dev-template-configure` will tell you what to run.
+
+## Quick Start
+### 1. Install the template
```bash
-kubectl apply -k manifests/
+dev-template
```
-The app will be accessible at `http://.localhost` after ArgoCD registration.
+### 2. Edit `template-info.yaml`
+Open `template-info.yaml`, find the `params:` section, set your values:
+
+```yaml
+params:
+ app_name: "my-cool-app"
+ database_name: "my_cool_app_db"
+```
+
+The full `template-info.yaml` declares the dependency:
+```yaml
+requires:
+ - service:
+ config:
+ ...
+ init: "config/init-."
+```
+
+### 3. (Optional) Customise `config/init-.`
+```
+-- The init file content goes here, embedded in the README
+```
+
+All statements should be idempotent so re-running configure is safe.
+
+### 4. Run `dev-template-configure`
+```bash
+dev-template-configure
+```
+
+### 5. Verify the database (or service)
+```bash
+uis connect
+```
+
+### 6. Run the app
+```bash
+uv venv
+source .venv/bin/activate
+uv pip install -r requirements.txt
+python app/app.py
+```
+
+### 7. Open in your browser
+VS Code's Ports tab auto-forwards the port. Click the globe icon next to it.
+
+## Project Structure
+
+After installation, your project contains:
+
+```plaintext
+├── app/
+├── config/
+│ └── init-. # Schema/config (applied by dev-template-configure)
+├── manifests/
+├── .vscode/
+│ └── settings.json # IDE settings
+├── .gitignore # Excludes .env*, .venv/, etc.
+├── template-info.yaml # Template metadata
+└── README-.md # This file
+```
+
+## Development
+
+...
+
+## Deploy to your local cluster
+
+1. `git push` — GitHub Actions builds and pushes the image
+2. `./uis argocd register ` — register with ArgoCD
+3. Access the app at `http://.localhost`
+
+The Kubernetes Secret containing service credentials is created automatically by `dev-template-configure` (via UIS) and referenced from `manifests/deployment.yaml` via `secretKeyRef`. You don't need to create it manually.
-## CI/CD
+## Try this with
-The GitHub Actions workflow automatically builds and pushes the Docker image
-to GitHub Container Registry when changes are pushed to the main branch.
+- [Companion or related templates](..//) — describe how they compose
```
## Notes
- The **Quick Start** section is the most important — users see it first after installation
-- **Project Structure** should show the deployed layout (what the user sees after `dev-template` runs), not the template source layout
+- **Project Structure** should show the layout the user sees after `dev-template ` runs, not the template source layout
- Keep descriptions concise — the README is a quick reference, not a tutorial
-- Don't include tool installation instructions — `dev-template.sh` handles this via `TEMPLATE_TOOLS`
+- Don't include tool installation instructions — `dev-template ` and `dev-template-configure` handle this via `tools:` in `template-info.yaml`
+- For templates with `requires:`, **embed the file contents** for `template-info.yaml` and init files in the README. Users need to see what they're editing.
+- **Don't document manual `docker build` or `kubectl apply` workflows.** They bypass GitHub Actions + ArgoCD and aren't the standard platform workflow.
diff --git a/website/docs/templates/basic-web-server-database/python-basic-webserver-database.mdx b/website/docs/templates/basic-web-server-database/python-basic-webserver-database.mdx
index 0c876d2..388219b 100644
--- a/website/docs/templates/basic-web-server-database/python-basic-webserver-database.mdx
+++ b/website/docs/templates/basic-web-server-database/python-basic-webserver-database.mdx
@@ -26,109 +26,240 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-python"
/>
-## Summary
-A Flask web server that reads from a PostgreSQL database via DATABASE_URL. Includes a sample tasks table, Docker containerization, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow. This is the consumer-side companion to the postgresql-demo UIS stack template — run that first to deploy PostgreSQL, then dev-template configure on this template to create the database and wire up the connection.
+A minimal Flask web server that connects to PostgreSQL and reads from a `tasks` table. The full producer/consumer flow:
----
+- **PostgreSQL** runs in your UIS-managed Kubernetes cluster (deployed once during cluster setup, or via `uis deploy postgresql`).
+- **`dev-template-configure`** creates a per-app database and user, applies the init SQL, and writes `DATABASE_URL` to `.env` for local dev.
+- **The Flask app** reads `DATABASE_URL` from `.env`, connects to PostgreSQL via `host.docker.internal:35432` (the local port forward UIS exposes), and serves the seeded data.
+
+## What this is
+
+A small but complete Flask application:
+
+| Endpoint | Method | Returns |
+|---|---|---|
+| `/` | GET | Plain-text greeting with the template name and current time |
+| `/tasks` | GET | JSON list of rows from the `tasks` table (the seeded data, plus anything you've added) |
+| `/health` | GET | `{"status": "ok", "database": "connected"}` if the DB is reachable, or a 503 if not |
+
+The app **requires** `DATABASE_URL` and exits immediately with a clear error if it's missing — there's no fallback. This is intentional: the template demonstrates the producer/consumer pattern where credentials always come from `dev-template-configure`.
+
+## Prerequisites
+
+This template uses UIS to configure PostgreSQL. Verify the UIS provision-host container is running:
+```bash
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
+```
-A minimal Flask web server that connects to PostgreSQL and reads from a `tasks` table. This template demonstrates the full producer/consumer flow:
+You should see `Up X minutes`. If not, start UIS from the `urbalurba-infrastructure` repo. Inside DCT (devcontainer-toolbox v1.7.34 or later) you also have the `uis` shim, which routes `uis ...` commands to the provision-host automatically.
-- **Producer (UIS):** `uis template install postgresql-demo` deploys PostgreSQL to the cluster
-- **Consumer (this template):** `dev-template configure` creates a per-app database, runs the init SQL, and writes `DATABASE_URL` to `.env`
-- **App:** reads `DATABASE_URL` from the environment and queries the `tasks` table
+If PostgreSQL isn't deployed in your cluster, **don't worry** — `dev-template-configure` will detect it in step 4 and tell you exactly what to run (`uis deploy postgresql`).
## Quick Start
-### 1. Deploy PostgreSQL (once per environment)
+### 1. Install the template
+
+```bash
+dev-template python-basic-webserver-database
+```
+
+DCT downloads the template from the registry and copies all files to your current project directory, including `app/`, `manifests/`, `Dockerfile`, `requirements.txt`, `.gitignore`, `template-info.yaml`, and `config/init-database.sql`.
+
+### 2. Edit `template-info.yaml`
+
+Open `template-info.yaml` and find the `params:` section near the bottom. Set values for your app:
+
+```yaml
+params:
+ app_name: "my-cool-app"
+ database_name: "my_cool_app_db"
+```
+
+The defaults (`my-app`, `my_app_db`) work, but pick names that match your project — these become the PostgreSQL user and database names.
+
+The full `template-info.yaml` declares the PostgreSQL dependency in the `requires:` section:
+
+```yaml
+params:
+ app_name: "my-app"
+ database_name: "my_app_db"
+
+requires:
+ - service: postgresql
+ config:
+ database: "{{ params.database_name }}"
+ init: "config/init-database.sql"
+```
+
+DCT reads this file when you run `dev-template-configure` in the next step. The `{{ params.database_name }}` reference is substituted with the value you set above.
+
+### 3. (Optional) Customise `config/init-database.sql`
+
+This file is the schema and seed data UIS applies to your database. The default creates a `tasks` table with 3 rows:
+
+```sql
+CREATE TABLE IF NOT EXISTS tasks (
+ id SERIAL PRIMARY KEY,
+ title VARCHAR(255) NOT NULL,
+ status VARCHAR(20) DEFAULT 'pending',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
+
+INSERT INTO tasks (title, status) VALUES
+ ('Set up the database connection', 'done'),
+ ('Build something with Flask + PostgreSQL', 'pending'),
+ ('Deploy to Kubernetes via ArgoCD', 'pending')
+ON CONFLICT DO NOTHING;
+```
+
+All statements are idempotent (`IF NOT EXISTS`, `ON CONFLICT DO NOTHING`) so re-running configure is safe. UIS applies the file with `psql --set ON_ERROR_STOP=on`, so any syntax error fails fast with a clear message.
-If PostgreSQL isn't running in your UIS cluster yet, deploy it via the `postgresql-demo` UIS stack template:
+For your real schema, edit this file to add your own tables, indexes, and seed data.
+
+### 4. Run `dev-template-configure`
```bash
-uis template install postgresql-demo
+dev-template-configure
+```
+
+What happens:
+
+1. DCT reads `template-info.yaml` and validates that the `params:` are filled in
+2. DCT calls `uis configure postgresql --app --database --init-file -` via the bridge, piping in the substituted SQL
+3. UIS creates the database and user, applies the init SQL, and writes connection details
+4. UIS also creates a Kubernetes Secret in your app's namespace so the deployed pod can read `DATABASE_URL` later (when you `git push` and ArgoCD deploys)
+5. DCT writes `.env` to your project root (gitignored) with the local connection string
+
+If PostgreSQL isn't deployed in your cluster, this step fails with a clear error from UIS telling you to run `uis deploy postgresql`.
+
+You should see something like:
+
+```
+📦 Configuring postgresql...
+✅ postgresql — configured
+ → .env: DATABASE_URL=postgresql://my_cool_app:Xa7mP9...@host.docker.internal:35432/my_cool_app_db (local)
+ → K8s Secret: -db in namespace (cluster)
```
-### 2. Configure this app's database
+### 5. Verify the database
-This creates a new database + user in PostgreSQL, applies the init SQL (tasks table + seed data), and writes `DATABASE_URL` to `.env`:
+Inspect the seeded data without starting the app:
```bash
-dev-template configure
+uis connect postgresql my_cool_app_db
```
-You'll be prompted to fill in `params.app_name` and `params.database_name` in `template-info.yaml` first (or pass them via `--param`).
+Inside psql:
-### 3. Install Python dependencies and run
+```sql
+SELECT * FROM tasks;
+\q
+```
+
+You should see 3 rows. If they're there, the database is set up correctly and `DATABASE_URL` is in your `.env`.
+
+### 6. Run the app
+
+DCT ships with [`uv`](https://github.com/astral-sh/uv) for fast Python package management. Create a virtualenv, install dependencies, and run the app:
```bash
-pip install -r requirements.txt
+uv venv
+source .venv/bin/activate
+uv pip install -r requirements.txt
python app/app.py
```
-Then open:
-- http://localhost:3000 — home page
-- http://localhost:3000/tasks — list tasks from the database
-- http://localhost:3000/health — verify DB connectivity
+Or one-liner (no manual activation):
-The app **requires** `DATABASE_URL` and will exit immediately if it isn't set.
+```bash
+uv venv
+uv pip install -r requirements.txt
+uv run python app/app.py
+```
-## Prerequisites
+The Flask debug server starts on port 3000.
-Development tools are installed automatically by the devcontainer. If you need to reinstall, run `dev-setup`.
+**VS Code tip (optional):** if you see "Error refreshing packages" from VS Code's Python extension, add this to your workspace `.vscode/settings.json`:
-UIS must be running with PostgreSQL deployed (see step 1 above).
+```json
+{
+ "python-envs.alwaysUseUv": true
+}
+```
-## Project Structure
+The error happens because `uv venv` doesn't install `pip` into the venv (it doesn't need to), and VS Code's Python extension defaults to `pip list` for package enumeration. The setting tells it to use `uv` instead. If your project's `.vscode/settings.json` already exists with other keys, just add this one — don't replace the whole file.
-```plaintext
+### 7. Open in your browser
+
+VS Code's "Ports" tab in the bottom panel auto-forwards port 3000. Click the globe icon next to it to open these URLs:
+
+- `http://localhost:3000/` — Home page
+- `http://localhost:3000/tasks` — JSON list of seeded rows
+- `http://localhost:3000/health` — DB connectivity check
+
+If `/tasks` shows the 3 seeded rows, your producer/consumer chain is working end-to-end: Flask → DATABASE_URL → host.docker.internal → UIS port-forward → PostgreSQL pod in K8s.
+
+## Project structure
+
+After installation, your project contains:
+
+```
├── app/
│ └── app.py # Flask app reading from PostgreSQL
├── config/
-│ └── init-database.sql # Tasks table + seed data (applied by uis configure)
+│ └── init-database.sql # Schema + seed data (applied by uis configure)
├── manifests/
│ ├── deployment.yaml # K8s Deployment + Service (uses Secret for DATABASE_URL)
│ └── kustomization.yaml # ArgoCD configuration
├── .github/
│ └── workflows/
│ └── urbalurba-build-and-push.yaml # CI/CD pipeline
+├── .gitignore # Excludes .env*, .venv/, etc.
├── Dockerfile # Container build
-├── requirements.txt # Python dependencies
-├── template-info.yaml # Template metadata
+├── requirements.txt # Flask, psycopg2-binary, python-dotenv
+├── template-info.yaml # Template metadata (read by dev-template-configure)
└── README-python-basic-webserver-database.md # This file
```
## Development
-- Edit `app/app.py` — the Flask application
-- Edit `config/init-database.sql` to change the schema (re-run `dev-template configure` to apply)
-- Changes auto-reload in debug mode
+- Edit `app/app.py` — the main Flask application. Changes auto-reload in debug mode.
+- Edit `config/init-database.sql` to change the schema. Re-run `dev-template-configure` to apply the changes.
+- Edit `template-info.yaml` to change `params`. Re-run `dev-template-configure` afterward (it's idempotent — safe to run repeatedly).
-## Docker Build
+## Deploy to your local cluster
-```bash
-docker build -t python-basic-webserver-database .
-docker run -p 3000:3000 --env-file .env python-basic-webserver-database
-```
+The standard workflow uses GitHub Actions + ArgoCD — no manual `docker build` or `kubectl apply`:
-## Kubernetes Deployment
+1. **Push your code to GitHub**:
+ ```bash
+ git push
+ ```
+ GitHub Actions builds and pushes the container image to GitHub Container Registry. The image is **credential-free** — `DATABASE_URL` is injected at runtime from a Kubernetes Secret.
-Before deploying, create the `DATABASE_URL` secret using the **cluster** connection string from `uis configure` output:
+2. **Register the app with ArgoCD** (one-time per project, from your host machine):
+ ```bash
+ ./uis argocd register
+ ```
+ This creates an ArgoCD Application that watches your repo and auto-deploys updates on every push.
-```bash
-kubectl create secret generic -db \
- --from-literal=DATABASE_URL='postgresql://user:pass@postgresql.default.svc.cluster.local:5432/'
-```
+3. **Access the app** at `http://.localhost`. ArgoCD applies the deployment manifest, K8s injects `DATABASE_URL` from the Secret UIS created in step 4 above, and the pod connects to PostgreSQL via the cluster service DNS (`postgresql.default.svc.cluster.local`).
-Then apply the manifests:
+You don't need to create the Kubernetes Secret manually — `dev-template-configure` already created it in the right namespace. The deployment manifest references it via `secretKeyRef`.
-```bash
-kubectl apply -k manifests/
-```
+## Try this with
+
+This is the consumer side of the producer/consumer pattern. The producer side is:
+
+- [PostgreSQL Demo](../demo/postgresql-demo) — a UIS stack template that deploys PostgreSQL standalone, useful for verifying your UIS setup. You don't need to install it for `python-basic-webserver-database` to work — `dev-template-configure` handles everything.
## CI/CD
-The GitHub Actions workflow automatically builds and pushes the Docker image to GitHub Container Registry when changes are pushed to the main branch.
+The GitHub Actions workflow (`.github/workflows/urbalurba-build-and-push.yaml`) automatically builds and pushes the Docker image to GitHub Container Registry when changes are pushed to the main branch. ArgoCD picks up the new image and deploys it.
---
diff --git a/website/docs/templates/basic-web-server/csharp-basic-webserver.mdx b/website/docs/templates/basic-web-server/csharp-basic-webserver.mdx
index 60f3e9d..b3f3c99 100644
--- a/website/docs/templates/basic-web-server/csharp-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/csharp-basic-webserver.mdx
@@ -25,12 +25,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-csharp"
/>
-## Summary
-
-A minimal ASP.NET Core web server with hot reload via dotnet watch, Docker multi-stage build, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A minimal ASP.NET Core web server. Displays "Hello World" with current time and date, with hot reload support via `dotnet watch`.
diff --git a/website/docs/templates/basic-web-server/golang-basic-webserver.mdx b/website/docs/templates/basic-web-server/golang-basic-webserver.mdx
index 52c4894..9a4ba3e 100644
--- a/website/docs/templates/basic-web-server/golang-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/golang-basic-webserver.mdx
@@ -24,12 +24,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-golang"
/>
-## Summary
-
-A minimal web server using Go's standard net/http package with a health check endpoint, Docker multi-stage build, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A minimal web server using Go's standard `net/http` package. Displays "Hello World" with current time and date, and provides health check endpoints.
diff --git a/website/docs/templates/basic-web-server/java-basic-webserver.mdx b/website/docs/templates/basic-web-server/java-basic-webserver.mdx
index 3aa49b4..3677a11 100644
--- a/website/docs/templates/basic-web-server/java-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/java-basic-webserver.mdx
@@ -24,12 +24,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-java"
/>
-## Summary
-
-A minimal Spring Boot web server with health check endpoints via Actuator, Docker multi-stage build, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A minimal Spring Boot web server. Displays "Hello World" with current time and date, and provides health check endpoints via Spring Boot Actuator.
diff --git a/website/docs/templates/basic-web-server/php-basic-webserver.mdx b/website/docs/templates/basic-web-server/php-basic-webserver.mdx
index 11f312d..e5c5816 100644
--- a/website/docs/templates/basic-web-server/php-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/php-basic-webserver.mdx
@@ -23,12 +23,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-php-laravel"
/>
-## Summary
-
-A minimal PHP web server using PHP's built-in server with a health check endpoint, Docker containerization, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A minimal PHP web server using PHP's built-in server. Displays "Hello World" with current time and date, and provides health check endpoints.
diff --git a/website/docs/templates/basic-web-server/python-basic-webserver.mdx b/website/docs/templates/basic-web-server/python-basic-webserver.mdx
index 4be2259..32e46c7 100644
--- a/website/docs/templates/basic-web-server/python-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/python-basic-webserver.mdx
@@ -24,12 +24,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-python"
/>
-## Summary
-
-A minimal Python web server using Flask with a health check endpoint, Docker containerization, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow. Ideal for microservices and API backends.
-
----
-
A minimal Flask web server. Displays "Hello World" with current time and date, and demonstrates deployment to Kubernetes via ArgoCD and GitHub Actions.
diff --git a/website/docs/templates/basic-web-server/typescript-basic-webserver.mdx b/website/docs/templates/basic-web-server/typescript-basic-webserver.mdx
index 1954250..ed6ecfe 100644
--- a/website/docs/templates/basic-web-server/typescript-basic-webserver.mdx
+++ b/website/docs/templates/basic-web-server/typescript-basic-webserver.mdx
@@ -25,12 +25,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-typescript"
/>
-## Summary
-
-A minimal Express.js web server written in TypeScript with hot reload via nodemon, Docker containerization, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A minimal Express.js web server written in TypeScript. Displays "Hello World" and demonstrates deployment to Kubernetes via ArgoCD and GitHub Actions.
diff --git a/website/docs/templates/demo/postgresql-demo.mdx b/website/docs/templates/demo/postgresql-demo.mdx
index c92d23f..5bedd32 100644
--- a/website/docs/templates/demo/postgresql-demo.mdx
+++ b/website/docs/templates/demo/postgresql-demo.mdx
@@ -16,21 +16,15 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
name="PostgreSQL Demo"
version="1.0.0"
description="Deploys PostgreSQL and creates a sample database with seed data"
- install="dev-template postgresql-demo"
+ install="uis template install postgresql-demo"
website=""
docs="https://github.com/helpers-no/dev-templates/tree/main/uis-stack-templates/postgresql-demo"
tags={["postgresql","database","demo","getting-started"]}
tools=""
/>
-## Summary
-A minimal demonstration template that deploys PostgreSQL to the UIS cluster and creates a sample database with a tasks table and seed data. Shows the uis template flow from registry to deployed, configured service. Use it to verify your UIS setup or as a starting point for your own stack templates.
-
----
-
-
-A minimal UIS stack template that deploys PostgreSQL and creates a sample database with seed data. Use this to verify your UIS setup or as a starting point for your own stack templates.
+A minimal UIS stack template that deploys PostgreSQL and creates a sample database with seed data. Use it to verify your UIS setup, or as a starting point for your own UIS stack templates.
## What it deploys
@@ -38,18 +32,28 @@ A minimal UIS stack template that deploys PostgreSQL and creates a sample databa
## What it configures
-- Creates a per-app user (derived from `app_name` param)
-- Creates a database (from `database_name` param)
-- Applies the init SQL file — creates a `tasks` table with 3 seed rows
+- A per-app user (derived from `app_name` param)
+- A database (from `database_name` param)
+- The init SQL — creates a `tasks` table with 3 seed rows
-## Usage
+## Before you start
-From the UIS provision-host:
+This template uses UIS. Verify the UIS provision-host container is running:
+
+```bash
+docker ps --filter name=uis-provision-host --format '{{.Status}}'
+```
+
+You should see `Up X minutes`. If not, start UIS from the `urbalurba-infrastructure` repo. Inside DCT (devcontainer-toolbox v1.7.34 or later) you also have the `uis` shim, which lets you run UIS commands directly.
+
+## Install
```bash
uis template install postgresql-demo
```
+This works from inside the DCT devcontainer (via the `uis` shim), from the host (via `./uis` from the urbalurba-infrastructure repo), and from inside the UIS provision-host. Same command, three contexts.
+
With custom params:
```bash
@@ -58,7 +62,7 @@ uis template install postgresql-demo --param app_name=myapp --param database_nam
## What you get
-After install, `uis template install` returns JSON with connection details:
+After install, `uis template install` returns JSON with connection details (passwords are randomly generated — your actual values will be different):
```json
{
@@ -67,42 +71,50 @@ After install, `uis template install` returns JSON with connection details:
"local": {
"host": "host.docker.internal",
"port": 35432,
- "database_url": "postgresql://demo_app:@host.docker.internal:35432/demo_db"
+ "database_url": "postgresql://demo_app:Xa7mP9...@host.docker.internal:35432/demo_db"
},
"cluster": {
"host": "postgresql.default.svc.cluster.local",
- "port": 5432,
- "database_url": "postgresql://demo_app:@postgresql.default.svc.cluster.local:5432/demo_db"
+ "port": 5432
},
"database": "demo_db",
"username": "demo_app",
- "password": ""
+ "password": "Xa7mP9...",
+ "secret_name": "-db",
+ "secret_namespace": ""
}
```
-## Verify it worked
-
-Expose the service and connect:
+The `local` URL works from your DCT devcontainer (Flask, psql, etc.) via the host port-forward.
+The `cluster` connection is what K8s pods use — UIS also creates a Kubernetes Secret in your app's namespace so deployments can read it via `secretKeyRef`.
-```bash
-uis expose postgresql
-```
+## Verify it worked
-Then from any container with psql:
+The simplest way to inspect the seeded data:
```bash
-psql -h host.docker.internal -p 35432 -U demo_app -d demo_db
-# Enter the password from the JSON output above
+uis connect postgresql demo_db
```
-Query the tasks table:
+Inside psql:
```sql
SELECT * FROM tasks;
+\q
```
You should see 3 rows. Re-running `uis template install postgresql-demo` is safe — it detects the existing database and returns `already_configured`.
+## Try this with
+
+Once PostgreSQL is running and you've installed this demo template, scaffold a Flask app on top with the consumer-side template:
+
+```bash
+dev-template python-basic-webserver-database
+```
+
+That template's `dev-template-configure` step will create its own per-app database (separate from `demo_db`) and write `DATABASE_URL` to `.env` for local dev. Run the app with `uv run python app/app.py` and curl `/tasks` to see the full producer/consumer chain working end-to-end.
+
## Extending this template
This template is the minimum viable example. To build your own:
@@ -112,3 +124,11 @@ This template is the minimum viable example. To build your own:
3. Add more init files in `config/` (SQL, Authentik blueprints, Grafana dashboards)
4. Reference UIS stacks (like `observability`) in `provides.stacks` to include multi-service stacks
+See the [contributor docs](https://tmp.sovereignsky.no/docs/contributors/creating-a-template) for the full template authoring guide.
+
+---
+
+## Related Templates
+
+- [Python Basic Webserver with Database](../basic-web-server-database/python-basic-webserver-database)
+
diff --git a/website/docs/templates/web-app/designsystemet-basic-react-app.mdx b/website/docs/templates/web-app/designsystemet-basic-react-app.mdx
index fb41426..753a8ab 100644
--- a/website/docs/templates/web-app/designsystemet-basic-react-app.mdx
+++ b/website/docs/templates/web-app/designsystemet-basic-react-app.mdx
@@ -25,12 +25,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools="dev-typescript"
/>
-## Summary
-
-A React application using Designsystemet from Digdir with blog cards, Vite for development, TypeScript support, Docker containerization, Kubernetes deployment manifests, and GitHub Actions CI/CD workflow.
-
----
-
A React application using [Designsystemet](https://designsystemet.no/) from Digdir. Displays a blog page with cards using Designsystemet components, built with Vite and TypeScript.
diff --git a/website/docs/templates/workflow/plan-based-workflow.mdx b/website/docs/templates/workflow/plan-based-workflow.mdx
index aa06839..6f2ff73 100644
--- a/website/docs/templates/workflow/plan-based-workflow.mdx
+++ b/website/docs/templates/workflow/plan-based-workflow.mdx
@@ -24,12 +24,6 @@ import TemplateHeader from '@site/src/components/TemplateHeader';
tools=""
/>
-## Summary
-
-A structured AI development workflow that guides AI coding assistants through investigation, planning, and phased implementation. Includes CLAUDE.md, 6 portable docs, plan templates, and git safety rules. Designed for human-in-the-loop collaboration.
-
----
-
A structured AI-assisted development workflow with investigation plans, phased implementation, and human-in-the-loop validation. Works with Claude Code and other AI coding assistants.
diff --git a/website/src/data/template-registry.json b/website/src/data/template-registry.json
index 35706bf..533bf89 100644
--- a/website/src/data/template-registry.json
+++ b/website/src/data/template-registry.json
@@ -1,5 +1,5 @@
{
- "generated": "2026-04-06T08:39:50.490Z",
+ "generated": "2026-04-09T10:47:44.120Z",
"categories": [
{
"id": "WORKFLOW",
@@ -339,7 +339,9 @@
"website": "",
"docs": "https://github.com/helpers-no/dev-templates/tree/main/uis-stack-templates/postgresql-demo",
"summary": "A minimal demonstration template that deploys PostgreSQL to the UIS cluster and creates a sample database with a tasks table and seed data. Shows the uis template flow from registry to deployed, configured service. Use it to verify your UIS setup or as a starting point for your own stack templates.",
- "related": [],
+ "related": [
+ "python-basic-webserver-database"
+ ],
"context": "uis",
"params": {
"app_name": "demo-app",