Skip to main content

Investigate: Environment Card Improvements

IMPLEMENTATION RULES: Before implementing this plan, read and follow:

Status: Decided — ready for plan (2026-04-13)​

Goal: Determine the right scope, design, and implementation approach for four improvements to the Environment card on per-template documentation pages: fix the ④ numbering bug, decouple the configure command from hardcoded text, rewrite the Configure sub-section intro, and auto-generate the "Expected output" sample block from registry data.

Last Updated: 2026-04-13

Related:


Background​

The Environment card is rendered by <TemplateEnvironment> on every non-overlay template page. It currently has 3–4 numbered sub-sections:

ENVIRONMENT
① What gets set up (tools + services + init SQL)
② Configure (only when services are required)
③ Setup (when quickstart.setup has commands)
④ Run the Flask app (always — but no number rendered, see Problem 1)

Several issues surfaced during visual review of the deployed site (tmp.sovereignsky.no) and discussion of the architecture diagram v2 work:

  1. The ④ Run heading is missing its number prefix
  2. Multiple places in the codebase hardcode dev-template configure (or its incorrect dev-template-configure variant) instead of reading from a single source
  3. The Configure sub-section's intro text ("This template uses PostgreSQL") is a label, not an explanation of what configuring does
  4. The "Expected output from dev-template configure" block is a 94-line hand-written mock in README-python-basic-webserver-database.md, even though every value in it is derivable from registry data

This investigation captures the design decisions for fixing each of these. A downstream PLAN-environment-card-improvements.md will be created once all open questions are resolved.


Problem 1: ④ numbering bug​

Current state​

TemplateEnvironment/index.tsx has:

const sectionsShown = [hasWhatGetsSetUp, showConfigure, showRun].filter(Boolean).length;
const showNumbers = sectionsShown > 1;
let n = 0;
const nextNumber = () => {
if (!showNumbers) return null;
n += 1;
const symbols = ['①', '②', '③'];
return <span className={styles.number}>{symbols[n - 1]}</span>;
};

Why it broke​

The earlier quickstart split (PR #31) introduced a separate Setup sub-section above the Run sub-section. Both call nextNumber(). When all four sub-sections render (What gets set up + Configure + Setup + Run), nextNumber() is called four times, but symbols only has three entries — the fourth call reads symbols[3] → undefined → no number prefix on the Run heading.

Also, sectionsShown is computed from [hasWhatGetsSetUp, showConfigure, showRun] — three booleans. The Setup sub-section's existence is implicit in showRun && quickstart.setup.length > 0, so it's not counted, which means showNumbers may be false when it should be true in some edge cases.

Decision​

Add ④ to the symbol array and count the Setup sub-section separately.

const setupVisible = showRun && (quickstart?.setup?.length ?? 0) > 0;
const sectionsShown = [hasWhatGetsSetUp, showConfigure, setupVisible, showRun].filter(Boolean).length;
...
const symbols = ['①', '②', '③', '④'];

Two-line change, zero schema impact, no architectural debate.


Problem 2: configure_command is hardcoded in multiple places​

Current state​

Three places in the codebase assume the configure command:

  1. build-architecture-mermaid.ts — hardcodes Dev->>DCT: dev-template configure in the app sequence diagram and Dev->>DCT: uis template install ${entry.id} in the stack sequence diagram (branch on install_type)
  2. <TemplateEnvironment> — hardcodes <code>dev-template-configure</code> (with a hyphen — wrong form) inside the Configure sub-section
  3. Planned expected-output generator — would need to switch on install_type to pick the right command form

The hyphen vs space inconsistency (dev-template-configure vs dev-template configure) appears in 7+ places across the codebase and was identified during an earlier text audit. The rendered Configure sub-section currently shows the wrong form (dev-template-configure) on every template page.

Options​

Option A: Add a top-level configure_command: string field​

# python-basic-webserver-database
configure_command: "dev-template configure"

# postgresql-demo (stack)
configure_command: "uis template install postgresql-demo"

# python-basic-webserver (no configure step)
# field omitted

Pros: each template explicitly declares its own command; eliminates hardcoded branching on install_type; resolves the hyphen/space inconsistency at the per-template authoritative level. Cons: one new yaml field per relevant template (~2 templates today).

Option B: Hardcode but centralize in one constants file​

scripts/lib/configure-commands.ts exports getConfigureCommand(install_type, entry) that returns the right string. Component reads from the constant.

Pros: no yaml changes. Cons: still hardcodes the assumption that "app templates use dev-template configure"; doesn't let templates override; doesn't fix the per-template authority concern.

Option C: Leave it hardcoded but fix the hyphen bug only​

Just change dev-template-configure → dev-template configure in the component and rendered docs. No new field.

Pros: smallest change. Cons: doesn't fix the underlying coupling between the component, the architecture builder, and the eventual expected-output generator.

Decision​

Option A — configure_command: string (top-level, snake_case, optional).

Decided 2026-04-13. Rationale: matches the project's per-template authority pattern (the same reason quickstart.run exists). Fixes the hyphen bug as a side effect (each template authoritatively declares the right form).

Field naming rationale​

CandidateVerdict
configure: "dev-template configure"Too ambiguous — reads like a boolean or block, not a command string
configure_command: "dev-template configure"✅ Clear, explicit, snake_case matches install_type style at top level
configureCommandWrong case — top-level fields use snake_case, camelCase is for nested/resolved blocks
setup_commandConflicts with quickstart.setup (install dependencies)
provision_commandAccurate but unfamiliar terminology
configure: { command, note } blockOver-engineered for one value; promote later if needed

Problem 3: Configure sub-section intro text + template-info.yaml dropdown​

Current state​

The Configure sub-section currently renders:

② Configure

This template uses **PostgreSQL**.

Then run:
dev-template-configure (with hyphen, wrong form)

Two issues:

  1. "This template uses PostgreSQL" is a label, not an explanation. It doesn't tell the developer what configuring does (provisions services, writes .env, creates K8s Secret).
  2. No template-info.yaml context — developers can't see what they might want to edit without opening the template's source. The init-SQL pattern (collapsible <details> with the file content) works well; the same pattern should apply to template-info.yaml.

Options for the intro text​

Option A (chosen): Keep "Configure" as the heading, rewrite the intro​

② Configure

Set up and configure the backend services this template needs.
Creates a per-app database, wires credentials into a local `.env` file,
and stores a Kubernetes Secret in your cluster for the deployed pod.

Uses: **PostgreSQL**

[▶ template-info.yaml — edit to change defaults]

Then run:
dev-template configure

Pros: short numbered heading stays consistent with other sub-sections; intro sentence describes what configuring does; matches the init-SQL dropdown pattern.

Option B: Action-led heading​

② Set up and configure backend services

Cons: longer heading wraps awkwardly on narrow screens; inconsistent with other sub-sections that use short labels.

Option C: Two-line heading (title + subtitle)​

② Configure
Set up backend services and write credentials to .env

Cons: needs new subtitle CSS; breaks visual consistency with other sub-sections.

Decision (intro)​

Option A. Decided 2026-04-13.

Options for the dropdown content​

Content shown in dropdownProsCons
Full template-info.yamlComplete context; mirrors init-SQL pattern (show whole file)Longer (~40–80 lines per template)
Just the params: blockTargeted to what users editHides other fields the author might want shown
Curated subset (params + configure_command + requires)Shows the decision surfaceArbitrary; feels editorial

Decision (dropdown content)​

Show the full raw template-info.yaml. Decided 2026-04-13. Mirrors the init-SQL pattern. Closed by default — doesn't dominate the page.

Implementation note​

Requires a new registry field templateInfoYaml: string storing the raw file content (read via readFileSync before yaml.load parses it). The component receives it as a prop and renders inside <details><summary>template-info.yaml — edit to change defaults</summary><pre><code>...</code></pre></details>.


Problem 4: Expected output is hand-written README prose​

Current state​

README-python-basic-webserver-database.md has a #### Expected output section (~94 lines, lines 106–200) showing what dev-template configure prints when you run it. It's a hand-written mock with template-specific values inline (my-cool-app, my_cool_app_db, etc.).

Only one template has this block today. Other templates would need similar blocks if hand-authored.

Why it can be auto-generated​

A field-by-field audit shows every value in the block is derivable from registry data:

SectionSource
Banner, UIS bridge checkStatic
Git identityparams.app_name (placeholder for the real repo name detected at runtime)
template-info.yaml readentry.id, entry.install_type
Parametersentry.params
Service config (postgresql, namespace, secret)resolvedServices[0], manifest.envVar, manifest.secretName
Init file sizeresolvedInitFiles[path] content length + line count
UIS command (literal)All args from registry data
.env write linemanifest.envVar, service.id, database, exposePort
K8s Secret infomanifest.secretName, manifest.envVar
Port-forward ASCII artStatic format with exposePort, namespace, in-cluster port
FooterService count from resolvedServices.length
Next stepsservice.id, service.database, manifest.secretName, quickstart.run

Decision​

Auto-generate the block. Decided 2026-04-13. Lives as scripts/lib/build-expected-output.ts (separate from the mermaid builder — different concern).

Where it renders​

Inside the Configure sub-section, as a collapsible <details> element matching the init-SQL pattern (and the new template-info.yaml dropdown from Problem 3). Closed by default; expand to see a sample run.

Per-archetype rendering​

ArchetypeExpected output?
E1 (app + services + manifest)✅ Generated
E2 (app + manifest, no services)null — no configure flow
E3 (stack template)OPEN — see Q3
E4 (overlay)null

In-cluster port dependency​

The mock includes lines like:

postgresql.default.svc.cluster.local:5432

The in-cluster port (5432 for postgres) is not currently surfaced anywhere in our data. A separate investigation INVESTIGATE-uis-in-cluster-port.md addresses this. Decision: introduce a stopgap file scripts/lib/service-ports.ts with {postgresql: 5432, redis: 6379, ...}, used by both the expected-output generator and the existing architecture diagram builder (which currently hardcodes 5432). When UIS ships inClusterPort on services.json, delete the stopgap file.


Open Questions​

These need decisions before the downstream PLAN can be written.

Q1. Git repo placeholder text in the mock output​

Real DCT detects the git repo at runtime and prints something like yourorg/my-cool-app. In the auto-generated mock, what should we substitute?

  • A: yourorg/<params.app_name> (e.g., yourorg/my-app) — concrete, reads like a real example
  • B: <your-org>/<your-repo> — explicit placeholders, clearly a sample
  • C: your-github-user/<params.app_name> — hybrid

Recommendation: Option A — concrete examples are easier to read than angle-bracket placeholders.

Decision: Option A — yourorg/<params.app_name>. Decided 2026-04-13.

Q2. Output-format drift policy​

The mock mimics real DCT runtime output (emojis, banners, exact wording). When real DCT updates its output format, our mock will drift.

  • A: Accept the drift, update the generator when noticed
  • B: Add a "last verified against DCT vX.Y" note in the generator file header so a future maintainer knows when to re-check

Recommendation: Option B — a one-line comment costs nothing and prevents long-term drift becoming silently wrong.

Decision: Option B — add a "last verified against DCT vX.Y on YYYY-MM-DD" note in the build-expected-output.ts file header. Decided 2026-04-13.

Q3. E3 (stack template) expected output​

postgresql-demo is a stack template. Its configure flow is uis template install postgresql-demo, not dev-template configure. Different command, different output format.

  • A: Defer — stacks return null; only E1 templates get expected output for now. postgresql-demo's page has no expected-output dropdown.
  • B: Do both formats in one go — adds ~60 lines of code for a different format template, but ships a complete experience for the one stack template that exists today.

Recommendation: Option B — only one stack template exists, doing both now means no visible gap on postgresql-demo's page.

Decision: Option B — generate expected output for both E1 (app + services + manifest) and E3 (stack template) in the same plan. Two format templates inside build-expected-output.ts: one for dev-template configure flow, one for uis template install flow. Decided 2026-04-13.

Q4. README cleanup scope​

I confirmed only python-basic-webserver-database has an #### Expected output block. But other templates might have similar hand-written mock output under different headings.

  • A: Only remove the one confirmed block in python-basic-webserver-database
  • B: Re-grep all 10 READMEs for similar mock blocks (any pre/code block that looks like terminal output) before the cleanup phase

Recommendation: Option B — quick check (one grep), avoids leaving stale duplicates.

Decision: Option B — re-grep all 10 READMEs for similar mock blocks (terminal-output-style fenced code blocks) before the cleanup phase. Remove anything that the new generator now produces. Decided 2026-04-13.

Q5. Maintainers field per-template review​

The earlier schema plan added maintainers: [terchris] uniformly to all 10 templates. Some templates may have had a different author historically.

  • A: Leave uniformly as terchris
  • B: Review git log for each template to confirm the original author and adjust

Recommendation: Option A for now — terchris is the active maintainer; historical authorship is captured in git. Adjust per template only when a non-terchris contributor specifically asks to be listed.

Decision: Option A — leave maintainers: [terchris] uniformly. Per-template review is out of scope. Decided 2026-04-13.

Q6. Should the new dropdowns share a CSS class with init-SQL?​

Both new dropdowns (template-info.yaml and expected-output) are <details> elements with similar content (a code block). Init-SQL already uses <details>. Two options:

  • A: One shared .detailsBlock class used by all three
  • B: Per-purpose classes (.initFile, .templateInfoYaml, .expectedOutput) for future independent styling

Recommendation: Option A — visual consistency is the goal; if one ever needs to look different, refactor then.

Decision: Option A — one shared .detailsBlock (or similar) CSS class used by init-SQL, template-info.yaml dropdown, and expected-output dropdown. Decided 2026-04-13.


Decisions made (locked in)​

Core problem decisions:

  • P1: Add ④ to symbol array, count Setup sub-section in sectionsShown
  • P2 — Field name: configure_command (snake_case, top-level, optional string)
  • P2 — Source of truth: each template declares its own command; component, architecture builder, and expected-output generator all read from the field
  • P3 — Intro rewrite: Option A (keep "Configure" heading, two-paragraph action-oriented intro)
  • P3 — Dropdown content: full raw template-info.yaml, closed by default, matching init-SQL pattern
  • P3 — New registry field: templateInfoYaml: string (raw file content)
  • P4 — Expected output: auto-generate from registry data, render as <details> inside the Configure sub-section
  • P4 — In-cluster port stopgap: scripts/lib/service-ports.ts until UIS ships inClusterPort

Open question resolutions (decided 2026-04-13):

  • Q1 — Git repo placeholder: Option A — yourorg/<params.app_name> (concrete example, easier to read than angle-bracket placeholders)
  • Q2 — Drift policy: Option B — add a "last verified against DCT vX.Y on YYYY-MM-DD" note in the generator file header
  • Q3 — E3 stack expected output: Option B — generate for both E1 (dev-template configure flow) and E3 (uis template install flow) in the same plan. Two format templates inside build-expected-output.ts.
  • Q4 — README cleanup scope: Option B — re-grep all 10 READMEs for similar mock blocks before the cleanup phase, remove anything the new generator now produces
  • Q5 — Maintainers field: Option A — leave uniformly as terchris, per-template review out of scope
  • Q6 — Dropdown CSS: Option A — one shared .detailsBlock class for init-SQL, template-info.yaml, and expected-output dropdowns

Recommendation Summary​

After all open questions are resolved, the downstream PLAN-environment-card-improvements.md should have these phases (per the "small phases" best practice — each independently shippable with its own validation gate):

PhaseScopeIndependently shippable?
1Fix ④ numbering bug✅
2Add configure_command field, eliminate hardcoding, fix hyphen bug✅
3Rewrite Configure sub-section intro text✅ (depends on 2 for the command line)
4Add template-info.yaml dropdown✅
5Build the expected-output generator + service-ports stopgap✅
6Wire expected-output into registry + component✅
7Unit tests for expected-output✅
8Remove hardcoded "Expected output" from README(s)✅
9Contributor docs update + move plan to completed✅

Phase ordering: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8 → 9. Phases 1–4 are pure component/yaml/generator changes for the Configure sub-section. Phases 5–7 build the expected-output generator on top. Phase 8 removes the now-duplicate README block. Phase 9 closes out.


Next Steps​

  • User answers Q1–Q6 — all six resolved 2026-04-13
  • Draft PLAN-environment-card-improvements.md in backlog/ based on the locked decisions and the 9-phase structure above
  • Implementation moves the plan to active/ per WORKFLOW.md
  • After the plan ships, move both this investigation and the plan to completed/