Skip to content

feat(cli): update cmd_spec and cmd_project for vBRIEF-centric model#355

Merged
MScottAdams merged 2 commits intophase2/vbrief-cutoverfrom
agent1/feat/320-cli-vbrief
Apr 13, 2026
Merged

feat(cli): update cmd_spec and cmd_project for vBRIEF-centric model#355
MScottAdams merged 2 commits intophase2/vbrief-cutoverfrom
agent1/feat/320-cli-vbrief

Conversation

@MScottAdams
Copy link
Copy Markdown
Collaborator

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.json instead of PROJECT.md. Uses vBRIEF v0.5 schema with narratives for project identity (Overview, TechStack, Languages, Strategy, Coverage, optional Branching) and empty items array as scope registry.

cmd_spec: Now outputs scope vBRIEFs to vbrief/proposed/YYYY-MM-DD-descriptive-slug.vbrief.json instead of INTERVIEW.md/PRD.md. Uses vBRIEF v0.5 schema with proposed status, 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)
  • Tests pass locally (51 tests, 1 pre-existing xfail in test_contracts.py unrelated to this PR)

Post-Merge

…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-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 13, 2026

Greptile Summary

This PR updates cmd_spec and cmd_project to target the vBRIEF v0.5 document model: cmd_project now writes vbrief/PROJECT-DEFINITION.vbrief.json (replacing PROJECT.md), and cmd_spec outputs scope vBRIEFs to vbrief/proposed/YYYY-MM-DD-slug.vbrief.json (replacing INTERVIEW.md/PRD.md). Helpers _read_project_strategy, _read_project_process, and a new _slugify are updated/added accordingly, with 51 tests covering the new paths.

Confidence Score: 5/5

Safe 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

Filename Overview
run Core CLI rewrite: cmd_project and cmd_spec now emit vBRIEF v0.5 JSON; _slugify added; overwrite guard fires after full interview; empty slug edge case produces malformed filename.
run.py Import shim updated to re-export _slugify; minor style concern exporting a private helper in all.
tests/cli/test_cmd_spec.py New test file with 11 tests; correctly covers Light/Full paths, filename convention, schema, force-overwrite, and _slugify.
tests/cli/test_project.py Rewrites project tests for vBRIEF schema; 12 tests covering schema validation, narratives, branching, dedup, and status fields.
tests/cli/test_spec_sizing.py Updated for vBRIEF model; covers _read_project_process, _read_project_strategy, sizing gate, Light/Full paths, process override, and strategy-aware metadata.
tests/conftest.py isolated_env fixture updated to set DEFT_VBRIEF_PROPOSED and DEFT_PROJECT_PATH to new vbrief paths; all fixtures look correct.
tests/cli/test_project_user_defaults.py USER.md defaults integration tests updated for vBRIEF output paths; assertions correctly verify Language/Strategy propagation.
CHANGELOG.md Entry added under [Unreleased] for #320; format is consistent with existing entries.

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])
Loading
Prompt To Fix All With AI
This 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)
@MScottAdams MScottAdams merged commit 8328074 into phase2/vbrief-cutover Apr 13, 2026
2 of 3 checks passed
@MScottAdams MScottAdams deleted the agent1/feat/320-cli-vbrief branch April 13, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant