From 3499230fdc1535631a3dc8e2179fc93269da7429 Mon Sep 17 00:00:00 2001 From: Dean Sharon Date: Mon, 14 Sep 2026 11:31:13 +0300 Subject: [PATCH 1/2] fix(flags): default pin-sonnet-4-6 and disable-bundled-skills to off User feedback: pinning Sonnet to 4.6 and removing Claude Code's bundled skills should be opt-in, not the recommended default. Both flags move to the optional section with defaultValue false. Existing installs keep their manifest value (ADR-014); toggle with `devflow flags`. --- CHANGELOG.md | 2 ++ CLAUDE.md | 2 +- docs/cli-reference.md | 4 ++-- src/core/flags.ts | 8 ++++---- tests/flags.test.ts | 4 ++-- tests/init-seed.test.ts | 4 ++-- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30de9bbd..aceee766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **`tests/integration/subagent-skill-preload.test.ts` is excluded from `npm run test:integration`** — before: `vitest.integration.config.ts` declared only an `include` glob, so the file was collected by every integration run, including CI, and no-op'd only where the `claude` binary was absent, through its own `describe.skipIf(!isClaudeAvailable())` guard; on a machine with `claude` installed it spawned live sessions. After: the config carries a real `exclude` entry. The test drives live `claude` sessions against the developer's own `~/.claude` with `--dangerously-skip-permissions` and has previously committed to this repo mid-run, so it is opt-in: set `DEVFLOW_INTEGRATION_ALL=1` to include it. A command-line path alone cannot re-add it — `exclude` is applied at glob time. +- **`pin-sonnet-4-6` and `disable-bundled-skills` now default OFF** — before: both flags were in the recommended set with `defaultValue: true`, so a fresh `devflow init` pinned `ANTHROPIC_DEFAULT_SONNET_MODEL` to `claude-sonnet-4-6` and wrote `disableBundledSkills: true` to settings.json, removing Claude Code's built-in skills and commands. After: both are optional flags defaulting to `false`; a fresh install leaves the Sonnet alias and Claude Code's bundled skills untouched. Existing installs keep whatever value their manifest already records (ADR-014 — re-init preserves existing flag values); opt in or out with `devflow flags --enable/--disable pin-sonnet-4-6` and `devflow flags --enable/--disable disable-bundled-skills`. + ### Fixed - **`/debug #42` wrong Git-op spawn key** — before: `debug.mds` passed `ISSUE: {issue number}` to the `fetch-issue` Git operation, which declares `ISSUE_INPUT:`; the key mismatch meant no issue was ever fetched. After: `debug.mds` passes `ISSUE_INPUT: {issue reference}` — the key the op declares. (AC-0.1) diff --git a/CLAUDE.md b/CLAUDE.md index 5c503b6d..2e1c9b5d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,7 +50,7 @@ Debug logs stored at `~/.devflow/logs/{project-slug}/`. **Debug Tracing**: Single global toggle covering all hooks. Enabled via `devflow debug --enable/--disable/--status` CLI or by setting `DEVFLOW_HOOK_DEBUG=1` in `~/.claude/settings.json` env block (survives reinstalls). All hooks share the `src/assets/scripts/hooks/debug-trace` helper script (sourced via `hook-bootstrap`) so tracing behavior is consistent and updated in one place. Two-phase logging: pre-CWD traces go to global `~/.devflow/logs/.hook-debug.log`; post-CWD traces go to per-project `~/.devflow/logs/{project-slug}/.hook-debug.log`. A 5MB size guard prevents unbounded growth. applies ADR-007 -**Claude Code Flags**: Typed registry (`src/core/flags.ts`) for managing Claude Code feature flags (env vars and top-level settings). Four kinds: `boolean` (on/off — declared as `EnvBooleanFlagDef | SettingBooleanFlagDef`: env targets constrain `onPayload` to `string` and type `settingDeleteGuard` as `never`, setting targets allow object payloads and an optional `settingDeleteGuard`; enforced at registry declaration sites by object-literal assignability — a `target.type` check does not narrow `onPayload` for consumers, use `isEnvBooleanFlag`), `enum` (validated domain), `number` (bounded integer), `string` (validated with maxLength). 29 flags total: recommended (default ON) — `tui`, `tool-search`, `lsp`, `prompt-caching-1h`, `show-turn-duration`, `clear-context-on-plan`, `disable-bundled-skills`, `pin-sonnet-4-6`, `max-concurrent-subagents` (number, devflow default 40, upstream default 20); optional boolean (default OFF) — `brief`, `thinking-summaries`, `subprocess-env-scrub`, `disable-nonessential-traffic`, `forked-subagents`, `disable-adaptive-thinking`, `always-thinking`, `disable-git-instructions`, `disable-compact`, `disable-1m-context`, `disable-autoupdater`, `agent-teams`, `enable-todo-tools`, `suppress-attribution` (writes `{"commit":"","pr":""}` to settings.json — suppresses Claude attribution in git commits and PRs; shape-guarded deletion: only removed on disable when the value exactly matches the devflow-managed shape, never when a user has a custom attribution — the guard covers deletion only; enabling overwrites any existing `attribution` value (ADR-024 corollary b); D27/D-ATTR-GUARD); valued (default unset) — `subagent-spawn-depth` (number, upstream default 3), `workflow-size-guideline` (enum: `small|medium|large|unrestricted`), `default-model` (string), `goal-checkin-minutes` (number, upstream default 30 min), `spellcheck` (string), `view-mode` (enum: `default|verbose|focus`, devflow default `default`). Stored in manifest `features.flags: Record` — entry-presence = known, `null` = deliberately unset (neutral, deletes the target key), absent = adopt-on-next-init. Pipeline: `applyFlags(settingsJson, FlagsRecord)` / `stripFlags(settingsJson)` — `applyViewMode`/`stripViewMode` retired; view-mode is an enum flag with `neutralValue: 'default'` (the `viewMode` settings.json key is written only when non-default); `resolveExistingViewMode`/`resolveFinalViewMode` remain exported for init.ts external-mode preservation. `devflow flags` bare on TTY launches the interactive flags editor TUI; bare on non-TTY prints a status table to stdout and exits 1. Registry entries carry a `blurb` field (≤30-char per-flag short hint) shown as a dim HINT column in the TUI and in `--status` rows. Display vocabulary via `effectiveDisplay`: booleans render 'on'/'off' (off is dim); neutral/unset enum shows `neutralValue` dim; unset number shows its applicable default dim with ' (default)' suffix; unset string shows '—' dim; an actively set non-boolean renders plain (at devflow default) or bold (deviating); the literal 'unset' is never a displayed value. TUI rendering: `RunTuiSpec.screen?: 'alt' | 'inline'`; flags editor runs inline (renders in-place in the normal scroll buffer, no alt-screen); agents-view defaults to alt. Manageable via `devflow flags --enable/--disable/--set /--unset /--status/--list`; `--enable`/`--disable` are boolean-only — non-boolean flags are redirected to `--set`. +**Claude Code Flags**: Typed registry (`src/core/flags.ts`) for managing Claude Code feature flags (env vars and top-level settings). Four kinds: `boolean` (on/off — declared as `EnvBooleanFlagDef | SettingBooleanFlagDef`: env targets constrain `onPayload` to `string` and type `settingDeleteGuard` as `never`, setting targets allow object payloads and an optional `settingDeleteGuard`; enforced at registry declaration sites by object-literal assignability — a `target.type` check does not narrow `onPayload` for consumers, use `isEnvBooleanFlag`), `enum` (validated domain), `number` (bounded integer), `string` (validated with maxLength). 29 flags total: recommended (default ON) — `tui`, `tool-search`, `lsp`, `prompt-caching-1h`, `show-turn-duration`, `clear-context-on-plan`, `max-concurrent-subagents` (number, devflow default 40, upstream default 20); optional boolean (default OFF) — `disable-bundled-skills`, `pin-sonnet-4-6`, `brief`, `thinking-summaries`, `subprocess-env-scrub`, `disable-nonessential-traffic`, `forked-subagents`, `disable-adaptive-thinking`, `always-thinking`, `disable-git-instructions`, `disable-compact`, `disable-1m-context`, `disable-autoupdater`, `agent-teams`, `enable-todo-tools`, `suppress-attribution` (writes `{"commit":"","pr":""}` to settings.json — suppresses Claude attribution in git commits and PRs; shape-guarded deletion: only removed on disable when the value exactly matches the devflow-managed shape, never when a user has a custom attribution — the guard covers deletion only; enabling overwrites any existing `attribution` value (ADR-024 corollary b); D27/D-ATTR-GUARD); valued (default unset) — `subagent-spawn-depth` (number, upstream default 3), `workflow-size-guideline` (enum: `small|medium|large|unrestricted`), `default-model` (string), `goal-checkin-minutes` (number, upstream default 30 min), `spellcheck` (string), `view-mode` (enum: `default|verbose|focus`, devflow default `default`). Stored in manifest `features.flags: Record` — entry-presence = known, `null` = deliberately unset (neutral, deletes the target key), absent = adopt-on-next-init. Pipeline: `applyFlags(settingsJson, FlagsRecord)` / `stripFlags(settingsJson)` — `applyViewMode`/`stripViewMode` retired; view-mode is an enum flag with `neutralValue: 'default'` (the `viewMode` settings.json key is written only when non-default); `resolveExistingViewMode`/`resolveFinalViewMode` remain exported for init.ts external-mode preservation. `devflow flags` bare on TTY launches the interactive flags editor TUI; bare on non-TTY prints a status table to stdout and exits 1. Registry entries carry a `blurb` field (≤30-char per-flag short hint) shown as a dim HINT column in the TUI and in `--status` rows. Display vocabulary via `effectiveDisplay`: booleans render 'on'/'off' (off is dim); neutral/unset enum shows `neutralValue` dim; unset number shows its applicable default dim with ' (default)' suffix; unset string shows '—' dim; an actively set non-boolean renders plain (at devflow default) or bold (deviating); the literal 'unset' is never a displayed value. TUI rendering: `RunTuiSpec.screen?: 'alt' | 'inline'`; flags editor runs inline (renders in-place in the normal scroll buffer, no alt-screen); agents-view defaults to alt. Manageable via `devflow flags --enable/--disable/--set /--unset /--status/--list`; `--enable`/`--disable` are boolean-only — non-boolean flags are redirected to `--set`. **Feature Knowledge Bases**: Per-feature `.devflow/features/` directory containing KNOWLEDGE.md files that capture area-specific patterns, conventions, architecture, and gotchas. Uses a **write-through** model: load = direct file-I/O reading `.devflow/features/index.md` (regenerable cache) with frontmatter-glob fallback over `features/*/KNOWLEDGE.md` (source of truth) + verify-against-code on read; save = in-command write-through via a simplified Knowledge agent that writes `KNOWLEDGE.md` + the `index.md` line directly (no `.create-result.json`, no external scripts, no lock). **Git-tracked & shared (amends ADR-021 for `features/`)**: the root `.gitignore` carve-out (`.devflow/*` + level-by-level `!` re-includes, written byte-identically by `ensure-root-gitignore` / `ensureDevflowGitignore`) un-ignores `.devflow/features/index.md` + every `{slug}/KNOWLEDGE.md` while the rest of `.devflow/` stays local; after writing, the **Knowledge agent commits those two paths to the current worktree branch itself** by running git via its Bash tool (scoped `commit --only` pathspec, never `git add -A`, **never push, never force**, no commit script — per the LLM-vs-plumbing principle the commit is the agent's, not a deterministic helper). A user opts back out by re-adding `.devflow/features/` to their own `.gitignore`. Existing installs upgrade once via the versioned `.root-gitignore-configured-v3` marker (v2→v3 adds the `!.devflow/conventions.md` re-include). Freshness = write-through + verify-on-read (NO git-staleness, NO SessionEnd eval, NO Learning task). `index.md` line format: `- **{slug}** — {areas} — {Use-when description}`; frontmatter is authoritative if the line is lost. MDS module: `src/assets/commands/_partials/_knowledge.mds` (defines/exports `knowledge_load` and `knowledge_writeback` partials) + 9 host `.mds` sources in `src/assets/commands/` compiled to `dist/commands/` by `scripts/build-mds.ts` (`npm run build:mds`). `knowledge_load` is used up-front by: implement, plan, resolve, code-review, self-review, research, bug-analysis. `knowledge_writeback` is used at workflow end by: implement, resolve, self-review, explore, debug. explore/debug do NOT load up-front (intentional asymmetry). Config gate: single `knowledge: true|false` in feature config (default true) — gates write-back only; load is ungated. CLI: `devflow knowledge list` (read index.md / frontmatter glob), `devflow knowledge --enable/--disable/--status` (flip config). Note: `/debug` keeps FEATURE_KNOWLEDGE orchestrator-local (investigation workers examine code without pre-loaded context). Toggleable via `devflow knowledge --enable/--disable/--status` or `devflow init --knowledge/--no-knowledge`. diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 0a9e4d11..069ba3ad 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -215,8 +215,8 @@ All 29 flags by kind and devflow default: | `prompt-caching-1h` | boolean | env `ENABLE_PROMPT_CACHING_1H` | `true` | | `show-turn-duration` | boolean | setting `showTurnDuration` | `true` | | `clear-context-on-plan` | boolean | setting `showClearContextOnPlanAccept` | `true` | -| `disable-bundled-skills` | boolean | setting `disableBundledSkills` | `true` | -| `pin-sonnet-4-6` | boolean | env `ANTHROPIC_DEFAULT_SONNET_MODEL` | `true`¹ | +| `disable-bundled-skills` | boolean | setting `disableBundledSkills` | `false` | +| `pin-sonnet-4-6` | boolean | env `ANTHROPIC_DEFAULT_SONNET_MODEL` | `false`¹ | | `max-concurrent-subagents` | number | env `CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS` | `40` (upstream: 20) | | `brief` | boolean | env `CLAUDE_CODE_BRIEF` | `false` | | `thinking-summaries` | boolean | setting `showThinkingSummaries` | `false` | diff --git a/src/core/flags.ts b/src/core/flags.ts index 2117c20b..975e0937 100644 --- a/src/core/flags.ts +++ b/src/core/flags.ts @@ -268,8 +268,8 @@ export const FLAG_REGISTRY: readonly ClaudeCodeFlag[] = [ kind: 'boolean', target: { type: 'setting', key: 'disableBundledSkills' }, onPayload: true, - recommended: true, - defaultValue: true, + recommended: false, + defaultValue: false, }, { id: 'pin-sonnet-4-6', @@ -280,8 +280,8 @@ export const FLAG_REGISTRY: readonly ClaudeCodeFlag[] = [ kind: 'boolean', target: { type: 'env', key: 'ANTHROPIC_DEFAULT_SONNET_MODEL' }, onPayload: 'claude-sonnet-4-6', - recommended: true, - defaultValue: true, + recommended: false, + defaultValue: false, }, { // Devflow fan-outs routinely exceed the upstream default of 20. diff --git a/tests/flags.test.ts b/tests/flags.test.ts index eaeb68e2..b12b4be1 100644 --- a/tests/flags.test.ts +++ b/tests/flags.test.ts @@ -210,14 +210,14 @@ describe('getDefaultFlagsRecord', () => { expect(record['prompt-caching-1h']).toBe(true); expect(record['show-turn-duration']).toBe(true); expect(record['clear-context-on-plan']).toBe(true); - expect(record['disable-bundled-skills']).toBe(true); - expect(record['pin-sonnet-4-6']).toBe(true); // New recommended number flag expect(record['max-concurrent-subagents']).toBe(40); // Optional boolean flags (default OFF = false = neutral) expect(record['brief']).toBe(false); + expect(record['disable-bundled-skills']).toBe(false); + expect(record['pin-sonnet-4-6']).toBe(false); expect(record['thinking-summaries']).toBe(false); expect(record['subprocess-env-scrub']).toBe(false); expect(record['disable-nonessential-traffic']).toBe(false); diff --git a/tests/init-seed.test.ts b/tests/init-seed.test.ts index 3869b185..3d874694 100644 --- a/tests/init-seed.test.ts +++ b/tests/init-seed.test.ts @@ -156,10 +156,10 @@ describe('resolveSeedFlags', () => { expect(result['prompt-caching-1h']).toBe(true); expect(result['show-turn-duration']).toBe(true); expect(result['clear-context-on-plan']).toBe(true); - expect(result['disable-bundled-skills']).toBe(true); - expect(result['pin-sonnet-4-6']).toBe(true); // Default-OFF boolean flags are false expect(result['brief']).toBe(false); + expect(result['disable-bundled-skills']).toBe(false); + expect(result['pin-sonnet-4-6']).toBe(false); // Number flag with non-neutral default is present expect(result['max-concurrent-subagents']).toBe(40); // view-mode default is 'default' (neutralValue for the enum) From e459961e10043b09a47e83942129305db44d989b Mon Sep 17 00:00:00 2001 From: Dean Sharon Date: Mon, 14 Sep 2026 21:49:00 +0300 Subject: [PATCH 2/2] test(flags): assert pin-sonnet-4-6 and disable-bundled-skills adopt OFF in init e2e The old-format-manifest adoption test listed the Sonnet pin among the default-ON flags that must materialise in settings.json. Both flags are now optional/default-OFF, so the test proves the opposite: adoption records false and writes neither the env var nor the setting. --- tests/init-e2e-flags.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/init-e2e-flags.test.ts b/tests/init-e2e-flags.test.ts index 238f201a..1f3e4211 100644 --- a/tests/init-e2e-flags.test.ts +++ b/tests/init-e2e-flags.test.ts @@ -203,7 +203,15 @@ describe('init e2e — flags Phase 6 integration', () => { // Other adopted default-ON flags materialise too (proves applyFlags ran over the // whole adopted record, not just the one flag asserted above). expect(env.ENABLE_TOOL_SEARCH).toBe('true'); - expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('claude-sonnet-4-6'); + expect(env.ENABLE_PROMPT_CACHING_1H).toBe('true'); + + // Adopted default-OFF flags write nothing: pin-sonnet-4-6 and disable-bundled-skills + // are optional (opt-in via `devflow flags --enable`), so a fresh adoption leaves + // the Sonnet alias and Claude Code's bundled skills untouched. + expect(flagsRecord['pin-sonnet-4-6']).toBe(false); + expect(flagsRecord['disable-bundled-skills']).toBe(false); + expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBeUndefined(); + expect(settings).not.toHaveProperty('disableBundledSkills'); // Deliberate prior disables are PRESERVED, not re-adopted (ADR-014): the old manifest // recorded knownFlags ['tui','lsp'] with an empty enabled list, so both stay off and