feat(cli): update cmd_spec and cmd_project for vBRIEF-centric model#355
Conversation
…320) - Rewrite cmd_project to output vbrief/PROJECT-DEFINITION.vbrief.json with vBRIEF v0.5 schema (narratives for project identity, empty items registry) - Rewrite cmd_spec to output scope vBRIEFs to vbrief/proposed/YYYY-MM-DD-slug.vbrief.json instead of INTERVIEW.md/PRD.md - Add _slugify() helper for D7 filename convention - Update get_default_paths() with vBRIEF paths and vbrief_proposed default - Update _read_project_strategy() and _read_project_process() for vBRIEF JSON with legacy PROJECT.md fallback - Update all CLI help text (usage, TUI, command descriptions) - Rewrite tests/cli/test_project.py (12 tests for vBRIEF output) - Create tests/cli/test_cmd_spec.py (11 tests for scope vBRIEF output) - Rewrite tests/cli/test_spec_sizing.py for vBRIEF model - Update tests/cli/test_project_user_defaults.py and tests/conftest.py for new paths
Greptile SummaryThis PR updates Confidence Score: 5/5Safe to merge — all findings are P2 style/UX suggestions with no correctness or data-integrity impact. The vBRIEF output logic, JSON schema construction, legacy fallback parsing, and overwrite guard are all functionally correct. The two notable findings (late overwrite check, empty-slug filename) are non-blocking edge cases. 51 tests cover Light/Full paths, schema shape, strategy/process overrides, filename convention, force-overwrite, and USER.md defaults propagation. run (lines 1355–1364 _slugify, lines 1484–1494 overwrite guard placement) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([run spec]) --> B[_read_project_strategy from PROJECT-DEFINITION.vbrief.json]
B -->|found| C[strategy = stem]
B -->|not found| D[strategy = interview]
C --> E[Ask: project name spec_name]
D --> E
E --> F[Ask: brief description spec_desc]
F --> G[Feature collection loop until empty]
G --> H{_read_project_process override?}
H -->|Light or Full| I[sizing = override skip prompt]
H -->|None| J[Sizing gate prompt 1=Light 2=Full]
J --> K[sizing = Light or Full]
I --> L[Compute output filename YYYY-MM-DD-slug.vbrief.json]
K --> L
L --> M{output_file.exists? and not force?}
M -->|yes| N([return 1 warn: use --force])
M -->|no| O[Build scope vBRIEF v0.5]
O --> P{sizing == Full?}
P -->|yes| Q[Add rich narrative placeholders]
P -->|no| R[Minimal narratives Overview + Strategy]
Q --> S[_atomic_write to vbrief/proposed/]
R --> S
S --> T([return 0])
Prompt To Fix All With AIThis is a comment left during a code review.
Path: run
Line: 1491-1494
Comment:
**Overwrite guard fires after the full interview**
The output filename (`{today}-{slug}.vbrief.json`) is fully determinable right after `spec_name` is collected on line 1438, but the existence check isn't reached until after features and sizing are also collected. On a same-day re-run without `--force`, the user completes the entire interview only to be told the file already exists. Moving the check to immediately after `spec_name` is saved in progress, before the feature-collection loop, would surface the conflict earlier.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: run
Line: 1355-1364
Comment:
**Empty slug produces a malformed filename**
If `spec_name` consists entirely of non-alphanumeric characters (e.g. `"---"` or `"!!!"`), `_slugify` returns `""` and the output filename becomes `"YYYY-MM-DD-.vbrief.json"` — which also fails the `YYYY-MM-DD-[a-z0-9-]+\.vbrief\.json` pattern asserted in `test_spec_filename_convention`. A minimal guard:
```suggestion
def _slugify(text: str) -> str:
"""Convert text to a URL-friendly slug for vBRIEF filenames.
Strips non-alphanumeric characters, lowercases, and joins with hyphens.
Returns 'scope' as a fallback when the result would be empty.
"""
text = text.lower().strip()
text = re.sub(r'[^a-z0-9\s_-]', '', text)
text = re.sub(r'[\s_]+', '-', text)
text = re.sub(r'-+', '-', text)
slug = text[:60].strip('-')
return slug or 'scope' # Fallback to avoid YYYY-MM-DD-.vbrief.json
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: run.py
Line: 62-63
Comment:
**Private helper exported via `__all__`**
`_slugify` is underscore-prefixed (private by convention) but listed in both the explicit import block and `__all__`. Tests access it as `deft_run_module._slugify`, which works without the re-export. Removing it from `__all__` keeps the public surface clean without breaking any tests.
How can I resolve this? If you propose a fix, please make it concise.Reviews (2): Last reviewed commit: "fix: address Greptile review findings (b..." | Re-trigger Greptile |
- Move 'import re' to module-level imports (P2: import convention)
- Fix slug truncation order: text[:60].strip('-') prevents trailing hyphen (P2)
- Remove stale DEFT_INTERVIEW_PATH from get_default_paths() docstring (P2)
Summary
Update the Python CLI (
run) to target the new vBRIEF-centric document model (RFC #309, Story K).cmd_project: Now outputs
vbrief/PROJECT-DEFINITION.vbrief.jsoninstead ofPROJECT.md. Uses vBRIEF v0.5 schema withnarrativesfor project identity (Overview, TechStack, Languages, Strategy, Coverage, optional Branching) and emptyitemsarray as scope registry.cmd_spec: Now outputs scope vBRIEFs to
vbrief/proposed/YYYY-MM-DD-descriptive-slug.vbrief.jsoninstead of INTERVIEW.md/PRD.md. Uses vBRIEF v0.5 schema withproposedstatus, features as items, strategy/sizing in metadata. Light path creates minimal narratives; Full path adds rich narrative placeholders.Helpers updated:
get_default_paths(),_read_project_strategy(),_read_project_process()updated for vBRIEF JSON format with legacy PROJECT.md fallback. Added_slugify()for D7 filename convention.Tests: 51 tests covering all cmd_spec and cmd_project behavior (12 project, 11 cmd_spec, 28 spec_sizing+user_defaults).
Closes #320
Related Issues
Closes #320
Part of #309 (RFC: vBRIEF-centric document model)
Checklist
/deft:change-- N/A (task dispatched as Story K from RFC RFC: vBRIEF-centric document model for Deft Directive #309)CHANGELOG.md-- added entry under[Unreleased]ROADMAP.md-- N/A (updated at release time per AGENTS.md convention)Post-Merge