create_story.py — one-command Story creation (CVN-N014-EC-S07)¶
scripts/create_story.py performs the create + scaffold half of the Story
workflow in a single command, baking in the two rules the manual ritual keeps
getting wrong (memory feedback_create_op_wp_for_stories):
- Always create the OpenProject work package — not just the GH issue —
by delegating to the tested
scripts/openproject_import_gh.py(never re-implemented). - The OP wp lands as
New(ADR-69). This script never advances status; that stays/story-advance(a gated, operator-approved skill).
What it does (and where it stops)¶
create_story.py: GH issue → OP wp (New) → Epic registry → plan-dossier skeleton → STOP
/story-advance: → In specification → … → Closed
| Step | Action |
|---|---|
| 0 | duplicate pre-check — refuse if an issue already carries the cvn-id (unless --force) |
| 1 | write documentation/reviews/<date>-<cvn>-<slug>-plan.md skeleton (satisfies the → In specification gate; idempotent) |
| 2 | gh issue create — title normalized to [Story] <cvn-id> — <title> |
| 3 | openproject_import_gh.py --type Story — OP wp lands as New |
| 4 | Epic registry — append a row to documentation/epics/<parent>*.md if it exists, else comment on the parent Epic's GH issue (format-agnostic, never edits a free-form body) |
Usage¶
# preview everything, create nothing
python scripts/create_story.py --cvn-id CVN-N014-EC-S08 --parent-cvn-id CVN-N014-EC \
--title "pr-merge (SKILL)" --dry-run
# real run (issue body defaults to TEMPLATE_story.md; pass --body / --body-file to override)
python scripts/create_story.py --cvn-id CVN-N014-EC-S08 --parent-cvn-id CVN-N014-EC \
--title "pr-merge (SKILL)" --body-file /tmp/body.md
Flags: --body / --body-file (mutually exclusive), --labels a,b (default none —
this repo has no story label), --gh-repo owner/repo, --force (bypass the
duplicate check), --dry-run, -v.
Failure semantics (ADR-25, no silent half-done state)¶
- Bad
--cvn-id/--parent-cvn-id→ exit2, nothing created. - Duplicate cvn-id (issue exists, no
--force) → exit3. - OP import fails after the issue was created → exit
4, prints the created issue number and the exactopenproject_import_gh.pyretry command so you re-run only the failed step against the existing issue. - Any other command failure → exit
1with the failing command surfaced.
The agreed flow (create → … → Specified)¶
1. create_story.py → GH issue + OP wp (New) + Epic registry + plan skeleton
2. /story-advance <wp> → In specification (gate: plan dossier exists ✅ — the skeleton)
3. complete the plan dossier + committee plan_review
4. /story-advance <wp> → Specified (gate: plan_review PASSED ✅)
5. … In progress → impl → CR → pr_review → merge → Developed → … → Closed
create_story.py covers step 1 only.
The /create-story SKILL (judgment layer, CVN-N014-EC-S14)¶
.claude/skills/create-story/SKILL.md wraps this SCRIPT as a user-only slash command
(disable-model-invocation: true) — the judgment layer over the deterministic CLI, mirroring
/story-advance over op_story_transition.py (S11/S01) and /loki-query over loki_query.py (S12/S02):
- derives the next free
S-number (scans GH issues anddocumentation/Epic dossiers), - resolves the parent Epic (read-only; STOPs if ambiguous/missing — never an orphan Story),
- shapes title/body from intent, guards duplicates (surfaces
--force, never auto-bypass), - previews + requires explicit operator approval before the write, runs the SCRIPT
--dry-runthen for real, - stops at
New(does not chain into/story-advance) and surfaces the SCRIPT's exit-4 retry command verbatim.
The SKILL re-implements none of the create logic — it calls this SCRIPT (34 unit tests) and emits
skill_invoked/skill_operator_approval/skill_completed (S00 §8). Use /create-story for the
day-to-day "open a Story" act; call create_story.py directly for scripted/headless creation.
Tests¶
tests/unit/test_create_story.py — arg/cvn-id validation, title normalization
(incl. idempotence), slug + dossier path, skeleton render, command assembly, and
the orchestrated main flow with gh/OP calls mocked (dup-refuse, happy path,
OP-failure retry surface).