feat: add Dev and Preview title prefixes (#2464)
This commit is contained in:
@@ -182,6 +182,7 @@ cd /app
|
||||
export KANDEV_HOME_DIR=/data
|
||||
export KANDEV_DOCKER_ENABLED=false
|
||||
export KANDEV_LOG_LEVEL=info
|
||||
export KANDEV_WEB_TITLE_PREFIX=Preview
|
||||
export KANDEV_WEB_DIST_DIR=/app/apps/web/dist
|
||||
# Preview mode: keep the mock agent while loading the built-in agent catalogue
|
||||
# so Settings can show agents that are available to install.
|
||||
|
||||
@@ -154,6 +154,9 @@ func TestBuildExtractScript(t *testing.T) {
|
||||
if !strings.Contains(script, "KANDEV_WEB_DIST_DIR=/app/apps/web/dist") {
|
||||
t.Errorf("expected KANDEV_WEB_DIST_DIR to point at packaged Vite dist")
|
||||
}
|
||||
if !strings.Contains(script, "KANDEV_WEB_TITLE_PREFIX=Preview") {
|
||||
t.Errorf("expected preview title prefix in script")
|
||||
}
|
||||
if !strings.Contains(script, "ln -sf /app/apps/backend/bin/kandev /usr/local/bin/kandev") {
|
||||
t.Errorf("expected native kandev symlink in script")
|
||||
}
|
||||
|
||||
@@ -158,6 +158,15 @@ debug:
|
||||
dev: "true"
|
||||
e2e: ""
|
||||
|
||||
# ── Browser identity ─────────────────────────────────────────────
|
||||
# Distinguish the local development tab from release and preview tabs without
|
||||
# changing the title of production or e2e environments.
|
||||
ui:
|
||||
KANDEV_WEB_TITLE_PREFIX:
|
||||
prod: ""
|
||||
dev: "Dev"
|
||||
e2e: ""
|
||||
|
||||
# ── E2E test tuning ───────────────────────────────────────────────
|
||||
# Behaviour knobs that only matter when running playwright. These
|
||||
# don't belong in dev (would mask real interactions) and never in
|
||||
|
||||
@@ -52,6 +52,9 @@ func TestApplyProfile_DefaultsToProd(t *testing.T) {
|
||||
"false",
|
||||
)
|
||||
}
|
||||
if _, ok := os.LookupEnv("KANDEV_WEB_TITLE_PREFIX"); ok {
|
||||
t.Errorf("KANDEV_WEB_TITLE_PREFIX is set in prod; want it unset")
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProfile_DevUsesDevelopmentDefaults verifies the mixed dev profile:
|
||||
@@ -86,6 +89,23 @@ func TestApplyProfile_DevUsesDevelopmentDefaults(t *testing.T) {
|
||||
"false",
|
||||
)
|
||||
}
|
||||
if v := os.Getenv("KANDEV_WEB_TITLE_PREFIX"); v != "Dev" {
|
||||
t.Errorf("KANDEV_WEB_TITLE_PREFIX = %q in dev; want %q", v, "Dev")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyProfile_ExplicitTitlePrefixWinsInDev(t *testing.T) {
|
||||
clearProfileSelectors(t)
|
||||
clearProfilesYAMLVars(t)
|
||||
t.Setenv("KANDEV_DEBUG_DEV_MODE", "true")
|
||||
t.Setenv("KANDEV_WEB_TITLE_PREFIX", "Custom")
|
||||
|
||||
if _, _, err := ApplyProfile(); err != nil {
|
||||
t.Fatalf("ApplyProfile: %v", err)
|
||||
}
|
||||
if v := os.Getenv("KANDEV_WEB_TITLE_PREFIX"); v != "Custom" {
|
||||
t.Errorf("ApplyProfile overwrote explicit title prefix: got %q; want %q", v, "Custom")
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProfile_E2EWinsOverDev checks the documented detection
|
||||
@@ -118,6 +138,9 @@ func TestApplyProfile_E2EWinsOverDev(t *testing.T) {
|
||||
"false",
|
||||
)
|
||||
}
|
||||
if _, ok := os.LookupEnv("KANDEV_WEB_TITLE_PREFIX"); ok {
|
||||
t.Errorf("KANDEV_WEB_TITLE_PREFIX is set in e2e; want it unset")
|
||||
}
|
||||
}
|
||||
|
||||
// TestApplyProfile_ExistingEnvWins is the precedence guarantee that
|
||||
@@ -230,6 +253,7 @@ func TestProfilesYAML_ContainsRequiredSections(t *testing.T) {
|
||||
"KANDEV_FEATURES_OFFICE:",
|
||||
"KANDEV_FEATURES_APP_STATUS_BAR:",
|
||||
"KANDEV_FEATURES_CLAUDE_BACKGROUND_PROMPT_HANDOFF:",
|
||||
"KANDEV_WEB_TITLE_PREFIX:",
|
||||
} {
|
||||
if !strings.Contains(yaml, section) {
|
||||
t.Errorf("embedded profiles.yaml missing section/key %q; embed broken or file truncated", section)
|
||||
|
||||
@@ -29,6 +29,7 @@ describe("supervisor launch manifest", () => {
|
||||
KANDEV_HOME_DIR: home,
|
||||
KANDEV_SERVER_PORT: "38429",
|
||||
KANDEV_DESKTOP_HEALTH_TOKEN: "health-token",
|
||||
KANDEV_WEB_TITLE_PREFIX: "Preview",
|
||||
SECRET_TOKEN: "do-not-write",
|
||||
},
|
||||
home_dir: home,
|
||||
@@ -45,6 +46,7 @@ describe("supervisor launch manifest", () => {
|
||||
KANDEV_HOME_DIR: home,
|
||||
KANDEV_SERVER_PORT: "38429",
|
||||
KANDEV_DESKTOP_HEALTH_TOKEN: "health-token",
|
||||
KANDEV_WEB_TITLE_PREFIX: "Preview",
|
||||
});
|
||||
if (process.platform !== "win32") {
|
||||
expect((fs.statSync(target).mode & 0o777).toString(8)).toBe("600");
|
||||
@@ -75,6 +77,7 @@ describe("supervisor launch manifest", () => {
|
||||
KANDEV_HOME_DIR: "/tmp/kandev",
|
||||
KANDEV_CONSOLE_LOG_LEVEL: "warn",
|
||||
KANDEV_DESKTOP_HEALTH_TOKEN: "health-token",
|
||||
KANDEV_WEB_TITLE_PREFIX: "Dev",
|
||||
AWS_SECRET_ACCESS_KEY: "secret",
|
||||
GITHUB_TOKEN: "secret",
|
||||
}),
|
||||
@@ -82,6 +85,7 @@ describe("supervisor launch manifest", () => {
|
||||
KANDEV_HOME_DIR: "/tmp/kandev",
|
||||
KANDEV_CONSOLE_LOG_LEVEL: "warn",
|
||||
KANDEV_DESKTOP_HEALTH_TOKEN: "health-token",
|
||||
KANDEV_WEB_TITLE_PREFIX: "Dev",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ const ENV_ALLOWLIST = new Set([
|
||||
"KANDEV_AGENT_STANDALONE_PORT",
|
||||
"KANDEV_LOG_LEVEL",
|
||||
"KANDEV_CONSOLE_LOG_LEVEL",
|
||||
"KANDEV_WEB_TITLE_PREFIX",
|
||||
"KANDEV_DEBUG_DEV_MODE",
|
||||
"KANDEV_DEBUG_AGENT_MESSAGES",
|
||||
"KANDEV_DEBUG_PPROF_ENABLED",
|
||||
|
||||
@@ -438,8 +438,9 @@ export const backendFixture = base.extend<object, { backend: BackendContext }>({
|
||||
// process.env.X before spawn — that already flows through the
|
||||
// `...sanitizeInheritedEnv(process.env)` spread above, and the
|
||||
// backend's ApplyProfile leaves already-set vars alone. (Note:
|
||||
// KANDEV_FEATURES_* is the exception — it's stripped from the
|
||||
// inherited env so the profile always governs feature flags.)
|
||||
// KANDEV_FEATURES_* and KANDEV_WEB_TITLE_PREFIX are exceptions —
|
||||
// they are stripped from the inherited env so the e2e profile
|
||||
// always controls the baseline.)
|
||||
GIT_AUTHOR_NAME: "E2E Test",
|
||||
GIT_AUTHOR_EMAIL: "e2e@test.local",
|
||||
GIT_COMMITTER_NAME: "E2E Test",
|
||||
@@ -539,6 +540,9 @@ function writeGitShimLauncher(shimDir: string, shimScript: string): void {
|
||||
// in the test backend → /api/v1/office/* 404s. Dropping the whole
|
||||
// KANDEV_FEATURES_* namespace lets the e2e profile govern feature flags so
|
||||
// the suite always exercises them, regardless of where it's launched.
|
||||
// - KANDEV_WEB_TITLE_PREFIX — the browser identity is profile-managed in
|
||||
// this suite. Explicit per-test values are applied after this baseline is
|
||||
// sanitized through `backend.restart(overrides)`.
|
||||
// - PATH casing aliases — Windows commonly inherits `Path`; retaining it
|
||||
// beside the fixture's new `PATH` makes child-process lookup order
|
||||
// ambiguous. The caller restores one canonical PATH after sanitizing.
|
||||
@@ -547,7 +551,9 @@ function sanitizeInheritedEnv(env: Record<string, string>): Record<string, strin
|
||||
delete cleaned.GH_TOKEN;
|
||||
delete cleaned.GITHUB_TOKEN;
|
||||
for (const key of Object.keys(cleaned)) {
|
||||
if (key.startsWith("KANDEV_FEATURES_")) delete cleaned[key];
|
||||
if (key === "KANDEV_WEB_TITLE_PREFIX" || key.startsWith("KANDEV_FEATURES_")) {
|
||||
delete cleaned[key];
|
||||
}
|
||||
if (key.toUpperCase() === "PATH") delete cleaned[key];
|
||||
}
|
||||
return cleaned;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { expect, test } from "../../fixtures/test-base";
|
||||
|
||||
test.describe.serial("environment browser title prefixes", () => {
|
||||
test("uses Dev for the development profile", async ({ backend, testPage }) => {
|
||||
await backend.restart({ KANDEV_E2E_MOCK: "", KANDEV_DEBUG_DEV_MODE: "true" });
|
||||
|
||||
try {
|
||||
await testPage.goto("/");
|
||||
await expect(testPage).toHaveTitle("Dev Kandev");
|
||||
} finally {
|
||||
await backend.restart();
|
||||
}
|
||||
});
|
||||
|
||||
test("uses Preview for an explicitly configured preview backend", async ({
|
||||
backend,
|
||||
testPage,
|
||||
}) => {
|
||||
await backend.restart({ KANDEV_WEB_TITLE_PREFIX: "Preview" });
|
||||
|
||||
try {
|
||||
await testPage.goto("/");
|
||||
await expect(testPage).toHaveTitle("Preview Kandev");
|
||||
} finally {
|
||||
await backend.restart();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -34,6 +34,10 @@ All env vars use the `KANDEV_` prefix. Nested keys map by replacing `.` with `_`
|
||||
|
||||
The aliases on the right are explicit bindings - see `LoadWithPath` in `config.go` for the full list. New keys should follow the deterministic rule (`KANDEV_<SECTION>_<KEYUPPERCASE>`) unless there is a reason to add an alias.
|
||||
|
||||
The `make dev` launcher uses `Dev` as the default browser title prefix, and PR
|
||||
preview environments use `Preview`. An explicit `KANDEV_WEB_TITLE_PREFIX`
|
||||
value overrides those environment defaults.
|
||||
|
||||
## Full `config.yaml` example
|
||||
|
||||
Every key shown here has a default - copying the whole file changes nothing. Use it as a starting point and delete what you don't need to override.
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
spec: docs/specs/dev-preview-title-prefixes/spec.md
|
||||
created: 2026-08-09
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Implementation Plan: Environment-specific browser tab title prefixes
|
||||
|
||||
## Overview
|
||||
|
||||
Reuse the merged PR #2459 title-prefix contract and supply environment-specific
|
||||
defaults at the launch boundaries. The development profile will provide `Dev`,
|
||||
the PR preview startup script will provide `Preview`, and the CLI supervisor
|
||||
will preserve the prefix across restarts. Focused Go, CLI, browser, and docs
|
||||
checks will cover the launcher contracts and the visible browser result.
|
||||
|
||||
## Backend and launchers
|
||||
|
||||
### Development profile
|
||||
|
||||
- `apps/backend/internal/profiles/profiles.yaml` (the target of the root
|
||||
`profiles.yaml` symlink): add `KANDEV_WEB_TITLE_PREFIX` with an empty `prod`
|
||||
and `e2e` value and `Dev` as the `dev` value.
|
||||
- `apps/backend/internal/profiles/profiles_test.go`: assert the dev profile
|
||||
supplies `Dev` while the existing profile precedence remains intact.
|
||||
|
||||
### PR preview launcher
|
||||
|
||||
- `apps/backend/cmd/preview/sprite_ops.go`: export
|
||||
`KANDEV_WEB_TITLE_PREFIX=Preview` in the generated preview startup script.
|
||||
- `apps/backend/cmd/preview/sprite_ops_test.go`: pin the generated script to
|
||||
the `Preview` prefix alongside its existing startup assertions.
|
||||
|
||||
### Supervisor environment contract
|
||||
|
||||
- `apps/cli/src/supervisor/manifest.ts`: add `KANDEV_WEB_TITLE_PREFIX` to the
|
||||
restart-manifest allowlist so any configured prefix survives a supervised
|
||||
backend restart.
|
||||
- `apps/cli/src/supervisor/manifest.test.ts`: verify the title-prefix variable
|
||||
is retained while arbitrary variables remain excluded.
|
||||
|
||||
## Frontend and browser behavior
|
||||
|
||||
No new frontend implementation is required. The existing PR #2459 path already
|
||||
renders the configured prefix in the server shell and applies it for the
|
||||
`/api/v1/app-state` boot path.
|
||||
|
||||
Add `apps/web/e2e/tests/system/title-prefix.spec.ts` to restart the fixture
|
||||
backend with the development profile and with an explicit preview prefix, then
|
||||
assert both visible browser titles through the UI. The test restores the worker
|
||||
environment afterward.
|
||||
|
||||
### Mobile parity contract
|
||||
|
||||
The affected surface is browser metadata (`document.title`), not layout,
|
||||
navigation, touch input, scrolling, or viewport-bound UI. The desktop and
|
||||
mobile outcomes are identical, so no mobile-specific interaction test is
|
||||
needed; the E2E test and existing title helper contract cover the same
|
||||
viewport-independent behavior.
|
||||
|
||||
## Public documentation
|
||||
|
||||
- `docs/public/configuration.md`: explain that `make dev` defaults the title
|
||||
prefix to `Dev` and PR previews use `Preview`, while explicit environment
|
||||
values still override defaults.
|
||||
- `docs/configuration.md`: keep the internal configuration reference aligned.
|
||||
|
||||
## Tests
|
||||
|
||||
- **What:** the development profile resolves the title prefix to `Dev` and
|
||||
leaves production/e2e defaults blank.
|
||||
**File:** `apps/backend/internal/profiles/profiles_test.go`.
|
||||
**How:** profile application unit test with isolated environment variables.
|
||||
- **What:** the preview startup script exports `Preview`.
|
||||
**File:** `apps/backend/cmd/preview/sprite_ops_test.go`.
|
||||
**How:** generated-script contract assertion.
|
||||
- **What:** the supervisor retains the title-prefix environment variable.
|
||||
**File:** `apps/cli/src/supervisor/manifest.test.ts`.
|
||||
**How:** allowlist unit test.
|
||||
- **What:** a configured runtime prefix is visible in the browser title.
|
||||
**File:** `apps/web/e2e/tests/system/title-prefix.spec.ts`.
|
||||
**How:** worker backend restart plus Playwright UI assertion.
|
||||
|
||||
## E2E Tests
|
||||
|
||||
- **Scenarios:** GIVEN a fixture backend restarted with the development profile,
|
||||
WHEN the browser opens Kandev, THEN the page title is `Dev Kandev`; GIVEN a
|
||||
fixture backend restarted with `KANDEV_WEB_TITLE_PREFIX=Preview`, THEN the
|
||||
page title is `Preview Kandev`.
|
||||
- **File:** `apps/web/e2e/tests/system/title-prefix.spec.ts`.
|
||||
- **What to verify:** the user-visible browser tab title after a fresh page
|
||||
navigation.
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `cd apps/backend && go test -tags fts5 ./internal/profiles ./cmd/preview` —
|
||||
passed, 30 tests in 2 packages.
|
||||
- `cd apps && pnpm --filter kandev test -- --run src/dev.test.ts src/supervisor/manifest.test.ts` —
|
||||
passed, 2 files and 6 tests.
|
||||
- `cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts` —
|
||||
passed, 2 Playwright tests (`Dev Kandev` and `Preview Kandev`).
|
||||
- `node --test scripts/validate-public-docs.test.mjs` — passed, 58 tests.
|
||||
- `node scripts/validate-public-docs.mjs` — passed, 41 published pages.
|
||||
- `pnpm exec prettier --check ...` — passed for the changed TypeScript files.
|
||||
- `git diff --check` — passed.
|
||||
|
||||
## Implementation Waves And Parallel Candidates
|
||||
|
||||
Wave 1 (sequential; the launch contracts and their tests are one cohesive
|
||||
change):
|
||||
|
||||
- [x] [task-01-launch-prefixes](task-01-launch-prefixes.md)
|
||||
|
||||
## Open Questions
|
||||
|
||||
None.
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: "01-launch-prefixes"
|
||||
title: "Set environment title prefixes"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/dev-preview-title-prefixes/spec.md"
|
||||
---
|
||||
|
||||
# Task 01: Set environment title prefixes
|
||||
|
||||
## Acceptance
|
||||
|
||||
- `make dev` selects a backend profile that produces `Dev Kandev` by default,
|
||||
while normal production/start and e2e profiles remain unprefixed unless an
|
||||
explicit environment override is supplied.
|
||||
- PR preview startup produces `Preview Kandev`, and the CLI supervisor retains
|
||||
`KANDEV_WEB_TITLE_PREFIX` when it writes and reuses its restart manifest.
|
||||
- Focused browser, Go, TypeScript, and public-doc checks pass for the new
|
||||
launcher behavior.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the focused unit checks:
|
||||
|
||||
```bash
|
||||
cd apps/backend && go test -tags fts5 ./internal/profiles ./cmd/preview
|
||||
cd ../ && pnpm --filter kandev test -- --run src/dev.test.ts src/supervisor/manifest.test.ts
|
||||
```
|
||||
|
||||
Run the browser contract against the managed production E2E build:
|
||||
|
||||
```bash
|
||||
cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts
|
||||
```
|
||||
|
||||
Validate public documentation:
|
||||
|
||||
```bash
|
||||
node --test scripts/validate-public-docs.test.mjs
|
||||
node scripts/validate-public-docs.mjs
|
||||
```
|
||||
|
||||
On a fresh worktree, install workspace dependencies first with
|
||||
`cd apps && pnpm install --frozen-lockfile`.
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/backend/internal/profiles/profiles.yaml`
|
||||
- `apps/backend/internal/profiles/profiles_test.go`
|
||||
- `apps/backend/cmd/preview/sprite_ops.go`
|
||||
- `apps/backend/cmd/preview/sprite_ops_test.go`
|
||||
- `apps/cli/src/supervisor/manifest.ts`
|
||||
- `apps/cli/src/supervisor/manifest.test.ts`
|
||||
- `apps/web/e2e/tests/system/title-prefix.spec.ts`
|
||||
- `docs/configuration.md`
|
||||
- `docs/public/configuration.md`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None.
|
||||
|
||||
## Parallelism
|
||||
|
||||
Sequential. The profile, preview script, supervisor allowlist, and browser test
|
||||
all implement one environment-to-title contract, and the tests share the same
|
||||
behavioral acceptance criteria.
|
||||
|
||||
## Inputs
|
||||
|
||||
- `docs/specs/dev-preview-title-prefixes/spec.md`
|
||||
- `docs/plans/dev-preview-title-prefixes/plan.md`
|
||||
- The merged PR #2459 `KANDEV_WEB_TITLE_PREFIX` contract.
|
||||
- Existing profile precedence: explicit shell/launcher environment overrides
|
||||
profile defaults.
|
||||
- Existing backend and web E2E fixtures.
|
||||
|
||||
## Output contract
|
||||
|
||||
Report the changed files, exact test commands and results, any E2E cleanup, and
|
||||
whether the task and plan statuses were updated. Record blockers and remaining
|
||||
risks explicitly.
|
||||
|
||||
## Results
|
||||
|
||||
Implemented the profile, preview launcher, and supervisor environment
|
||||
contracts. Added browser coverage for both environment defaults and updated the
|
||||
public and internal configuration references.
|
||||
|
||||
Verification completed:
|
||||
|
||||
- `cd apps/backend && go test -tags fts5 ./internal/profiles ./cmd/preview` —
|
||||
passed, 30 tests in 2 packages.
|
||||
- `cd apps && pnpm --filter kandev test -- --run src/dev.test.ts src/supervisor/manifest.test.ts` —
|
||||
passed, 2 files and 6 tests.
|
||||
- `cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts` —
|
||||
passed, 2 Playwright tests.
|
||||
- `node --test scripts/validate-public-docs.test.mjs` — passed, 58 tests.
|
||||
- `node scripts/validate-public-docs.mjs` — passed, 41 published pages.
|
||||
- `pnpm exec prettier --check ...` and `git diff --check` — passed.
|
||||
|
||||
The managed E2E runner restored the fixture backend after each test; no
|
||||
generated files are tracked in the worktree.
|
||||
@@ -58,7 +58,7 @@ Some common camelCase keys have explicit compatibility aliases. Use the document
|
||||
| `server.readTimeout` | `KANDEV_SERVER_READTIMEOUT` | `30` | HTTP read timeout in seconds. |
|
||||
| `server.writeTimeout` | `KANDEV_SERVER_WRITETIMEOUT` | `30` | HTTP write timeout in seconds. |
|
||||
| `server.webInternalUrl` | `KANDEV_WEB_INTERNAL_URL` | empty | Development reverse-proxy target for a separately running web app. Installed releases normally serve embedded assets. |
|
||||
| `server.webTitlePrefix` | `KANDEV_WEB_TITLE_PREFIX` | empty | Prefixes the browser tab title as `<prefix> Kandev` (for example `TEST` renders `TEST Kandev`), so several instances stay distinguishable in adjacent tabs. Empty keeps the plain `Kandev` title. |
|
||||
| `server.webTitlePrefix` | `KANDEV_WEB_TITLE_PREFIX` | empty | Prefixes the browser tab title as `<prefix> Kandev` (for example `TEST` renders `TEST Kandev`), so several instances stay distinguishable in adjacent tabs. `make dev` defaults to `Dev`; PR previews use `Preview`. An explicit value overrides these defaults. Empty keeps the plain `Kandev` title. |
|
||||
|
||||
The default host exposes the server on every interface even though the CLI prints a `localhost` URL. The current local product path must not be treated as an authenticated multi-user perimeter. For remote access, bind to loopback and use a trusted authenticated tunnel/proxy, or isolate the network at the deployment layer.
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
status: shipped
|
||||
created: 2026-08-09
|
||||
owner: Kandev
|
||||
---
|
||||
|
||||
# Environment-specific browser tab title prefixes
|
||||
|
||||
## Why
|
||||
|
||||
Developers often keep a local development instance and one or more pull-request
|
||||
preview instances open at the same time. Identical `Kandev` browser tabs make it
|
||||
easy to use the wrong instance.
|
||||
|
||||
## What
|
||||
|
||||
- A Kandev browser tab title uses the existing `<prefix> Kandev` composition.
|
||||
- A backend started in the development profile, including `make dev`, uses the
|
||||
default prefix `Dev`, so its tab title is `Dev Kandev`.
|
||||
- A PR preview environment uses the prefix `Preview`, so its tab title is
|
||||
`Preview Kandev`.
|
||||
- An explicitly supplied `KANDEV_WEB_TITLE_PREFIX` continues to take precedence
|
||||
over a profile default. Its value is trimmed by the existing title-prefix
|
||||
contract.
|
||||
- A normal production/start launch with no explicit prefix keeps the plain
|
||||
`Kandev` title.
|
||||
- The prefix is present both on the initial server-rendered page and after a
|
||||
client boot from `/api/v1/app-state`, using the title behavior delivered by
|
||||
PR #2459.
|
||||
- A supervised backend restart keeps the configured prefix.
|
||||
|
||||
## API surface
|
||||
|
||||
The existing environment contract remains the public configuration surface:
|
||||
|
||||
| Environment | `KANDEV_WEB_TITLE_PREFIX` | Result |
|
||||
|---|---|---|
|
||||
| Development profile (`make dev`) | `Dev` by default | `Dev Kandev` |
|
||||
| PR preview launcher | `Preview` | `Preview Kandev` |
|
||||
| Normal start with no override | unset | `Kandev` |
|
||||
| Any launch with an explicit override | caller value | `<value> Kandev` |
|
||||
|
||||
The prefix is process configuration. It is not stored in the database and is
|
||||
not changed from the Kandev UI.
|
||||
|
||||
## Failure modes
|
||||
|
||||
- If the prefix is unset or blank, the title remains `Kandev`.
|
||||
- If the preview launcher cannot set the environment variable, the preview
|
||||
falls back to the existing plain-title behavior rather than failing startup.
|
||||
- If a supervised restart occurs, the launcher preserves the allowlisted prefix
|
||||
in the restart manifest so the restarted backend does not silently change its
|
||||
title.
|
||||
|
||||
## Persistence guarantees
|
||||
|
||||
The title prefix lasts for the lifetime of the launched environment and its
|
||||
supervised restarts. It is not persisted across separate launches unless the
|
||||
caller supplies the same environment or profile again.
|
||||
|
||||
## Scenarios
|
||||
|
||||
- **GIVEN** a clean checkout with no title-prefix override, **WHEN** the user
|
||||
runs `make dev` and opens the Kandev URL, **THEN** the browser title is
|
||||
`Dev Kandev`.
|
||||
- **GIVEN** a PR preview deployment, **WHEN** its startup service launches
|
||||
Kandev, **THEN** the browser title is `Preview Kandev`.
|
||||
- **GIVEN** a development profile and `KANDEV_WEB_TITLE_PREFIX=Custom`, **WHEN**
|
||||
the backend starts, **THEN** the browser title is `Custom Kandev`.
|
||||
- **GIVEN** a normal start with no title-prefix environment variable, **WHEN**
|
||||
the user opens Kandev, **THEN** the browser title is `Kandev`.
|
||||
- **GIVEN** a preview or development backend with a configured prefix, **WHEN**
|
||||
its supervisor restarts the backend, **THEN** the browser title retains the
|
||||
same prefix.
|
||||
- **GIVEN** a phone-sized browser viewport, **WHEN** either environment is
|
||||
opened, **THEN** the same title is shown; no viewport-specific interaction is
|
||||
required.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Changing the title dynamically from the Kandev UI.
|
||||
- Changing desktop window titles, CLI output, logs, or task titles.
|
||||
- Automatically labeling arbitrary self-hosted instances beyond the existing
|
||||
explicit environment variable.
|
||||
Reference in New Issue
Block a user