feat(cli): upgrade gate + lifecycle subdirs in run (#404, #410)#414
feat(cli): upgrade gate + lifecycle subdirs in run (#404, #410)#414MScottAdams merged 1 commit intophase2/vbrief-cutoverfrom
Conversation
|
| Filename | Overview |
|---|---|
| run | Adds upgrade gate helpers, cmd_upgrade, lifecycle subdir scaffolding in cmd_project, and gate wiring in main(); one dead variable left over in _check_upgrade_gate |
| tests/cli/test_upgrade_gate.py | 23 well-structured tests covering all new code paths; one dead inner function _isatty_patch in the _stdin_tty helper |
| CHANGELOG.md | Adds entries for #404 and #410 under [Unreleased]; no issues |
| vbrief/active/2026-04-16-404-run-project-lifecycle-subdirs.vbrief.json | Scope vBRIEF for #404; no code issues |
| vbrief/active/2026-04-16-410-run-upgrade-gate.vbrief.json | Scope vBRIEF for #410; no code issues |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["deft/run <command>"] --> B{TUI / no-args?}
B -- yes --> C["Launch TUI / show usage"]
B -- no --> D{help / -h / --help?}
D -- yes --> E["usage() → exit 0"]
D -- no --> F["_check_upgrade_gate(command)"]
F --> G{command in SKIP_COMMANDS?}
G -- yes --> H["return True (skip)"]
G -- no --> I{_running_inside_deft_repo?}
I -- yes --> H
I -- no --> J["_read_version_marker()"]
J --> K{marker found?}
K -- "yes, matches VERSION" --> H
K -- "yes, drift" --> L["warn: version drift"]
K -- no --> M["_detect_pre_cutover_legacy()"]
M --> N{legacy artifacts?}
N -- none --> H
N -- found --> O["warn: Pre-v0.20"]
L --> P{sys.stdin.isatty?}
O --> P
P -- no --> H
P -- yes --> Q["read_yn: Continue anyway? y/N"]
Q -- yes --> H
Q -- no --> R["warn + return False → exit 1"]
H --> S["dispatch command"]
S --> T{cmd_project}
S --> U{cmd_upgrade}
T --> V["_atomic_write PROJECT-DEFINITION.vbrief.json"]
V --> W["mkdir lifecycle folders"]
W --> X["_write_version_marker vbrief/.deft-version"]
U --> Y{vbrief/ exists?}
Y -- yes --> Z["_write_version_marker vbrief/.deft-version"]
Y -- no --> AA["_write_version_marker project_root/.deft-version"]
Prompt To Fix All With AI
This is a comment left during a code review.
Path: run
Line: 592
Comment:
**Dead variable `vbrief_dir`**
`vbrief_dir` is assigned but never referenced again inside `_check_upgrade_gate`. It appears to be a leftover from an earlier revision where the function had an early return guarded on `vbrief_dir.is_dir()`. Safe to remove.
```suggestion
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: tests/cli/test_upgrade_gate.py
Line: 222-223
Comment:
**Dead inner function `_isatty_patch`**
`_isatty_patch` is defined but never called; the `lambda: value` on line 227 does all the actual patching. The inner function can be removed without changing any test behavior.
```suggestion
# Patch isatty on both the attribute and the function
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (4): Last reviewed commit: "feat(cli): upgrade gate + lifecycle subd..." | Re-trigger Greptile
b243412 to
fd5054d
Compare
- #404: `run project` now scaffolds the 5 vBRIEF lifecycle subdirectories (proposed/, pending/, active/, completed/, cancelled/) immediately after writing PROJECT-DEFINITION.vbrief.json, matching the skill-driven greenfield path so `task vbrief:validate` and `task scope:*` work out-of-the-box. A module-level LIFECYCLE_FOLDERS constant is shared between cmd_project and future callers. - #410: add a non-fatal version/upgrade gate at `deft/run` entry. On every invocation, compare the recorded framework version in vbrief/.deft-version (fallback: ./.deft-version) against VERSION and warn on drift, directing users to `deft/run upgrade` or `task migrate:vbrief`. Detect pre-v0.20 legacy artifacts (SPECIFICATION.md or PROJECT.md without the <!-- deft:deprecated-redirect --> sentinel) and warn even if no marker exists. Interactive sessions get a "Continue anyway? [y/N]" prompt; non-interactive sessions warn and continue so CI never breaks. Skip entirely when running inside the deft framework repo itself (detected by root main.md + missing ./deft/). - Add `run upgrade` command: records the current VERSION in vbrief/.deft-version, surfaces legacy-artifact guidance, and is idempotent when the marker already matches. - `run project` writes the marker on successful first-setup so the gate stays silent on subsequent invocations of a freshly-configured project. - Register `upgrade` in the command dispatcher and usage() listings. - Add scope vBRIEFs 2026-04-16-404-run-project-lifecycle-subdirs.vbrief.json and 2026-04-16-410-run-upgrade-gate.vbrief.json with origin provenance. - CHANGELOG entries under [Unreleased]. - 23 new tests in tests/cli/test_upgrade_gate.py covering marker round-trips, legacy detection, deft-repo heuristic, gate behavior (silent match, warn on drift, non-interactive continues, skip commands), cmd_upgrade flows, and the #404 lifecycle subdir scaffolding. Closes #404 Closes #410 Part of #402
fd5054d to
9848b03
Compare
|
Acknowledging the two remaining P2 findings (confidence now 5/5):
Not fixing in this PR per the swarm-402 charter severity ladder — P2 style findings are non-blocking and a force-push to clean dead lines would cost another ~5-7 min Greptile re-review on an already-5/5 batch. Both will be swept up in a follow-up All required checks green (Python CI, Go CI, Greptile Review). All PO Pre-Approval conditions on #402 satisfied (confidence > 3, no P0/P1, CI green, all Greptile comments addressed or acknowledged, no other reviewers with unresolved comments). Proceeding to self-merge. |
Summary
Swarm-402 PR 2/4. Combines #404 and #410 because both touch the single
runfile; the charter explicitly serializes them into one PR.#404 —
run projectscaffolds vBRIEF lifecycle subdirectoriescmd_project()previously wrotevbrief/PROJECT-DEFINITION.vbrief.jsonbut did not create the 5 lifecycle subdirectories (proposed/,pending/,active/,completed/,cancelled/). The skill-driven greenfield path already creates them (skills/deft-directive-setup/SKILL.md), so the CLI path now matches.After
run project,task vbrief:validateandtask scope:*work out-of-the-box without manual folder creation.A new module-level
LIFECYCLE_FOLDERSconstant inrunmirrors the same tuple inscripts/vbrief_validate.pyandscripts/project_render.py; a test asserts they stay in sync.#410 — version/upgrade gate on
deft/runentryConsumer projects updating their
deft/submodule have no deterministic gate that reminds them to run migration. The sync skill only fires on explicit agent triggers;task checkis the consumer's own.deft/runis the one entry point directive controls that runs in consumer project context.This PR adds a non-fatal version gate before any subcommand dispatch:
run projectwrites avbrief/.deft-versionmarker containing the frameworkVERSION.VERSIONand warns on drift: "Deft has been updated from X to Y. Rundeft/run upgradeortask migrate:vbriefto update your project files."SPECIFICATION.md/PROJECT.mdwithout the<!-- deft:deprecated-redirect -->sentinel are found, the gate warns about pre-v0.20 state.Continue anyway? [y/N]prompt; non-interactive sessions (CI, cloud agents) warn once and continue per the swarm-402 charter ("never fatal").main.mdpresent, no./deft/subdir), so maintainers don't get nagged every invocation.help,--help,-h,version,--version,-v,upgrade) are skipped.A new
run upgradecommand writes the marker and surfaces legacy-artifact guidance;task migrate:vbriefremains authoritative for the heavy lifting (SPECIFICATION.md / PROJECT.md migration, lifecycle folder creation for existing projects).run upgradeis registered in the dispatcher and listed inusage().Changes
run— addLIFECYCLE_FOLDERS+DEPRECATED_REDIRECT_SENTINELconstants nearVERSION; add_version_marker_paths,_read_version_marker,_write_version_marker,_detect_pre_cutover_legacy,_running_inside_deft_repo,_UPGRADE_GATE_SKIP_COMMANDS,_check_upgrade_gate,cmd_upgrade; extendcmd_projectto mkdir the 5 lifecycle folders and write the marker after_atomic_write; wire_check_upgrade_gateintomain()before command dispatch; registerupgrade: cmd_upgradein the commands dict; listupgradein the rich/plainusage()output.tests/cli/test_upgrade_gate.py— new 23-test module (TestProjectLifecycleSubdirs,TestVersionMarkerHelpers,TestLegacyDetection,TestRunningInsideDeftRepo,TestUpgradeGate,TestCmdUpgrade).vbrief/active/2026-04-16-404-run-project-lifecycle-subdirs.vbrief.json— fix(cli): run project should create vbrief lifecycle subdirectories #404 scope vBRIEF.vbrief/active/2026-04-16-410-run-upgrade-gate.vbrief.json— feat(cli): add version/upgrade gate to deft/run entry point #410 scope vBRIEF.CHANGELOG.md— Added entry for feat(cli): add version/upgrade gate to deft/run entry point #410, Fixed entry for fix(cli): run project should create vbrief lifecycle subdirectories #404 under[Unreleased].Validation
task check— passed locally (1562 passed, 1 xfailed; +23 tests vs previous baseline).uv run ruff check tests/cli/test_upgrade_gate.py— clean.runis excluded from coverage measurement per pyproject.toml (Bring run CLI into test coverage measurement #228), but the forward coverage rule is satisfied bytests/cli/test_upgrade_gate.pyfor every new code path (helpers, gate, cmd_upgrade, fix(cli): run project should create vbrief lifecycle subdirectories #404 subdir scaffolding).Scope & constraints
run, the new test module, the two scope vBRIEFs, and CHANGELOG entries are touched.Part of #402 pre-merge must-fix batch
PR 2 of 4 in the swarm-402 cascade on
phase2/vbrief-cutover. Previous: #413 (#405). Next will target #407 + #408 (README + BROWNFIELD.md).Closes #404
Closes #410
Refs #402