fix: separate debug and development launch profiles (#2487)
This commit is contained in:
+14
-4
@@ -7,6 +7,16 @@ GO := go
|
|||||||
GOFLAGS := -v
|
GOFLAGS := -v
|
||||||
LDFLAGS := -s -w
|
LDFLAGS := -s -w
|
||||||
|
|
||||||
|
# Debug launches get a distinct browser identity by default. An explicit
|
||||||
|
# environment or command-line value still wins through Make's `?=` semantics.
|
||||||
|
KANDEV_WEB_TITLE_PREFIX ?= Debug
|
||||||
|
|
||||||
|
# Quote the debug title prefix for the POSIX recipe. The replacement keeps
|
||||||
|
# embedded single quotes safe while preserving spaces and shell metacharacters.
|
||||||
|
single_quote := '
|
||||||
|
single_quote_escape := '\''
|
||||||
|
shell_quote = $(single_quote)$(subst $(single_quote),$(single_quote_escape),$(1))$(single_quote)
|
||||||
|
|
||||||
# Cross-platform commands
|
# Cross-platform commands
|
||||||
#
|
#
|
||||||
# Two independent axes decide these variables; conflating them breaks Git
|
# Two independent axes decide these variables; conflating them breaks Git
|
||||||
@@ -50,8 +60,8 @@ ifeq ($(OS)$(MSYSTEM),Windows_NT)
|
|||||||
GO_DARWIN_AMD64 = set CGO_ENABLED=0& set GOOS=darwin& set GOARCH=amd64&
|
GO_DARWIN_AMD64 = set CGO_ENABLED=0& set GOOS=darwin& set GOARCH=amd64&
|
||||||
# Run-target env prefixes and binary invocation. cmd.exe has no `NAME=VAL cmd`
|
# Run-target env prefixes and binary invocation. cmd.exe has no `NAME=VAL cmd`
|
||||||
# syntax (use `set NAME=VAL&`) and no `exec`; the binary path needs backslashes.
|
# syntax (use `set NAME=VAL&`) and no `exec`; the binary path needs backslashes.
|
||||||
DEV_ENV = set KANDEV_DEBUG_PPROF_ENABLED=true&
|
DEV_ENV = set KANDEV_DEBUG_DEV_MODE=true&
|
||||||
DEBUG_ENV = set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug&
|
DEBUG_ENV = set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug& set "KANDEV_WEB_TITLE_PREFIX=$(KANDEV_WEB_TITLE_PREFIX)"&
|
||||||
EXEC =
|
EXEC =
|
||||||
RUN_BIN = "$(subst /,\,$(BUILD_DIR)/$(BINARY_NAME)$(EXE))"
|
RUN_BIN = "$(subst /,\,$(BUILD_DIR)/$(BINARY_NAME)$(EXE))"
|
||||||
# check-make-shells is a bash script; native cmd.exe can't run its shebang, so
|
# check-make-shells is a bash script; native cmd.exe can't run its shebang, so
|
||||||
@@ -75,8 +85,8 @@ else
|
|||||||
GO_LINUX_ARM64 = CGO_ENABLED=0 GOOS=linux GOARCH=arm64
|
GO_LINUX_ARM64 = CGO_ENABLED=0 GOOS=linux GOARCH=arm64
|
||||||
GO_DARWIN_ARM64 = CGO_ENABLED=0 GOOS=darwin GOARCH=arm64
|
GO_DARWIN_ARM64 = CGO_ENABLED=0 GOOS=darwin GOARCH=arm64
|
||||||
GO_DARWIN_AMD64 = CGO_ENABLED=0 GOOS=darwin GOARCH=amd64
|
GO_DARWIN_AMD64 = CGO_ENABLED=0 GOOS=darwin GOARCH=amd64
|
||||||
DEV_ENV = KANDEV_DEBUG_PPROF_ENABLED=true
|
DEV_ENV = KANDEV_DEBUG_DEV_MODE=true
|
||||||
DEBUG_ENV = KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug
|
DEBUG_ENV = KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX=$(call shell_quote,$(KANDEV_WEB_TITLE_PREFIX))
|
||||||
EXEC = exec
|
EXEC = exec
|
||||||
RUN_BIN = ./$(BUILD_DIR)/$(BINARY_NAME)$(EXE)
|
RUN_BIN = ./$(BUILD_DIR)/$(BINARY_NAME)$(EXE)
|
||||||
CHECK_SHELLS = @../../scripts/check-make-shells
|
CHECK_SHELLS = @../../scripts/check-make-shells
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const healthTokenBytes = 32
|
const healthTokenBytes = 32
|
||||||
@@ -30,6 +31,7 @@ func backendEnv(ports portConfig, logLevel, consoleLogLevel string, debug bool,
|
|||||||
if debug {
|
if debug {
|
||||||
env = upsertEnv(env, "KANDEV_DEBUG_AGENT_MESSAGES", "true")
|
env = upsertEnv(env, "KANDEV_DEBUG_AGENT_MESSAGES", "true")
|
||||||
env = upsertEnv(env, "KANDEV_DEBUG_PPROF_ENABLED", "true")
|
env = upsertEnv(env, "KANDEV_DEBUG_PPROF_ENABLED", "true")
|
||||||
|
env = setEnvIfUnset(env, "KANDEV_WEB_TITLE_PREFIX", "Debug")
|
||||||
}
|
}
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
@@ -37,10 +39,20 @@ func backendEnv(ports portConfig, logLevel, consoleLogLevel string, debug bool,
|
|||||||
func upsertEnv(env []string, key, value string) []string {
|
func upsertEnv(env []string, key, value string) []string {
|
||||||
prefix := key + "="
|
prefix := key + "="
|
||||||
for i, item := range env {
|
for i, item := range env {
|
||||||
if len(item) >= len(prefix) && item[:len(prefix)] == prefix {
|
if strings.HasPrefix(item, prefix) {
|
||||||
env[i] = prefix + value
|
env[i] = prefix + value
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return append(env, prefix+value)
|
return append(env, prefix+value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setEnvIfUnset(env []string, key, value string) []string {
|
||||||
|
prefix := key + "="
|
||||||
|
for _, item := range env {
|
||||||
|
if strings.HasPrefix(item, prefix) {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(env, prefix+value)
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,6 +56,24 @@ func TestBackendEnvCarriesIndependentThresholds(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackendEnvDebugDefaultsToDebugTitle(t *testing.T) {
|
||||||
|
unsetEnvForTest(t, "KANDEV_WEB_TITLE_PREFIX")
|
||||||
|
|
||||||
|
env := backendEnv(portConfig{BackendPort: 1234, AgentctlPort: 5678}, "debug", "warn", true, "health-token")
|
||||||
|
if got := envValue(env, "KANDEV_WEB_TITLE_PREFIX"); got != "Debug" {
|
||||||
|
t.Fatalf("KANDEV_WEB_TITLE_PREFIX = %q for debug launch; want %q", got, "Debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackendEnvDebugPreservesExplicitTitle(t *testing.T) {
|
||||||
|
t.Setenv("KANDEV_WEB_TITLE_PREFIX", "Custom")
|
||||||
|
|
||||||
|
env := backendEnv(portConfig{BackendPort: 1234, AgentctlPort: 5678}, "debug", "warn", true, "health-token")
|
||||||
|
if got := envValue(env, "KANDEV_WEB_TITLE_PREFIX"); got != "Custom" {
|
||||||
|
t.Fatalf("KANDEV_WEB_TITLE_PREFIX = %q for explicit debug launch; want %q", got, "Custom")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewHealthTokenIsFreshAndOpaque(t *testing.T) {
|
func TestNewHealthTokenIsFreshAndOpaque(t *testing.T) {
|
||||||
first, err := newHealthToken()
|
first, err := newHealthToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -192,3 +210,28 @@ func TestRunManagedAppAttachesSignalsBeforeBackendLaunch(t *testing.T) {
|
|||||||
t.Fatalf("health token launched=%q waited=%q, want one shared non-empty token", launchedHealthToken, waitedHealthToken)
|
t.Fatalf("health token launched=%q waited=%q, want one shared non-empty token", launchedHealthToken, waitedHealthToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func envValue(env []string, key string) string {
|
||||||
|
prefix := key + "="
|
||||||
|
for _, item := range env {
|
||||||
|
if strings.HasPrefix(item, prefix) {
|
||||||
|
return strings.TrimPrefix(item, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsetEnvForTest(t *testing.T, key string) {
|
||||||
|
t.Helper()
|
||||||
|
value, wasSet := os.LookupEnv(key)
|
||||||
|
if err := os.Unsetenv(key); err != nil {
|
||||||
|
t.Fatalf("unset %s: %v", key, err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if wasSet {
|
||||||
|
_ = os.Setenv(key, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = os.Unsetenv(key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -176,6 +176,7 @@ func allowedSupervisorEnv(env []string) map[string]string {
|
|||||||
"KANDEV_AGENT_STANDALONE_PORT": true,
|
"KANDEV_AGENT_STANDALONE_PORT": true,
|
||||||
"KANDEV_LOG_LEVEL": true,
|
"KANDEV_LOG_LEVEL": true,
|
||||||
"KANDEV_CONSOLE_LOG_LEVEL": true,
|
"KANDEV_CONSOLE_LOG_LEVEL": true,
|
||||||
|
"KANDEV_WEB_TITLE_PREFIX": true,
|
||||||
"KANDEV_DEBUG_DEV_MODE": true,
|
"KANDEV_DEBUG_DEV_MODE": true,
|
||||||
"KANDEV_DEBUG_AGENT_MESSAGES": true,
|
"KANDEV_DEBUG_AGENT_MESSAGES": true,
|
||||||
"KANDEV_DEBUG_PPROF_ENABLED": true,
|
"KANDEV_DEBUG_PPROF_ENABLED": true,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func TestBuildManifestUsesSameBinaryBackendMode(t *testing.T) {
|
|||||||
"KANDEV_SERVER_PORT=1234",
|
"KANDEV_SERVER_PORT=1234",
|
||||||
"KANDEV_DESKTOP_HEALTH_TOKEN=health-token",
|
"KANDEV_DESKTOP_HEALTH_TOKEN=health-token",
|
||||||
"KANDEV_CONSOLE_LOG_LEVEL=warn",
|
"KANDEV_CONSOLE_LOG_LEVEL=warn",
|
||||||
|
"KANDEV_WEB_TITLE_PREFIX=Debug",
|
||||||
"UNRELATED=value",
|
"UNRELATED=value",
|
||||||
}
|
}
|
||||||
manifest := buildManifest("/opt/kandev/bin/kandev", []string{"__backend"}, "/opt/kandev/bin", env, "/tmp/home", 1234, "run")
|
manifest := buildManifest("/opt/kandev/bin/kandev", []string{"__backend"}, "/opt/kandev/bin", env, "/tmp/home", 1234, "run")
|
||||||
@@ -35,6 +36,9 @@ func TestBuildManifestUsesSameBinaryBackendMode(t *testing.T) {
|
|||||||
if manifest.Env["KANDEV_DESKTOP_HEALTH_TOKEN"] != "health-token" {
|
if manifest.Env["KANDEV_DESKTOP_HEALTH_TOKEN"] != "health-token" {
|
||||||
t.Fatalf("KANDEV_DESKTOP_HEALTH_TOKEN = %q", manifest.Env["KANDEV_DESKTOP_HEALTH_TOKEN"])
|
t.Fatalf("KANDEV_DESKTOP_HEALTH_TOKEN = %q", manifest.Env["KANDEV_DESKTOP_HEALTH_TOKEN"])
|
||||||
}
|
}
|
||||||
|
if manifest.Env["KANDEV_WEB_TITLE_PREFIX"] != "Debug" {
|
||||||
|
t.Fatalf("KANDEV_WEB_TITLE_PREFIX = %q", manifest.Env["KANDEV_WEB_TITLE_PREFIX"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrepareSupervisorEnvWritesExpectedPaths(t *testing.T) {
|
func TestPrepareSupervisorEnvWritesExpectedPaths(t *testing.T) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func DetectEnvironment() Environment {
|
|||||||
if isTruthy(os.Getenv("KANDEV_E2E_MOCK")) {
|
if isTruthy(os.Getenv("KANDEV_E2E_MOCK")) {
|
||||||
return EnvE2E
|
return EnvE2E
|
||||||
}
|
}
|
||||||
if isTruthy(os.Getenv("KANDEV_DEBUG_DEV_MODE")) || isTruthy(os.Getenv("KANDEV_DEBUG_PPROF_ENABLED")) {
|
if isTruthy(os.Getenv("KANDEV_DEBUG_DEV_MODE")) {
|
||||||
return EnvDev
|
return EnvDev
|
||||||
}
|
}
|
||||||
return EnvProd
|
return EnvProd
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ debug:
|
|||||||
dev: "true"
|
dev: "true"
|
||||||
e2e: ""
|
e2e: ""
|
||||||
|
|
||||||
# Legacy alias for KANDEV_DEBUG_DEV_MODE — kept for compatibility
|
# Legacy debug behavior variable — kept for compatibility with older
|
||||||
# with older deploy scripts that grep for "pprof".
|
# deploy scripts that enable pprof. It does not select the dev profile.
|
||||||
KANDEV_DEBUG_PPROF_ENABLED:
|
KANDEV_DEBUG_PPROF_ENABLED:
|
||||||
prod: ""
|
prod: ""
|
||||||
dev: "true"
|
dev: "true"
|
||||||
|
|||||||
@@ -94,6 +94,26 @@ func TestApplyProfile_DevUsesDevelopmentDefaults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyProfile_PprofDebugKeepsProductionProfile(t *testing.T) {
|
||||||
|
clearProfileSelectors(t)
|
||||||
|
clearProfilesYAMLVars(t)
|
||||||
|
t.Setenv("KANDEV_DEBUG_PPROF_ENABLED", "true")
|
||||||
|
|
||||||
|
_, env, err := ApplyProfile()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ApplyProfile: %v", err)
|
||||||
|
}
|
||||||
|
if env != EnvProd {
|
||||||
|
t.Fatalf("env = %q with pprof-only debug; want %q", env, EnvProd)
|
||||||
|
}
|
||||||
|
if v := os.Getenv("KANDEV_FEATURES_OFFICE"); v != "false" {
|
||||||
|
t.Errorf("KANDEV_FEATURES_OFFICE = %q with pprof-only debug; want %q", v, "false")
|
||||||
|
}
|
||||||
|
if _, ok := os.LookupEnv("KANDEV_WEB_TITLE_PREFIX"); ok {
|
||||||
|
t.Error("KANDEV_WEB_TITLE_PREFIX was set by the production profile; want it unset")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestApplyProfile_ExplicitTitlePrefixWinsInDev(t *testing.T) {
|
func TestApplyProfile_ExplicitTitlePrefixWinsInDev(t *testing.T) {
|
||||||
clearProfileSelectors(t)
|
clearProfileSelectors(t)
|
||||||
clearProfilesYAMLVars(t)
|
clearProfilesYAMLVars(t)
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
import { expect, test } from "../../fixtures/test-base";
|
import { expect, test } from "../../fixtures/test-base";
|
||||||
|
|
||||||
test.describe.serial("environment browser title prefixes", () => {
|
test.describe.serial("environment browser title prefixes", () => {
|
||||||
|
test("uses Debug for a diagnostics-only production profile", async ({ backend, testPage }) => {
|
||||||
|
await backend.restart({
|
||||||
|
KANDEV_E2E_MOCK: "",
|
||||||
|
KANDEV_DEBUG_DEV_MODE: "",
|
||||||
|
KANDEV_DEBUG_PPROF_ENABLED: "true",
|
||||||
|
KANDEV_WEB_TITLE_PREFIX: "Debug",
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await testPage.goto("/");
|
||||||
|
await expect(testPage).toHaveTitle("Debug Kandev");
|
||||||
|
} finally {
|
||||||
|
await backend.restart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("uses Dev for the development profile", async ({ backend, testPage }) => {
|
test("uses Dev for the development profile", async ({ backend, testPage }) => {
|
||||||
await backend.restart({ KANDEV_E2E_MOCK: "", KANDEV_DEBUG_DEV_MODE: "true" });
|
await backend.restart({ KANDEV_E2E_MOCK: "", KANDEV_DEBUG_DEV_MODE: "true" });
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +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 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
|
The `make dev` launcher uses `Dev` as the default browser title prefix. The
|
||||||
preview environments use `Preview`. An explicit `KANDEV_WEB_TITLE_PREFIX`
|
`make start-debug` launcher keeps production defaults, enables diagnostics, and
|
||||||
value overrides those environment defaults.
|
uses `Debug`. PR preview environments use `Preview`. An explicit
|
||||||
|
`KANDEV_WEB_TITLE_PREFIX` value overrides those environment defaults.
|
||||||
|
|
||||||
## Full `config.yaml` example
|
## Full `config.yaml` example
|
||||||
|
|
||||||
|
|||||||
@@ -57,9 +57,15 @@ Top-level sections are purely for human readability (the loader walks every leaf
|
|||||||
| signal | profile | who sets it |
|
| signal | profile | who sets it |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `KANDEV_E2E_MOCK=true` | `e2e` | `apps/web/e2e/fixtures/backend.ts` |
|
| `KANDEV_E2E_MOCK=true` | `e2e` | `apps/web/e2e/fixtures/backend.ts` |
|
||||||
| `KANDEV_DEBUG_DEV_MODE=true` (or `…_PPROF_ENABLED=true`) | `dev` | `apps/cli/src/dev.ts` |
|
| `KANDEV_DEBUG_DEV_MODE=true` | `dev` | `apps/cli/src/dev.ts`, the `make dev` backend target |
|
||||||
| (neither) | `prod` | the default for any other launch |
|
| (neither) | `prod` | the default for any other launch |
|
||||||
|
|
||||||
|
`KANDEV_DEBUG_PPROF_ENABLED` is a legacy debug behavior variable. It enables
|
||||||
|
pprof when a launcher sets it, but it does not select the `dev` profile. This
|
||||||
|
keeps `make start-debug` on the production profile while it retains debug
|
||||||
|
diagnostics; that launcher supplies the explicit `Debug` title prefix. See
|
||||||
|
[ADR-2026-08-10-debug-launcher-profile-selection](2026-08-10-debug-launcher-profile-selection.md).
|
||||||
|
|
||||||
`profiles.ApplyProfile()` then walks every leaf and calls `os.Setenv` **only when the var is not already set** and **only when the resolved value is non-empty**. Empty means "leave unset."
|
`profiles.ApplyProfile()` then walks every leaf and calls `os.Setenv` **only when the var is not already set** and **only when the resolved value is non-empty**. Empty means "leave unset."
|
||||||
|
|
||||||
This produces the precedence chain:
|
This produces the precedence chain:
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# ADR-2026-08-10: Keep Debug Launches on the Production Profile
|
||||||
|
|
||||||
|
**Status:** accepted
|
||||||
|
**Date:** 2026-08-10
|
||||||
|
**Area:** backend, frontend, cli
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
The native `--debug` launcher sets `KANDEV_DEBUG_PPROF_ENABLED=true` and
|
||||||
|
`KANDEV_DEBUG_AGENT_MESSAGES=true`. Profile detection also treated the pprof
|
||||||
|
variable as the development-profile selector. As a result, `make start-debug`
|
||||||
|
received development defaults and the `Dev Kandev` browser title.
|
||||||
|
|
||||||
|
The debug launcher needs diagnostic behavior without development-only defaults.
|
||||||
|
The source-checkout `make dev` path already has a canonical development
|
||||||
|
selector, `KANDEV_DEBUG_DEV_MODE=true`.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Use only `KANDEV_DEBUG_DEV_MODE=true` as the development-profile selector.
|
||||||
|
Keep `KANDEV_DEBUG_PPROF_ENABLED` as a legacy behavior variable for pprof and
|
||||||
|
related debug compatibility. It does not select the `dev` profile.
|
||||||
|
|
||||||
|
The launch contracts are:
|
||||||
|
|
||||||
|
- `make dev` selects `dev` and uses `Dev Kandev`.
|
||||||
|
- `make start-debug` enables debug diagnostics, selects `prod`, and uses
|
||||||
|
`Debug Kandev` by default. An explicit title prefix still wins.
|
||||||
|
- `KANDEV_E2E_MOCK=true` keeps its existing priority and selects `e2e`.
|
||||||
|
|
||||||
|
The browser title continues to use `KANDEV_WEB_TITLE_PREFIX`. The debug
|
||||||
|
launcher supplies `Debug` as its default prefix without adding a new profile.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Debugging a release-style build no longer enables development feature defaults
|
||||||
|
or the development browser identity. It has the distinct `Debug Kandev` title
|
||||||
|
while retaining production defaults. Contributors who need the development
|
||||||
|
profile use `make dev` or set `KANDEV_DEBUG_DEV_MODE=true`.
|
||||||
|
|
||||||
|
The legacy pprof variable remains available for older launch scripts. Tests and
|
||||||
|
launcher checks must keep the selector and debug behavior separate.
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
1. Set only an empty title prefix in `make start-debug`. Rejected because it
|
||||||
|
fixes the browser label but leaves other development defaults enabled and
|
||||||
|
does not identify a diagnostics-only launch.
|
||||||
|
2. Keep pprof as a profile selector. Rejected because pprof is a diagnostic
|
||||||
|
behavior, not a development-environment identity.
|
||||||
|
3. Remove the pprof variable. Rejected because existing launchers and config
|
||||||
|
users depend on the compatibility alias.
|
||||||
@@ -142,3 +142,4 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
|
|||||||
| 2026-08-08-task-owned-worktree-lifetime | [Keep Worktree Ownership at the Task Lifecycle](2026-08-08-task-owned-worktree-lifetime.md) | accepted | backend, frontend | 2026-08-08 |
|
| 2026-08-08-task-owned-worktree-lifetime | [Keep Worktree Ownership at the Task Lifecycle](2026-08-08-task-owned-worktree-lifetime.md) | accepted | backend, frontend | 2026-08-08 |
|
||||||
| 2026-08-09-bind-automation-mutations-to-event-targets | [Bind Automation Mutations to Event Targets](2026-08-09-bind-automation-mutations-to-event-targets.md) | accepted | backend, agentctl, protocol, security, workflow | 2026-08-09 |
|
| 2026-08-09-bind-automation-mutations-to-event-targets | [Bind Automation Mutations to Event Targets](2026-08-09-bind-automation-mutations-to-event-targets.md) | accepted | backend, agentctl, protocol, security, workflow | 2026-08-09 |
|
||||||
| 2026-08-09-exclusive-runtime-state-ownership | [Lock Runtime State Before Backend Startup](2026-08-09-exclusive-runtime-state-ownership.md) | accepted | backend, infra | 2026-08-09 |
|
| 2026-08-09-exclusive-runtime-state-ownership | [Lock Runtime State Before Backend Startup](2026-08-09-exclusive-runtime-state-ownership.md) | accepted | backend, infra | 2026-08-09 |
|
||||||
|
| 2026-08-10-debug-launcher-profile-selection | [Keep Debug Launches on the Production Profile](2026-08-10-debug-launcher-profile-selection.md) | accepted | backend, frontend, cli | 2026-08-10 |
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
spec: docs/specs/dev-preview-title-prefixes/spec.md
|
||||||
|
created: 2026-08-10
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Implementation Plan: Keep Debug Start on the Production Profile
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`make start-debug` sets the legacy pprof debug variable. The profile loader
|
||||||
|
currently treats that variable as a development selector, so it applies the
|
||||||
|
`Dev` title prefix. Make `KANDEV_DEBUG_DEV_MODE` the only development selector,
|
||||||
|
keep pprof as debug behavior, and preserve `Dev Kandev` for `make dev`. The
|
||||||
|
debug launcher supplies `Debug` as the default title prefix, producing
|
||||||
|
`Debug Kandev` without selecting the development profile.
|
||||||
|
|
||||||
|
The work proceeds from the backend profile contract to the launcher checks and
|
||||||
|
then to the browser regression test and public documentation.
|
||||||
|
|
||||||
|
The confirmed root cause is in `profiles.DetectEnvironment`: its dev branch
|
||||||
|
checks `KANDEV_DEBUG_PPROF_ENABLED` even though the release debug launcher sets
|
||||||
|
that variable without selecting the development profile.
|
||||||
|
|
||||||
|
## Backend and launchers
|
||||||
|
|
||||||
|
### Profile selection
|
||||||
|
|
||||||
|
- `apps/backend/internal/profiles/profiles.go`: make
|
||||||
|
`KANDEV_DEBUG_DEV_MODE=true` the only non-e2e signal for the `dev` profile.
|
||||||
|
Keep `KANDEV_DEBUG_PPROF_ENABLED` available to the debug configuration and
|
||||||
|
pprof routes.
|
||||||
|
- `apps/backend/internal/profiles/profiles_test.go`: add a pprof-only
|
||||||
|
regression case that resolves to `prod`, keeps the title prefix out of the
|
||||||
|
profile defaults, and keeps production feature defaults.
|
||||||
|
|
||||||
|
### Debug title default
|
||||||
|
|
||||||
|
- `apps/backend/internal/launcher/env.go`: when the native debug launcher is
|
||||||
|
used, supply `KANDEV_WEB_TITLE_PREFIX=Debug` only when the caller has not
|
||||||
|
already supplied a prefix. Keep the explicit environment override contract.
|
||||||
|
- `apps/backend/internal/launcher/start_test.go`: verify the debug launcher
|
||||||
|
supplies `Debug` by default and preserves an explicit prefix.
|
||||||
|
- `apps/backend/internal/launcher/supervisor.go`: preserve the title prefix in
|
||||||
|
the restart manifest so a supervised restart keeps `Debug Kandev`.
|
||||||
|
- `apps/backend/internal/launcher/supervisor_test.go`: cover the allowlisted
|
||||||
|
title prefix in the restart manifest.
|
||||||
|
|
||||||
|
### Make targets
|
||||||
|
|
||||||
|
- `apps/backend/Makefile`: make the direct `dev` target export the canonical
|
||||||
|
`KANDEV_DEBUG_DEV_MODE=true` selector. Make the direct `start-debug` target
|
||||||
|
default to the `Debug` prefix while preserving an explicitly supplied
|
||||||
|
`KANDEV_WEB_TITLE_PREFIX`. The debug target continues to export pprof and
|
||||||
|
debug logging without the dev selector.
|
||||||
|
- `scripts/check-make-shells`: update the expected `dev` command for Unix,
|
||||||
|
native Windows, and Git Bash/MSYS, plus the direct `start-debug` title
|
||||||
|
environment.
|
||||||
|
|
||||||
|
### Durable decision record
|
||||||
|
|
||||||
|
- `docs/decisions/0007-runtime-feature-flags.md`: reconcile the profile
|
||||||
|
selector table with the corrected contract.
|
||||||
|
- `docs/decisions/2026-08-10-debug-launcher-profile-selection.md`: preserve the
|
||||||
|
reason for separating diagnostic behavior from environment identity.
|
||||||
|
- `docs/decisions/INDEX.md`: index the new decision.
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
No frontend production code changes are required. The existing server-shell
|
||||||
|
and SPA title composition already uses `KANDEV_WEB_TITLE_PREFIX` correctly.
|
||||||
|
|
||||||
|
The existing title-prefix browser test will cover the debug-start result. This
|
||||||
|
is browser metadata, not a layout or interaction surface. Desktop and phone
|
||||||
|
viewports therefore have the same expected title, and no mobile-specific
|
||||||
|
interaction test is required.
|
||||||
|
|
||||||
|
## Public documentation
|
||||||
|
|
||||||
|
- `docs/public/configuration.md`: state that `make start-debug` keeps the
|
||||||
|
production profile and uses the `Debug Kandev` title while enabling
|
||||||
|
diagnostics.
|
||||||
|
- `docs/public/cli.md`: explain that `--debug` does not select the development
|
||||||
|
profile; `make dev` is the development-profile launcher.
|
||||||
|
- `docs/configuration.md`: keep the internal configuration reference aligned.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
- **What:** pprof-only startup selects `prod` and does not apply `Dev`.
|
||||||
|
**File:** `apps/backend/internal/profiles/profiles_test.go`.
|
||||||
|
**How:** isolated environment unit test through `ApplyProfile`.
|
||||||
|
- **What:** the native debug launcher supplies the `Debug` prefix by default
|
||||||
|
and preserves an explicit prefix.
|
||||||
|
**File:** `apps/backend/internal/launcher/start_test.go`.
|
||||||
|
**How:** inspect the environment assembled for debug and non-debug starts.
|
||||||
|
- **What:** supervised restarts preserve the debug title prefix.
|
||||||
|
**File:** `apps/backend/internal/launcher/supervisor_test.go`.
|
||||||
|
**How:** assert the debug prefix survives the restart manifest allowlist.
|
||||||
|
- **What:** direct `make dev` emits the canonical dev selector across shells.
|
||||||
|
**File:** `scripts/check-make-shells`.
|
||||||
|
**How:** run the existing deterministic `make -n` shell matrix.
|
||||||
|
- **What:** the debug-start launcher uses the distinct debug title.
|
||||||
|
**File:** `apps/web/e2e/tests/system/title-prefix.spec.ts`.
|
||||||
|
**How:** restart the fixture with pprof enabled and both profile selectors
|
||||||
|
disabled plus the launcher's `Debug` prefix, navigate to `/`, and assert
|
||||||
|
`Debug Kandev`.
|
||||||
|
- **What:** development and preview title behavior remains unchanged.
|
||||||
|
**File:** `apps/web/e2e/tests/system/title-prefix.spec.ts`.
|
||||||
|
**How:** retain the existing `Dev Kandev` and `Preview Kandev` assertions.
|
||||||
|
|
||||||
|
## E2E Tests
|
||||||
|
|
||||||
|
- **Scenario:** GIVEN a backend restarted with pprof/debug behavior and no
|
||||||
|
development or e2e selector, WHEN the browser opens Kandev, THEN the title is
|
||||||
|
`Debug Kandev`.
|
||||||
|
- **Scenario:** GIVEN a backend restarted with the development selector, WHEN
|
||||||
|
the browser opens Kandev, THEN the title is `Dev Kandev`.
|
||||||
|
- **Scenario:** GIVEN a backend with an explicit `Preview` prefix, WHEN the
|
||||||
|
browser opens Kandev, THEN the title is `Preview Kandev`.
|
||||||
|
- **File:** `apps/web/e2e/tests/system/title-prefix.spec.ts`.
|
||||||
|
- **Mobile note:** `document.title` is viewport-independent. The existing
|
||||||
|
browser metadata assertion covers the same user outcome on a phone, so no
|
||||||
|
`mobile-*.spec.ts` file is needed.
|
||||||
|
|
||||||
|
## Verification Results
|
||||||
|
|
||||||
|
- `make test-backend` — passed (full backend test suite).
|
||||||
|
- `make lint-backend` — passed (0 issues).
|
||||||
|
- `make build-backend` — passed (backend binaries built).
|
||||||
|
- `cd apps/backend && make check-make-shells` — passed for Unix, native
|
||||||
|
Windows, and Git Bash/MSYS dispatch, including explicit prefixes with spaces.
|
||||||
|
- `cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts`
|
||||||
|
— passed (3 tests).
|
||||||
|
- `node --test scripts/validate-public-docs.test.mjs` — passed (58 tests).
|
||||||
|
- `node scripts/validate-public-docs.mjs` — passed (41 published docs pages).
|
||||||
|
- `git diff --check` — passed.
|
||||||
|
|
||||||
|
## Implementation Waves And Parallel Candidates
|
||||||
|
|
||||||
|
Wave 1 (sequential):
|
||||||
|
|
||||||
|
- [x] [task-01-profile-selector-contract](task-01-profile-selector-contract.md)
|
||||||
|
|
||||||
|
Wave 2 (sequential, depends on Wave 1):
|
||||||
|
|
||||||
|
- [x] [task-02-title-regression-docs](task-02-title-regression-docs.md)
|
||||||
|
|
||||||
|
Parallel delegation is not authorized by this plan. The work stays in the
|
||||||
|
primary session and follows the dependency order above.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
id: "01-profile-selector-contract"
|
||||||
|
title: "Separate debug profile selector"
|
||||||
|
status: done
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
plan: "plan.md"
|
||||||
|
spec: "../../specs/dev-preview-title-prefixes/spec.md"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task 01: Separate debug profile selector
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
- `KANDEV_DEBUG_PPROF_ENABLED=true` without `KANDEV_DEBUG_DEV_MODE=true` selects
|
||||||
|
`prod` and does not receive the `Dev` profile prefix.
|
||||||
|
- `KANDEV_DEBUG_DEV_MODE=true` still selects `dev` and supplies the `Dev`
|
||||||
|
title prefix.
|
||||||
|
- The native and direct backend `start-debug` launchers supply the `Debug`
|
||||||
|
prefix by default, while preserving an explicit prefix.
|
||||||
|
- A supervised backend restart preserves the configured title prefix.
|
||||||
|
- The direct backend `dev` target exports `KANDEV_DEBUG_DEV_MODE=true`, while
|
||||||
|
`start-debug` retains pprof and debug logging without that selector.
|
||||||
|
- The profile ADR and its index describe the corrected boundary.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `make test-backend`
|
||||||
|
- `make lint-backend`
|
||||||
|
- `make build-backend`
|
||||||
|
- `make check-make-shells`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
## Files likely touched
|
||||||
|
|
||||||
|
- `apps/backend/internal/profiles/profiles.go`
|
||||||
|
- `apps/backend/internal/profiles/profiles_test.go`
|
||||||
|
- `apps/backend/internal/launcher/env.go`
|
||||||
|
- `apps/backend/internal/launcher/start_test.go`
|
||||||
|
- `apps/backend/internal/launcher/supervisor.go`
|
||||||
|
- `apps/backend/internal/launcher/supervisor_test.go`
|
||||||
|
- `apps/backend/Makefile`
|
||||||
|
- `scripts/check-make-shells`
|
||||||
|
- `docs/decisions/0007-runtime-feature-flags.md`
|
||||||
|
- `docs/decisions/2026-08-10-debug-launcher-profile-selection.md`
|
||||||
|
- `docs/decisions/INDEX.md`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Parallelism
|
||||||
|
|
||||||
|
Sequential. The profile selector and launcher environment are one shared
|
||||||
|
startup contract.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
- The `What`, API surface, failure modes, and scenarios in the repair spec.
|
||||||
|
- The confirmed root cause in `plan.md`.
|
||||||
|
- `profiles.DetectEnvironment`, `profiles.ApplyProfile`, and the existing shell
|
||||||
|
dispatch harness.
|
||||||
|
|
||||||
|
## Output contract
|
||||||
|
|
||||||
|
Report the changed files, exact test commands and results, any risks, and the
|
||||||
|
updated task and plan statuses in the same conversation. Do not delegate this
|
||||||
|
task to a subagent without explicit user authorization.
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
- `make test-backend` — passed (full backend test suite).
|
||||||
|
- `make lint-backend` — passed (0 issues).
|
||||||
|
- `make build-backend` — passed (backend binaries built).
|
||||||
|
- `make check-make-shells` — passed (Unix, native Windows, and Git Bash/MSYS
|
||||||
|
dispatch, including explicit prefixes with spaces).
|
||||||
|
- `git diff --check` — passed.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
id: "02-title-regression-docs"
|
||||||
|
title: "Prove debug title behavior"
|
||||||
|
status: done
|
||||||
|
wave: 2
|
||||||
|
depends_on: ["01-profile-selector-contract"]
|
||||||
|
plan: "plan.md"
|
||||||
|
spec: "../../specs/dev-preview-title-prefixes/spec.md"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Task 02: Prove debug title behavior
|
||||||
|
|
||||||
|
## Acceptance
|
||||||
|
|
||||||
|
- The title-prefix E2E test asserts `Debug Kandev` for a pprof/debug restart
|
||||||
|
without development or e2e profile selectors.
|
||||||
|
- The existing `Dev Kandev` and `Preview Kandev` assertions continue to pass.
|
||||||
|
- Public and internal configuration docs explain the different `make dev` and
|
||||||
|
`make start-debug` title behavior.
|
||||||
|
- The viewport-independent metadata behavior has an explicit mobile-parity
|
||||||
|
justification. No touch or layout surface changes.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts`
|
||||||
|
- `node --test scripts/validate-public-docs.test.mjs`
|
||||||
|
- `node scripts/validate-public-docs.mjs`
|
||||||
|
- `git diff --check`
|
||||||
|
|
||||||
|
If the fresh worktree has no frontend dependencies, first run
|
||||||
|
`cd apps && pnpm install --frozen-lockfile`.
|
||||||
|
|
||||||
|
## Files likely touched
|
||||||
|
|
||||||
|
- `apps/web/e2e/tests/system/title-prefix.spec.ts`
|
||||||
|
- `docs/public/configuration.md`
|
||||||
|
- `docs/public/cli.md`
|
||||||
|
- `docs/configuration.md`
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Task 01 must pass before this task starts.
|
||||||
|
|
||||||
|
## Parallelism
|
||||||
|
|
||||||
|
Sequential. The browser assertion depends on the profile-selector behavior.
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
- The title scenarios in `docs/specs/dev-preview-title-prefixes/spec.md`.
|
||||||
|
- The mobile-parity conclusion in `plan.md`.
|
||||||
|
- Existing fixture restart behavior in `apps/web/e2e/fixtures/backend.ts`.
|
||||||
|
|
||||||
|
## Output contract
|
||||||
|
|
||||||
|
Report the changed files, exact E2E and documentation checks, cleanup status,
|
||||||
|
and the updated task and plan statuses in the same conversation.
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
- `cd apps/web && pnpm e2e:run --project chromium tests/system/title-prefix.spec.ts`
|
||||||
|
— passed (3 tests: Debug, Dev, and Preview titles).
|
||||||
|
- `node --test scripts/validate-public-docs.test.mjs` — passed (58 tests).
|
||||||
|
- `node scripts/validate-public-docs.mjs` — passed (41 published docs pages).
|
||||||
|
- `cd apps && pnpm exec prettier --check web/e2e/tests/system/title-prefix.spec.ts`
|
||||||
|
— passed.
|
||||||
|
- `git diff --check` — passed.
|
||||||
+9
-1
@@ -251,7 +251,15 @@ Flags take precedence over the equivalent port variables. `KANDEV_BACKEND_PORT`
|
|||||||
|
|
||||||
The launcher also sets the selected server and `agentctl` ports for the backend. Treat its supervisor socket and manifest under `<home>/supervisor/` as private implementation state, not a control API.
|
The launcher also sets the selected server and `agentctl` ports for the backend. Treat its supervisor socket and manifest under `<home>/supervisor/` as private implementation state, not a control API.
|
||||||
|
|
||||||
`--debug` sets `KANDEV_DEBUG_AGENT_MESSAGES=true` and `KANDEV_DEBUG_PPROF_ENABLED=true`. Debug events go to `<home>/logs/backend-logs.log`; warn and above still appear on stdout. ACP logs can contain full prompts, file content, and tool calls, while diagnostic endpoints expose process details. Use debug mode only on a trusted machine and remove retained debug logs afterward. [Configuration](./configuration.md) lists locations and retention.
|
`--debug` sets `KANDEV_DEBUG_AGENT_MESSAGES=true` and
|
||||||
|
`KANDEV_DEBUG_PPROF_ENABLED=true` without selecting the `dev` profile. The
|
||||||
|
`make start-debug` launcher also defaults the browser title to `Debug Kandev`;
|
||||||
|
`make dev` selects the development profile and uses `Dev Kandev`. Debug events
|
||||||
|
go to `<home>/logs/backend-logs.log`; warn and above still appear on stdout.
|
||||||
|
ACP logs can contain full prompts, file content, and tool calls, while
|
||||||
|
diagnostic endpoints expose process details. Use debug mode only on a trusted
|
||||||
|
machine and remove retained debug logs afterward. [Configuration](./configuration.md)
|
||||||
|
lists locations and retention.
|
||||||
|
|
||||||
## Data and cleanup
|
## Data and cleanup
|
||||||
|
|
||||||
|
|||||||
@@ -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.readTimeout` | `KANDEV_SERVER_READTIMEOUT` | `30` | HTTP read timeout in seconds. |
|
||||||
| `server.writeTimeout` | `KANDEV_SERVER_WRITETIMEOUT` | `30` | HTTP write 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.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. `make dev` defaults to `Dev`; PR previews use `Preview`. An explicit value overrides these defaults. 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`; `make start-debug` keeps production defaults, enables diagnostics, and defaults to `Debug`; 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.
|
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.
|
||||||
|
|
||||||
@@ -170,9 +170,17 @@ Discovery roots bound automatic filesystem traversal, so scope them narrowly. Th
|
|||||||
| YAML key | Environment variable | Default | Current behavior |
|
| YAML key | Environment variable | Default | Current behavior |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` | `false` | Enables diagnostic endpoints and agent-message debug logging. |
|
| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` | `false` | Enables diagnostic endpoints and agent-message debug logging. |
|
||||||
| `debug.pprofEnabled` | `KANDEV_DEBUG_PPROF_ENABLED` | `false` | Legacy alias; also enables debug mode. |
|
| `debug.pprofEnabled` | `KANDEV_DEBUG_PPROF_ENABLED` | `false` | Legacy diagnostics switch. It enables pprof behavior but does not select the `dev` profile. |
|
||||||
|
|
||||||
Debug mode is high risk. It enables local diagnostic surfaces and implies `KANDEV_DEBUG_AGENT_MESSAGES=true` and `KANDEV_DEBUG_PPROF_ENABLED=true` when not explicitly locked by the environment. ACP JSONL frames include complete prompts, file content, and tool calls. Do not enable it on a shared or network-exposed backend.
|
`KANDEV_DEBUG_DEV_MODE=true` selects the `dev` profile. `make dev` sets that
|
||||||
|
selector and defaults the browser title to `Dev Kandev`. `make start-debug`
|
||||||
|
enables pprof and debug logging without selecting the `dev` profile, and
|
||||||
|
defaults the browser title to `Debug Kandev`. Debug mode is high risk. It
|
||||||
|
enables local diagnostic surfaces and implies
|
||||||
|
`KANDEV_DEBUG_AGENT_MESSAGES=true` and `KANDEV_DEBUG_PPROF_ENABLED=true` when
|
||||||
|
not explicitly locked by the environment. ACP JSONL frames include complete
|
||||||
|
prompts, file content, and tool calls. Do not enable it on a shared or
|
||||||
|
network-exposed backend.
|
||||||
|
|
||||||
## Minimal examples
|
## Minimal examples
|
||||||
|
|
||||||
@@ -304,7 +312,7 @@ Copying this entire file is unnecessary and can freeze old defaults in a deploym
|
|||||||
| `features.auth` | `KANDEV_FEATURES_AUTH` | off | Authentication and per-user workspaces for the whole install. |
|
| `features.auth` | `KANDEV_FEATURES_AUTH` | off | Authentication and per-user workspaces for the whole install. |
|
||||||
| `features.claudeBackgroundPromptHandoff` | `KANDEV_FEATURES_CLAUDE_BACKGROUND_PROMPT_HANDOFF` | off | High-risk Claude Code experiment that exposes recognized background-only activity and admits a successor prompt. |
|
| `features.claudeBackgroundPromptHandoff` | `KANDEV_FEATURES_CLAUDE_BACKGROUND_PROMPT_HANDOFF` | off | High-risk Claude Code experiment that exposes recognized background-only activity and admits a successor prompt. |
|
||||||
| `features.claudeMidTurnSteering` | `KANDEV_FEATURES_CLAUDE_MID_TURN_STEERING` | off | High-risk Claude Code experiment that delivers a prompt into a still-generating turn (mid-turn steering) instead of queuing it, for agents advertising prompt queueing. |
|
| `features.claudeMidTurnSteering` | `KANDEV_FEATURES_CLAUDE_MID_TURN_STEERING` | off | High-risk Claude Code experiment that delivers a prompt into a still-generating turn (mid-turn steering) instead of queuing it, for agents advertising prompt queueing. |
|
||||||
| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` (also locked by explicit legacy/debug-message vars) | off | High-risk diagnostic endpoints and ACP frame logging. |
|
| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` | off | High-risk diagnostic endpoints and ACP frame logging. |
|
||||||
|
|
||||||
UI changes are persisted in the database and require a restart. An explicitly set environment value wins and locks the UI control. Otherwise a database override wins over the embedded profile/default. Resetting a toggle removes its database override.
|
UI changes are persisted in the database and require a restart. An explicitly set environment value wins and locks the UI control. Otherwise a database override wins over the embedded profile/default. Resetting a toggle removes its database override.
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ owner: Kandev
|
|||||||
|
|
||||||
# Environment-specific browser tab title prefixes
|
# Environment-specific browser tab title prefixes
|
||||||
|
|
||||||
|
The profile-selection boundary follows [ADR-2026-08-10-debug-launcher-profile-selection](../../decisions/2026-08-10-debug-launcher-profile-selection.md).
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
Developers often keep a local development instance and one or more pull-request
|
Developers often keep a local development instance and one or more pull-request
|
||||||
@@ -24,6 +26,9 @@ easy to use the wrong instance.
|
|||||||
contract.
|
contract.
|
||||||
- A normal production/start launch with no explicit prefix keeps the plain
|
- A normal production/start launch with no explicit prefix keeps the plain
|
||||||
`Kandev` title.
|
`Kandev` title.
|
||||||
|
- A debug start (`make start-debug`) can enable pprof and debug logging without
|
||||||
|
selecting the development profile. It uses the explicit prefix `Debug` by
|
||||||
|
default, so its title is `Debug Kandev`.
|
||||||
- The prefix is present both on the initial server-rendered page and after a
|
- 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
|
client boot from `/api/v1/app-state`, using the title behavior delivered by
|
||||||
PR #2459.
|
PR #2459.
|
||||||
@@ -37,6 +42,7 @@ The existing environment contract remains the public configuration surface:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Development profile (`make dev`) | `Dev` by default | `Dev Kandev` |
|
| Development profile (`make dev`) | `Dev` by default | `Dev Kandev` |
|
||||||
| PR preview launcher | `Preview` | `Preview Kandev` |
|
| PR preview launcher | `Preview` | `Preview Kandev` |
|
||||||
|
| Debug start (`make start-debug`) | `Debug` by default | `Debug Kandev` |
|
||||||
| Normal start with no override | unset | `Kandev` |
|
| Normal start with no override | unset | `Kandev` |
|
||||||
| Any launch with an explicit override | caller value | `<value> Kandev` |
|
| Any launch with an explicit override | caller value | `<value> Kandev` |
|
||||||
|
|
||||||
@@ -46,6 +52,9 @@ not changed from the Kandev UI.
|
|||||||
## Failure modes
|
## Failure modes
|
||||||
|
|
||||||
- If the prefix is unset or blank, the title remains `Kandev`.
|
- If the prefix is unset or blank, the title remains `Kandev`.
|
||||||
|
- `KANDEV_DEBUG_PPROF_ENABLED=true` enables legacy pprof behavior but does not
|
||||||
|
select the development profile or add the `Dev` prefix. The debug launcher
|
||||||
|
supplies the `Debug` prefix unless the caller supplies another prefix.
|
||||||
- If the preview launcher cannot set the environment variable, the preview
|
- If the preview launcher cannot set the environment variable, the preview
|
||||||
falls back to the existing plain-title behavior rather than failing startup.
|
falls back to the existing plain-title behavior rather than failing startup.
|
||||||
- If a supervised restart occurs, the launcher preserves the allowlisted prefix
|
- If a supervised restart occurs, the launcher preserves the allowlisted prefix
|
||||||
@@ -69,11 +78,14 @@ caller supplies the same environment or profile again.
|
|||||||
the backend starts, **THEN** the browser title is `Custom Kandev`.
|
the backend starts, **THEN** the browser title is `Custom Kandev`.
|
||||||
- **GIVEN** a normal start with no title-prefix environment variable, **WHEN**
|
- **GIVEN** a normal start with no title-prefix environment variable, **WHEN**
|
||||||
the user opens Kandev, **THEN** the browser title is `Kandev`.
|
the user opens Kandev, **THEN** the browser title is `Kandev`.
|
||||||
|
- **GIVEN** a debug start with pprof and debug logging enabled but without the
|
||||||
|
development-profile selector, **WHEN** the user opens Kandev, **THEN** the
|
||||||
|
browser title is `Debug Kandev`.
|
||||||
- **GIVEN** a preview or development backend with a configured prefix, **WHEN**
|
- **GIVEN** a preview or development backend with a configured prefix, **WHEN**
|
||||||
its supervisor restarts the backend, **THEN** the browser title retains the
|
its supervisor restarts the backend, **THEN** the browser title retains the
|
||||||
same prefix.
|
same prefix.
|
||||||
- **GIVEN** a phone-sized browser viewport, **WHEN** either environment is
|
- **GIVEN** a phone-sized browser viewport, **WHEN** either environment is
|
||||||
opened, **THEN** the same title is shown; no viewport-specific interaction is
|
opened, **THEN** the same title is shown. No viewport-specific interaction is
|
||||||
required.
|
required.
|
||||||
|
|
||||||
## Out of scope
|
## Out of scope
|
||||||
|
|||||||
+24
-11
@@ -29,7 +29,7 @@ status=0
|
|||||||
|
|
||||||
# expect <label> <OS> <MSYSTEM> <target> <expected recipe tail>
|
# expect <label> <OS> <MSYSTEM> <target> <expected recipe tail>
|
||||||
expect() {
|
expect() {
|
||||||
local label=$1 os=$2 msys=$3 target=$4 want=$5 marker=${6:-__backend} got
|
local label=$1 os=$2 msys=$3 target=$4 want=$5 marker=${6:-__backend} title_prefix=${7:-} got
|
||||||
# The OS=/MSYSTEM= overrides only steer which branch the Makefile picks;
|
# The OS=/MSYSTEM= overrides only steer which branch the Makefile picks;
|
||||||
# $(shell ...) still runs under this harness's POSIX shell, and it expands
|
# $(shell ...) still runs under this harness's POSIX shell, and it expands
|
||||||
# even under `make -n`. So neutralise the build-stamp probes rather than let
|
# even under `make -n`. So neutralise the build-stamp probes rather than let
|
||||||
@@ -38,10 +38,17 @@ expect() {
|
|||||||
# NULL_REDIR (rather than passing /dev/null) also dodges Git Bash rewriting
|
# NULL_REDIR (rather than passing /dev/null) also dodges Git Bash rewriting
|
||||||
# a /dev/null argument to `nul` on its way to native make. None of this
|
# a /dev/null argument to `nul` on its way to native make. None of this
|
||||||
# affects the run/dev/start-debug recipes under assertion.
|
# affects the run/dev/start-debug recipes under assertion.
|
||||||
got=$(make -C "$backend_dir" OS="$os" MSYSTEM="$msys" NULL_REDIR= BUILD_TIME=simulated -n "$target" 2>/dev/null \
|
if [ -n "$title_prefix" ]; then
|
||||||
| grep -F "$marker" \
|
got=$(KANDEV_WEB_TITLE_PREFIX="$title_prefix" make -C "$backend_dir" OS="$os" MSYSTEM="$msys" NULL_REDIR= BUILD_TIME=simulated -n "$target" 2>/dev/null \
|
||||||
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/[[:space:]]\{1,\}/ /g' \
|
| grep -F "$marker" \
|
||||||
|| true)
|
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/[[:space:]]\{1,\}/ /g' \
|
||||||
|
|| true)
|
||||||
|
else
|
||||||
|
got=$(env -u KANDEV_WEB_TITLE_PREFIX make -C "$backend_dir" OS="$os" MSYSTEM="$msys" NULL_REDIR= BUILD_TIME=simulated -n "$target" 2>/dev/null \
|
||||||
|
| grep -F "$marker" \
|
||||||
|
| sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/[[:space:]]\{1,\}/ /g' \
|
||||||
|
|| true)
|
||||||
|
fi
|
||||||
if [ "$got" = "$want" ]; then
|
if [ "$got" = "$want" ]; then
|
||||||
printf 'ok %-16s %-12s %s\n' "$label" "$target" "$got"
|
printf 'ok %-16s %-12s %s\n' "$label" "$target" "$got"
|
||||||
else
|
else
|
||||||
@@ -53,18 +60,24 @@ expect() {
|
|||||||
|
|
||||||
# Unix — no .exe, POSIX env-prefix + exec.
|
# Unix — no .exe, POSIX env-prefix + exec.
|
||||||
expect unix '' '' run './bin/kandev __backend'
|
expect unix '' '' run './bin/kandev __backend'
|
||||||
expect unix '' '' dev 'KANDEV_DEBUG_PPROF_ENABLED=true exec ./bin/kandev __backend'
|
expect unix '' '' dev 'KANDEV_DEBUG_DEV_MODE=true exec ./bin/kandev __backend'
|
||||||
expect unix '' '' start-debug 'KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug exec ./bin/kandev __backend'
|
expect unix '' '' start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='Debug' exec ./bin/kandev __backend"
|
||||||
|
expect unix '' '' start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='Preview' exec ./bin/kandev __backend" __backend Preview
|
||||||
|
expect unix '' '' start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='QA Debug' exec ./bin/kandev __backend" __backend "QA Debug"
|
||||||
|
|
||||||
# Native Windows (cmd/PowerShell) — .exe, `set VAR=VAL&`, backslash path, no exec.
|
# Native Windows (cmd/PowerShell) — .exe, `set VAR=VAL&`, backslash path, no exec.
|
||||||
expect windows-native Windows_NT '' run '"bin\kandev.exe" __backend'
|
expect windows-native Windows_NT '' run '"bin\kandev.exe" __backend'
|
||||||
expect windows-native Windows_NT '' dev 'set KANDEV_DEBUG_PPROF_ENABLED=true& "bin\kandev.exe" __backend'
|
expect windows-native Windows_NT '' dev 'set KANDEV_DEBUG_DEV_MODE=true& "bin\kandev.exe" __backend'
|
||||||
expect windows-native Windows_NT '' start-debug 'set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug& "bin\kandev.exe" __backend'
|
expect windows-native Windows_NT '' start-debug 'set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug& set "KANDEV_WEB_TITLE_PREFIX=Debug"& "bin\kandev.exe" __backend'
|
||||||
|
expect windows-native Windows_NT '' start-debug 'set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug& set "KANDEV_WEB_TITLE_PREFIX=Preview"& "bin\kandev.exe" __backend' __backend Preview
|
||||||
|
expect windows-native Windows_NT '' start-debug 'set KANDEV_DEBUG_PPROF_ENABLED=true& set KANDEV_LOG_LEVEL=debug& set "KANDEV_WEB_TITLE_PREFIX=QA Debug"& "bin\kandev.exe" __backend' __backend "QA Debug"
|
||||||
|
|
||||||
# Git Bash/MSYS — .exe suffix but POSIX syntax (recipes run through sh).
|
# Git Bash/MSYS — .exe suffix but POSIX syntax (recipes run through sh).
|
||||||
expect git-bash Windows_NT MINGW64 run './bin/kandev.exe __backend'
|
expect git-bash Windows_NT MINGW64 run './bin/kandev.exe __backend'
|
||||||
expect git-bash Windows_NT MINGW64 dev 'KANDEV_DEBUG_PPROF_ENABLED=true exec ./bin/kandev.exe __backend'
|
expect git-bash Windows_NT MINGW64 dev 'KANDEV_DEBUG_DEV_MODE=true exec ./bin/kandev.exe __backend'
|
||||||
expect git-bash Windows_NT MINGW64 start-debug 'KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug exec ./bin/kandev.exe __backend'
|
expect git-bash Windows_NT MINGW64 start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='Debug' exec ./bin/kandev.exe __backend"
|
||||||
|
expect git-bash Windows_NT MINGW64 start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='Preview' exec ./bin/kandev.exe __backend" __backend Preview
|
||||||
|
expect git-bash Windows_NT MINGW64 start-debug "KANDEV_DEBUG_PPROF_ENABLED=true KANDEV_LOG_LEVEL=debug KANDEV_WEB_TITLE_PREFIX='QA Debug' exec ./bin/kandev.exe __backend" __backend "QA Debug"
|
||||||
|
|
||||||
# deadcode — the analyzer is invoked through the same shell-specific env-prefix
|
# deadcode — the analyzer is invoked through the same shell-specific env-prefix
|
||||||
# mechanism, but its command has no __backend marker, so match its module path.
|
# mechanism, but its command has no __backend marker, so match its module path.
|
||||||
|
|||||||
Reference in New Issue
Block a user