From ee249dd0e027d01402067353bee616de9d9a57b8 Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 07:35:55 -0400 Subject: [PATCH 1/6] docs(skills): add soft-wrap prose authoring rule to md-generating skills Add a uniformly-worded soft-wrap rule to every skill that authors markdown prose artifacts (docs, ed, spec, dev, pm, em, ux), placed at each skill's existing style/format authority and, for dispatchers, in SKILL.md so it loads on every verb. Generated prose must be one continuous line per paragraph; the renderer handles wrapping. --- skills/dev/SKILL.md | 1 + skills/dev/agents/proposal-writer.md | 2 ++ skills/docs/references/language-guide.md | 17 +++++++++++++++++ skills/ed/references/pedagogy.md | 5 +++++ skills/em/SKILL.md | 1 + skills/em/references/plan-template.md | 2 ++ skills/pm/SKILL.md | 1 + skills/pm/references/research-standards.md | 2 ++ skills/spec/SKILL.md | 4 ++++ .../spec/references/implementation-template.md | 2 ++ skills/spec/references/plan-template.md | 1 + skills/spec/references/user-guide-template.md | 1 + skills/ux/references/style-report-template.md | 2 ++ 13 files changed, 41 insertions(+) diff --git a/skills/dev/SKILL.md b/skills/dev/SKILL.md index 0aa118d..bb33ccf 100644 --- a/skills/dev/SKILL.md +++ b/skills/dev/SKILL.md @@ -24,6 +24,7 @@ Pass your intent directly on the invocation line — `explore` and `plan` procee - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged +- **Markdown output: soft-wrap prose, never hard-wrap** — when any dev workflow or agent writes a `.md` artifact (proposals, research notes, docs, plans), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. ## Step 0: Parse Arguments diff --git a/skills/dev/agents/proposal-writer.md b/skills/dev/agents/proposal-writer.md index 10a1164..ada08bd 100644 --- a/skills/dev/agents/proposal-writer.md +++ b/skills/dev/agents/proposal-writer.md @@ -40,6 +40,8 @@ Your prompt tells you: **One-sentence verdict.** The opening `>` line must be a verdict a decision-maker can read in isolation and understand the approach's core trade-off. Not "this is a solid approach" — something like "Best when you need strong type safety at the cost of a heavier migration." +**Soft-wrap prose — never hard-wrap.** Write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + ## Output Before reporting, verify: does the proposal contain a concrete "How it comes together" section with at least one cited library API? If not, add it before writing the file. diff --git a/skills/docs/references/language-guide.md b/skills/docs/references/language-guide.md index 8ea93cf..1360f66 100644 --- a/skills/docs/references/language-guide.md +++ b/skills/docs/references/language-guide.md @@ -85,3 +85,20 @@ just dev ### 10. Link; don't duplicate If auth.md already explains the session format, link to it — don't repeat it. Use the `## References` section. Duplication creates maintenance debt. + +### 11. Soft-wrap prose — never hard-wrap + +Write each paragraph as a single continuous line. Do not insert manual newlines to wrap prose at a fixed column width — on narrow screens and non-reflowing renderers, hard breaks wrap badly. Let the renderer handle wrapping. + +Bad (hard-wrapped at ~80 cols): +``` +The session is stored in an HttpOnly cookie so JavaScript running in the +browser cannot read it, which prevents XSS attacks from stealing sessions. +``` + +Good (one paragraph, one line): +``` +The session is stored in an HttpOnly cookie so JavaScript running in the browser cannot read it, which prevents XSS attacks from stealing sessions. +``` + +Newlines still separate paragraphs, list items, headings, and code fences — only mid-paragraph line breaks are forbidden. diff --git a/skills/ed/references/pedagogy.md b/skills/ed/references/pedagogy.md index ffd12b2..440a425 100644 --- a/skills/ed/references/pedagogy.md +++ b/skills/ed/references/pedagogy.md @@ -43,3 +43,8 @@ Prefer a diagram over a paragraph whenever a concept has spatial, structural, se - End each concept with a check: a question, a "predict the output", or a "why does this work?" prompt. - Prefer worked examples that show every intermediate step over stating a result. + +## 6. Soft-wrap prose — never hard-wrap + +- Write each paragraph as a single continuous line. Do not insert manual newlines to wrap prose at a fixed column width — hard breaks reflow badly on narrow screens and non-reflowing renderers. Let the renderer wrap. +- Newlines still separate paragraphs, list items, headings, and code fences (and mermaid/LaTeX/ASCII blocks keep their own line structure) — only mid-paragraph line breaks in prose are forbidden. diff --git a/skills/em/SKILL.md b/skills/em/SKILL.md index 160946c..dc0e098 100644 --- a/skills/em/SKILL.md +++ b/skills/em/SKILL.md @@ -25,6 +25,7 @@ Pass your intent directly on the invocation line — `plan` proceeds immediately - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged +- **Markdown output: soft-wrap prose, never hard-wrap** — when any em workflow or agent writes a `.md` artifact (epic plans, roadmaps, research notes), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. ## Step 0: Parse Arguments diff --git a/skills/em/references/plan-template.md b/skills/em/references/plan-template.md index 6c52af7..253ef6a 100644 --- a/skills/em/references/plan-template.md +++ b/skills/em/references/plan-template.md @@ -2,6 +2,8 @@ Use this template for `.codevoyant/em/plans/{slug}/plan.md`. +**Markdown output:** Soft-wrap prose — never hard-wrap. Write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + --- # {Project Name} diff --git a/skills/pm/SKILL.md b/skills/pm/SKILL.md index 7b4c593..e15d69b 100644 --- a/skills/pm/SKILL.md +++ b/skills/pm/SKILL.md @@ -25,6 +25,7 @@ Pass your intent directly on the invocation line — `explore` and `plan` procee - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged +- **Markdown output: soft-wrap prose, never hard-wrap** — when any pm workflow or agent writes a `.md` artifact (PRDs, roadmaps, research findings, plans), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. ## Step 0: Parse Arguments diff --git a/skills/pm/references/research-standards.md b/skills/pm/references/research-standards.md index 9072b6f..5cce7a1 100644 --- a/skills/pm/references/research-standards.md +++ b/skills/pm/references/research-standards.md @@ -20,6 +20,8 @@ Every finding in a research artifact must use this structure: > Supporting evidence: {quote, file line range, or data point} ``` +**Markdown output:** Soft-wrap prose — never hard-wrap. Write each paragraph (Observation, Interpretation, evidence) as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + --- ## Confidence Levels diff --git a/skills/spec/SKILL.md b/skills/spec/SKILL.md index 964d0b1..5b61bac 100644 --- a/skills/spec/SKILL.md +++ b/skills/spec/SKILL.md @@ -36,6 +36,10 @@ command -v npx >/dev/null 2>&1 || echo "MISSING: npx" - **Coding agents always receive a workflow checklist** — see `references/workflow-checklist.md` - See `references/workflows/` for per-verb behaviour; see `references/` for all templates +## Global Authoring Rule — Markdown output + +When you (or any spec agent/workflow) write a `.md` artifact — plan.md, phase files, user-guide.md, PR body, or any generated document — **soft-wrap prose, never hard-wrap**. Write each paragraph as a single continuous line; do not insert manual newlines to wrap prose at a fixed column width. Let the renderer wrap. Newlines still separate paragraphs, list items, headings, and code fences — only mid-paragraph line breaks are forbidden. + ## Step 0: Parse Arguments ```bash diff --git a/skills/spec/references/implementation-template.md b/skills/spec/references/implementation-template.md index 19659b5..4c7567d 100644 --- a/skills/spec/references/implementation-template.md +++ b/skills/spec/references/implementation-template.md @@ -14,6 +14,8 @@ Use this structure for each `implementation/phase-N.md` file created in Step 5.5 **Build system preservation:** Do NOT modify the build system, CI config, or dependencies unless this phase is explicitly about them. If the project built before you started, it must build after every task. If a change would require an unplanned build system modification, stop and flag it. +**Markdown output:** Soft-wrap prose — never hard-wrap. Write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + ### Task Runner Commands {List the relevant task runner commands for this phase. ALWAYS use these — never invent equivalent shell commands. Discover them by reading `mise.toml`, `justfile`, `Makefile`, or `package.json` scripts directly.} - Build: `{e.g. just build | make build | task build | mise run build}` diff --git a/skills/spec/references/plan-template.md b/skills/spec/references/plan-template.md index f75cb68..fbb3f93 100644 --- a/skills/spec/references/plan-template.md +++ b/skills/spec/references/plan-template.md @@ -58,3 +58,4 @@ Write this structure to `.codevoyant/plans/{plan-name}/plan.md` when creating a - Add ✅ to phase header only when all tasks in that phase are complete - Do NOT include detailed implementation specs in plan.md — those go in `implementation/phase-N.md` - Use `## References` (not `## Resources`) for links and external references +- Soft-wrap prose — never hard-wrap: write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. diff --git a/skills/spec/references/user-guide-template.md b/skills/spec/references/user-guide-template.md index cd56369..69d9091 100644 --- a/skills/spec/references/user-guide-template.md +++ b/skills/spec/references/user-guide-template.md @@ -72,3 +72,4 @@ Write this file to `$PLAN_DIR/user-guide.md` when creating a plan. Fill in what - Focus on what the user sees and interacts with, not how it works inside - Every code example must be runnable as-is (or clearly marked as pseudo-code) - Update this file incrementally during execution — don't leave it as a stub +- Soft-wrap prose — never hard-wrap: write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. diff --git a/skills/ux/references/style-report-template.md b/skills/ux/references/style-report-template.md index fd31f41..96ccbb7 100644 --- a/skills/ux/references/style-report-template.md +++ b/skills/ux/references/style-report-template.md @@ -2,6 +2,8 @@ Use for `docs/ux/style-research/{source}/style-report.md`. +**Markdown output:** Soft-wrap prose — never hard-wrap. Write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + --- # {SOURCE_NAME} Style Report From ad5bc92c6beb0b4bb1b280663850d4b623a7c024 Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 07:37:39 -0400 Subject: [PATCH 2/6] feat(flow): forward unrecognized flags like --branch to steps Extend the shared flow-dir arg-parse into a three-bucket parser: flow-control flags (--global/-g, and --set for go) are peeled off as before, and every other flag (e.g. --branch feature/x) is collected in order into PASSTHROUGH_FLAGS instead of being silently dropped. new bakes passthrough flags onto each stored step command; go appends them to each resolved step command at run time. SKILL.md and help.md document the forwarding behavior. --- skills/flow/SKILL.md | 4 ++- skills/flow/references/flow-dir.md | 31 ++++++++++++++++++++++++ skills/flow/references/workflows/go.md | 9 ++++--- skills/flow/references/workflows/help.md | 6 +++++ skills/flow/references/workflows/new.md | 13 ++++++---- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/skills/flow/SKILL.md b/skills/flow/SKILL.md index d5558a6..3507cd0 100644 --- a/skills/flow/SKILL.md +++ b/skills/flow/SKILL.md @@ -41,6 +41,8 @@ Dispatch to: references/workflows/{VERB}.md The `--global` / `-g` flag (store or read a flow under `~/.codevoyant/flows` instead of the local `.codevoyant/flows`) is **not** a verb — pass it through unchanged; each workflow parses it via `references/flow-dir.md`. +Any flag other than the flow-control flags (`--global`/`-g`, and `--set` for `go`) is **not** dropped: `references/flow-dir.md` collects it into `PASSTHROUGH_FLAGS`, and the `new`/`go` workflows forward it to the step commands. This is how `--branch feature/x` reaches the skills a flow runs (e.g. so `spec new` works on a separate branch). + ## Workflow index | Verb | File | Purpose | @@ -58,4 +60,4 @@ The `--global` / `-g` flag (store or read a flow under `~/.codevoyant/flows` ins 2. Apply aliases (run/exec/start → go; ls → list; show/review → status; export/publish → save). 3. If VERB is empty or unrecognized, default to `help`. 4. Read and execute the corresponding workflow file from `references/workflows/{VERB}.md`. -5. Pass all remaining arguments (including any `--global`/`-g` flag) to the workflow unchanged. +5. Pass all remaining arguments (including any `--global`/`-g` flag and any other unrecognized flags such as `--branch`) to the workflow unchanged — the workflow parses flow-control flags and buckets the rest into `PASSTHROUGH_FLAGS` via `references/flow-dir.md`. diff --git a/skills/flow/references/flow-dir.md b/skills/flow/references/flow-dir.md index 332d02f..f1551d5 100644 --- a/skills/flow/references/flow-dir.md +++ b/skills/flow/references/flow-dir.md @@ -23,6 +23,37 @@ FLOWS_DIR="$FLOW_ROOT/flows" - `~` always means the real home directory — use `"$HOME"` in bash, never a literal `~` inside quotes. - Create the global root on demand: `mkdir -p "$HOME/.codevoyant/flows"` (safe if it already exists). +## Pass-through flags (`--branch` and any other unrecognized flag) + +Flow recognizes exactly these **flow-control** flags: `--global`/`-g` (all verbs) and `--set key=value` (`go` only; parsed in `go.md`). **Every other flag must be preserved, not dropped** — for example `--branch feature/x`, which callers use to run a flow's steps on a separate branch. + +While stripping the flow-control flags, collect all remaining flags (any token starting with `-`, plus its value token when the following token does not itself start with `-`) into an ordered list `PASSTHROUGH_FLAGS`, and remove them from the positional args. Boolean flags (no value) are kept as-is; `--flag=value` forms are kept whole. + +```bash +# Walk the args left→right; peel flow-control flags, bucket the rest into PASSTHROUGH_FLAGS, +# leave true positionals (flow name, steps, input) in POSITIONALS. +GLOBAL=false +PASSTHROUGH_FLAGS=() # e.g. (--branch feature/x) +POSITIONALS=() +set -- $ARGS +while [ $# -gt 0 ]; do + case "$1" in + --global|-g) GLOBAL=true; shift ;; + --set) PASSTHROUGH_FLAGS+=("$1" "$2"); shift 2 ;; # go.md re-reads --set from PASSTHROUGH; harmless elsewhere + --*|-*) + if [ $# -ge 2 ] && [ "${2#-}" = "$2" ]; then # next token is a value (not another flag) + PASSTHROUGH_FLAGS+=("$1" "$2"); shift 2 + else + PASSTHROUGH_FLAGS+=("$1"); shift # boolean or --flag=value + fi ;; + *) POSITIONALS+=("$1"); shift ;; + esac +done +``` + +- `--set` is a special case for `go`: `go.md` parses `--set key=value` into `PARAMS` itself, so it consumes those from `PASSTHROUGH_FLAGS` before forwarding. For `new`/`status`/`list`, `--set` is not meaningful and simply rides along in `PASSTHROUGH_FLAGS` (or is ignored) — this shared rule does not need per-verb branches. +- `PASSTHROUGH_FLAGS` is what `new` bakes into stored step commands and what `go` appends to each resolved step command at run time (see those workflows). + ## Resolving a flow by name (for `go`, `status`, `save`) When the user names an existing flow to run/inspect, resolve which scope holds it: diff --git a/skills/flow/references/workflows/go.md b/skills/flow/references/workflows/go.md index 87565c4..247ad28 100644 --- a/skills/flow/references/workflows/go.md +++ b/skills/flow/references/workflows/go.md @@ -9,10 +9,13 @@ Steps run **non-interactively** — a subagent cannot prompt the user. When a st ``` --global / -g → read the flow from ~/.codevoyant/flows (see references/flow-dir.md) --set key=value → bind a named parameter (repeatable, e.g. --set feature="add OAuth" --set env=staging) -FLOW_NAME = first non-flag positional arg (required) -INPUT = all remaining bare (non-flag, non-name) positional text, joined with spaces → the {{input}} parameter +--branch, etc. → any other flag → PASSTHROUGH_FLAGS (see references/flow-dir.md), appended to every step this run +FLOW_NAME = first positional arg (POSITIONALS[0]; required) +INPUT = all remaining positional text (POSITIONALS[1..]), joined with spaces → the {{input}} parameter ``` +Parse flags via `references/flow-dir.md`. From the resulting `PASSTHROUGH_FLAGS`, pull out any `--set key=value` pairs into `PARAMS` (below); the remainder (e.g. `--branch feature/x`) stays in `PASSTHROUGH_FLAGS` and is appended to every step command this run (Step 2). + If `FLOW_NAME` is missing, error: "Usage: /flow go [input text] [--set k=v] [--global]. A flow name is required." Build the parameter map `PARAMS`: @@ -42,7 +45,7 @@ Initialize the **flow context** accumulator `CONTEXT`. **On resume:** if `FLOW_D For each pending step in order: -1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`. Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` +1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`, then append this run's `PASSTHROUGH_FLAGS` (e.g. `--branch feature/x`) to the command string if the step does not already carry them (steps that baked the flag in at `new` time already have it — do not duplicate). Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` 2. Read `FLOW_DIR/implementation/step-N.md` to get the agent prompt. Prepare the prompt for this run by filling its injection points: - Replace `{step-command}` occurrences with `RESOLVED_COMMAND` (substitute `{{placeholders}}` here too). diff --git a/skills/flow/references/workflows/help.md b/skills/flow/references/workflows/help.md index 7733e21..eddda02 100644 --- a/skills/flow/references/workflows/help.md +++ b/skills/flow/references/workflows/help.md @@ -25,6 +25,12 @@ Storage: local (default) .codevoyant/flows// — this project only global (--global) ~/.codevoyant/flows// — reusable across all projects +Forwarding flags to steps: + Any flag other than --global/-g (and --set for `go`) is forwarded to every step + command — e.g. run a flow's steps on a branch: + /flow new ship "/spec new {{input}}" "/spec go" --branch feature/x (bakes it in) + /flow go ship "add OAuth" --branch feature/x (one-off run) + Parameters (dynamic input): Put {{placeholders}} in any step. At run time: • bare text after the name binds to {{input}} diff --git a/skills/flow/references/workflows/new.md b/skills/flow/references/workflows/new.md index 49702e9..1b99095 100644 --- a/skills/flow/references/workflows/new.md +++ b/skills/flow/references/workflows/new.md @@ -5,12 +5,13 @@ Create a new named flow — a `{flows-dir}/{slug}/flow.md` checklist plus one `i ## Step 0: Parse arguments ``` ---global / -g → store under ~/.codevoyant/flows (see references/flow-dir.md); else local .codevoyant/flows -FLOW_NAME = first non-flag positional arg (required; error if missing) -STEPS = remaining positional args (each is one step command string) +--global / -g → store under ~/.codevoyant/flows (see references/flow-dir.md); else local .codevoyant/flows +--branch, etc. → any other flag → PASSTHROUGH_FLAGS (see references/flow-dir.md), baked onto every step command +FLOW_NAME = first positional arg (POSITIONALS[0]; required; error if missing) +STEPS = remaining positionals (POSITIONALS[1..]; each is one step command string) ``` -Resolve `FLOWS_DIR` per `references/flow-dir.md` (`.codevoyant/flows` local, `$HOME/.codevoyant/flows` if `--global`). Strip `--global`/`-g` before reading positionals. +Resolve `FLOWS_DIR` and parse flags per `references/flow-dir.md`: it sets `GLOBAL`, fills `PASSTHROUGH_FLAGS` with every non-flow-control flag (e.g. `--branch feature/x`), and leaves the flow name + step strings in `POSITIONALS`. Read `FLOW_NAME`/`STEPS` from `POSITIONALS`. If `FLOW_NAME` is missing, error: "Usage: /flow new [steps...] [--global]. A flow name is required." @@ -79,11 +80,13 @@ For each step N (1-based), create `FLOW_DIR/implementation/step-N.md` using `ref Fill in: - `{N}` = step number -- `{step-command}` = the step command string, with `{{placeholders}}` left **verbatim** (they are resolved at run time by `go.md`) +- `{step-command}` = the step command string, with `{{placeholders}}` left **verbatim** (they are resolved at run time by `go.md`), and with `PASSTHROUGH_FLAGS` appended to the command string when non-empty (e.g. a step `/spec new {{objective}}` created with `--branch feature/x` is stored as `/spec new {{objective}} --branch feature/x`). This bakes run-on-a-branch (and any other forwarded flag) into the flow definition. - `{flow-name}` = slug - `{total}` = total number of steps - Leave the `## Parameters` and `## Flow context so far` sections as their template placeholders — `go.md` fills them in at run time. +If `PASSTHROUGH_FLAGS` is non-empty, also append the same flags to each step line written to `flow.md` in Step 2, so the stored checklist matches the step files. + ## Step 4: Report ``` From e6fde7b24d8cb86d5867a6a2050b5aed27d231fe Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 07:50:05 -0400 Subject: [PATCH 3/6] fix(flow): parse flags from preserved argv instead of re-split string The pass-through parser used `set -- $ARGS`, an unquoted re-split that word-splits every argument on whitespace. This shredded multi-word step commands (e.g. `/spec new {{objective}}`) and quoted flag values (`--set feature="add OAuth"`) into separate tokens, corrupting both POSITIONALS and PASSTHROUGH_FLAGS. Iterate the preserved argv (`"$@"`) the dispatcher forwards so each original argument stays one element. Document the argv contract in SKILL.md, add a caveat that flag values starting with `-` aren't supported, and note `--set` is verb-specific (dropped by new, parsed by go). --- skills/flow/SKILL.md | 2 +- skills/flow/references/flow-dir.md | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/skills/flow/SKILL.md b/skills/flow/SKILL.md index 3507cd0..c93f132 100644 --- a/skills/flow/SKILL.md +++ b/skills/flow/SKILL.md @@ -60,4 +60,4 @@ Any flag other than the flow-control flags (`--global`/`-g`, and `--set` for `go 2. Apply aliases (run/exec/start → go; ls → list; show/review → status; export/publish → save). 3. If VERB is empty or unrecognized, default to `help`. 4. Read and execute the corresponding workflow file from `references/workflows/{VERB}.md`. -5. Pass all remaining arguments (including any `--global`/`-g` flag and any other unrecognized flags such as `--branch`) to the workflow unchanged — the workflow parses flow-control flags and buckets the rest into `PASSTHROUGH_FLAGS` via `references/flow-dir.md`. +5. Pass all remaining arguments (including any `--global`/`-g` flag and any other unrecognized flags such as `--branch`) to the workflow unchanged, **as a preserved argv array** — each original argument stays one element, so multi-word step strings (`/spec new {{objective}}`) and quoted flag values (`feature="add OAuth"`) survive intact. The workflow iterates this argv (`"$@"`) to parse flow-control flags and bucket the rest into `PASSTHROUGH_FLAGS` via `references/flow-dir.md`; it must never flatten the args into a single string and re-split them. diff --git a/skills/flow/references/flow-dir.md b/skills/flow/references/flow-dir.md index f1551d5..26767b8 100644 --- a/skills/flow/references/flow-dir.md +++ b/skills/flow/references/flow-dir.md @@ -13,9 +13,13 @@ A flow is a directory `{slug}/` containing `flow.md` and `implementation/step-N. `--global` (alias `-g`) selects the global scope. Parse it out of the arguments in every workflow **before** reading the flow name, and strip it from the positional args. +Work from the **preserved argv** the dispatcher forwarded (each original argument is one array element — a multi-word step string like `/spec new {{objective}}` is a single element, and a quoted flag value like `feature="add OAuth"` stays attached to its flag). Never flatten argv into a string and re-split it: iterate `"$@"` directly. + ```bash GLOBAL=false -case " $ARGS " in *" --global "*|*" -g "*) GLOBAL=true ;; esac +for a in "$@"; do + case "$a" in --global|-g) GLOBAL=true ;; esac +done FLOW_ROOT="$( [ "$GLOBAL" = true ] && echo "$HOME/.codevoyant" || echo ".codevoyant" )" FLOWS_DIR="$FLOW_ROOT/flows" ``` @@ -27,32 +31,35 @@ FLOWS_DIR="$FLOW_ROOT/flows" Flow recognizes exactly these **flow-control** flags: `--global`/`-g` (all verbs) and `--set key=value` (`go` only; parsed in `go.md`). **Every other flag must be preserved, not dropped** — for example `--branch feature/x`, which callers use to run a flow's steps on a separate branch. -While stripping the flow-control flags, collect all remaining flags (any token starting with `-`, plus its value token when the following token does not itself start with `-`) into an ordered list `PASSTHROUGH_FLAGS`, and remove them from the positional args. Boolean flags (no value) are kept as-is; `--flag=value` forms are kept whole. +While stripping the flow-control flags, collect all remaining flags (any element starting with `-`, plus its value element when the following element does not itself start with `-`) into an ordered list `PASSTHROUGH_FLAGS`, and leave the true positionals in `POSITIONALS`. Boolean flags (no value) are kept as-is; `--flag=value` forms are kept whole. + +**Iterate the preserved argv, never a re-split string.** The block below walks `"$@"` — the array the dispatcher forwarded — so each element stays intact: a multi-word step command (`/spec new {{objective}}`) remains one `POSITIONALS` entry, and a flag value carried as one shell word (`feature="add OAuth"`) is not shredded. Do **not** write `set -- $ARGS` (or any unquoted re-split): that word-splits every step string and quoted value on whitespace and corrupts both `POSITIONALS` and `PASSTHROUGH_FLAGS`. ```bash -# Walk the args left→right; peel flow-control flags, bucket the rest into PASSTHROUGH_FLAGS, -# leave true positionals (flow name, steps, input) in POSITIONALS. +# Walk argv "$@" left→right; peel flow-control flags, bucket the rest into PASSTHROUGH_FLAGS, +# leave true positionals (flow name, steps, input) in POSITIONALS. Each "$@" element is +# preserved whole — no re-splitting. GLOBAL=false PASSTHROUGH_FLAGS=() # e.g. (--branch feature/x) POSITIONALS=() -set -- $ARGS while [ $# -gt 0 ]; do case "$1" in --global|-g) GLOBAL=true; shift ;; - --set) PASSTHROUGH_FLAGS+=("$1" "$2"); shift 2 ;; # go.md re-reads --set from PASSTHROUGH; harmless elsewhere + --set) PASSTHROUGH_FLAGS+=("$1" "$2"); shift 2 ;; # go.md re-reads --set; new.md drops it before baking --*|-*) - if [ $# -ge 2 ] && [ "${2#-}" = "$2" ]; then # next token is a value (not another flag) + if [ $# -ge 2 ] && [ "${2#-}" = "$2" ]; then # next element is a value (does not start with -) PASSTHROUGH_FLAGS+=("$1" "$2"); shift 2 else - PASSTHROUGH_FLAGS+=("$1"); shift # boolean or --flag=value + PASSTHROUGH_FLAGS+=("$1"); shift # boolean, --flag=value, or value-less trailing flag fi ;; *) POSITIONALS+=("$1"); shift ;; esac done ``` -- `--set` is a special case for `go`: `go.md` parses `--set key=value` into `PARAMS` itself, so it consumes those from `PASSTHROUGH_FLAGS` before forwarding. For `new`/`status`/`list`, `--set` is not meaningful and simply rides along in `PASSTHROUGH_FLAGS` (or is ignored) — this shared rule does not need per-verb branches. -- `PASSTHROUGH_FLAGS` is what `new` bakes into stored step commands and what `go` appends to each resolved step command at run time (see those workflows). +- **`--set` handling is verb-specific.** `go.md` parses `--set key=value` out of `PASSTHROUGH_FLAGS` into `PARAMS` and forwards the remainder. For `new`, `--set` is **not** a step flag and must **not** be baked into stored steps: `new.md` explicitly drops any `--set key=value` pair from `PASSTHROUGH_FLAGS` before writing step commands (see new.md Step 2). For `status`/`list`, `--set` is inert and simply ignored. +- **Caveat — flag values starting with `-`.** The `[ "${2#-}" = "$2" ]` test treats any following element that starts with `-` as *not* this flag's value, so a legitimate value beginning with `-` (e.g. `--message "-n"`) is read as a value-less flag and the `-n` falls through to `POSITIONALS`. This is acceptable for the `--branch feature/x`-style forwarding flow actually uses; **flag values beginning with `-` are not supported** — pass them in `--flag=value` form (kept whole) if ever needed. +- `PASSTHROUGH_FLAGS` is what `new` bakes into stored step commands (minus `--set`) and what `go` appends to each resolved step command at run time (see those workflows). ## Resolving a flow by name (for `go`, `status`, `save`) From 4831e3f1b02ae8eef66a84b51fdba61050fdfea3 Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 07:50:16 -0400 Subject: [PATCH 4/6] fix(flow): bake passthrough flags when step lines are written The instruction to append PASSTHROUGH_FLAGS to each flow.md step line lived in new.md Step 3, but the step lines are written in Step 2 (which still said "keep placeholders verbatim"). An implementer reading top to bottom wrote flow.md without the flags, diverging the stored checklist from the step files. Move the flag-append into Step 2's step-line spec so flags are baked at write time using a single BAKED_FLAGS string shared by flow.md and the step files. Drop `--set` from BAKED_FLAGS (it is a go-time parameter binding, never a stored step flag). In go.md, make the "don't duplicate" dedup deterministic: skip appending a flag whose name already appears as a token in the step command. --- skills/flow/references/workflows/go.md | 4 ++-- skills/flow/references/workflows/new.md | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/skills/flow/references/workflows/go.md b/skills/flow/references/workflows/go.md index 247ad28..0f2a04b 100644 --- a/skills/flow/references/workflows/go.md +++ b/skills/flow/references/workflows/go.md @@ -14,7 +14,7 @@ FLOW_NAME = first positional arg (POSITIONALS[0]; required) INPUT = all remaining positional text (POSITIONALS[1..]), joined with spaces → the {{input}} parameter ``` -Parse flags via `references/flow-dir.md`. From the resulting `PASSTHROUGH_FLAGS`, pull out any `--set key=value` pairs into `PARAMS` (below); the remainder (e.g. `--branch feature/x`) stays in `PASSTHROUGH_FLAGS` and is appended to every step command this run (Step 2). +Parse flags via `references/flow-dir.md`, iterating the **preserved argv** (`"$@"`) — never re-split a flattened string. From the resulting `PASSTHROUGH_FLAGS`, pull out any `--set key=value` pairs into `PARAMS` (below); the remainder (e.g. `--branch feature/x`) stays in `PASSTHROUGH_FLAGS` and is appended to every step command this run (Step 2). If `FLOW_NAME` is missing, error: "Usage: /flow go [input text] [--set k=v] [--global]. A flow name is required." @@ -45,7 +45,7 @@ Initialize the **flow context** accumulator `CONTEXT`. **On resume:** if `FLOW_D For each pending step in order: -1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`, then append this run's `PASSTHROUGH_FLAGS` (e.g. `--branch feature/x`) to the command string if the step does not already carry them (steps that baked the flag in at `new` time already have it — do not duplicate). Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` +1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`, then append this run's `PASSTHROUGH_FLAGS` (e.g. `--branch feature/x`) to the command string. **Dedup by flag name:** for each flag in `PASSTHROUGH_FLAGS`, skip appending it if its flag name already appears as a token in the step command string (steps that baked the flag in at `new` time already carry it). Concretely, for a flag token `--branch` (the part before any `=`), append it (and its value) only when a whole-word match for `--branch` is absent from the command — this makes the "don't duplicate" rule deterministic rather than best-effort. Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` 2. Read `FLOW_DIR/implementation/step-N.md` to get the agent prompt. Prepare the prompt for this run by filling its injection points: - Replace `{step-command}` occurrences with `RESOLVED_COMMAND` (substitute `{{placeholders}}` here too). diff --git a/skills/flow/references/workflows/new.md b/skills/flow/references/workflows/new.md index 1b99095..ee28c67 100644 --- a/skills/flow/references/workflows/new.md +++ b/skills/flow/references/workflows/new.md @@ -6,12 +6,14 @@ Create a new named flow — a `{flows-dir}/{slug}/flow.md` checklist plus one `i ``` --global / -g → store under ~/.codevoyant/flows (see references/flow-dir.md); else local .codevoyant/flows ---branch, etc. → any other flag → PASSTHROUGH_FLAGS (see references/flow-dir.md), baked onto every step command +--branch, etc. → any other flag → PASSTHROUGH_FLAGS (see references/flow-dir.md); after dropping --set → BAKED_FLAGS, baked onto every step command FLOW_NAME = first positional arg (POSITIONALS[0]; required; error if missing) STEPS = remaining positionals (POSITIONALS[1..]; each is one step command string) ``` -Resolve `FLOWS_DIR` and parse flags per `references/flow-dir.md`: it sets `GLOBAL`, fills `PASSTHROUGH_FLAGS` with every non-flow-control flag (e.g. `--branch feature/x`), and leaves the flow name + step strings in `POSITIONALS`. Read `FLOW_NAME`/`STEPS` from `POSITIONALS`. +Resolve `FLOWS_DIR` and parse flags per `references/flow-dir.md`, iterating the **preserved argv** (`"$@"`) the dispatcher forwarded — never re-split a flattened string, or multi-word step commands and quoted values are corrupted. Parsing sets `GLOBAL`, fills `PASSTHROUGH_FLAGS` with every non-flow-control flag (e.g. `--branch feature/x`), and leaves the flow name + step strings in `POSITIONALS` (each step is one element). Read `FLOW_NAME`/`STEPS` from `POSITIONALS`. + +**Drop `--set` from the flags `new` bakes.** `--set key=value` is a `go`-time parameter binding, not a step flag — appending it to a stored step (like `--branch`) is never intended. Before using `PASSTHROUGH_FLAGS` below, remove any `--set` element and its following `key=value` value element, so only real forwarded flags (e.g. `--branch feature/x`) get baked in. Call the filtered result `BAKED_FLAGS`. If `FLOW_NAME` is missing, error: "Usage: /flow new [steps...] [--global]. A flow name is required." @@ -65,13 +67,15 @@ Use `references/flow-template.md` as the template. Fill in: - `{timestamp}` = current ISO timestamp - `{Status}` = `Active` - **Parameters** section: one bullet per token in `PARAMS` (`` - `{{name}}` — {short description you infer from how the step uses it} ``). If `PARAMS` is empty, replace the list with `_none_`. -- Steps checklist: one line per step, numbered, all unchecked `[ ]` — keep `{{placeholders}}` verbatim (do not substitute). +- Steps checklist: one line per step, numbered, all unchecked `[ ]` — keep `{{placeholders}}` verbatim (do not substitute), and **bake `BAKED_FLAGS` into each step command** when non-empty (see below). Each step line format: ``` -N. [ ] {step-command-string} +N. [ ] {step-command-string}{ BAKED_FLAGS, space-joined, when non-empty} ``` +When `BAKED_FLAGS` is non-empty, append it (space-separated) to every step command as it is written here — e.g. a step `/spec new {{objective}}` created with `--branch feature/x` is written as `1. [ ] /spec new {{objective}} --branch feature/x`. Bake the flags at write time so the stored checklist matches the step files produced in Step 3; do not defer this. (`--set` was already removed from `BAKED_FLAGS` in Step 0.) + Write to `FLOW_DIR/flow.md`. ## Step 3: Write step implementation files @@ -80,12 +84,12 @@ For each step N (1-based), create `FLOW_DIR/implementation/step-N.md` using `ref Fill in: - `{N}` = step number -- `{step-command}` = the step command string, with `{{placeholders}}` left **verbatim** (they are resolved at run time by `go.md`), and with `PASSTHROUGH_FLAGS` appended to the command string when non-empty (e.g. a step `/spec new {{objective}}` created with `--branch feature/x` is stored as `/spec new {{objective}} --branch feature/x`). This bakes run-on-a-branch (and any other forwarded flag) into the flow definition. +- `{step-command}` = the step command string, with `{{placeholders}}` left **verbatim** (they are resolved at run time by `go.md`), and with the same `BAKED_FLAGS` appended that Step 2 baked into the flow.md line (e.g. a step `/spec new {{objective}}` created with `--branch feature/x` is stored as `/spec new {{objective}} --branch feature/x`). Use the identical flag string for step N here and in flow.md so the two never diverge. This bakes run-on-a-branch (and any other forwarded flag) into the flow definition. - `{flow-name}` = slug - `{total}` = total number of steps - Leave the `## Parameters` and `## Flow context so far` sections as their template placeholders — `go.md` fills them in at run time. -If `PASSTHROUGH_FLAGS` is non-empty, also append the same flags to each step line written to `flow.md` in Step 2, so the stored checklist matches the step files. +Because the flags were baked into the flow.md step lines in Step 2 and into the step files here using the same `BAKED_FLAGS` string, the stored checklist and the step files stay in sync — there is nothing left to reconcile afterward. ## Step 4: Report From a83a1fb6e1edfffdefce7c75baecc4754feb4cb0 Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 07:50:27 -0400 Subject: [PATCH 5/6] fix(skills): apply soft-wrap rule consistently across generating skills The soft-wrap markdown rule had three different placements: dev/em/pm used a Critical-Rules bullet, spec used a bespoke `## Global Authoring Rule` heading, and docs/ed/ux carried it only in a references template with no SKILL.md entry. qa was missed entirely despite writing debug reports, smoke reports, and markdown posted to GitHub/GitLab/Linear. Standardize on the Critical-Rules bullet: convert spec's bespoke heading, add the bullet to docs/ed/ux SKILL.md (pointing to their full template), and add a Critical Rules section with the bullet to qa. --- skills/docs/SKILL.md | 1 + skills/ed/SKILL.md | 1 + skills/qa/SKILL.md | 4 ++++ skills/spec/SKILL.md | 5 +---- skills/ux/SKILL.md | 1 + 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/skills/docs/SKILL.md b/skills/docs/SKILL.md index 6ae2859..d281a19 100644 --- a/skills/docs/SKILL.md +++ b/skills/docs/SKILL.md @@ -16,6 +16,7 @@ Triggers: "docs new", "docs update", "docs review", "docs retcon", "create docs" - **Never execute workflow logic here** — this file only parses args and dispatches - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently +- **Markdown output: soft-wrap prose, never hard-wrap** — when any docs workflow writes a `.md` artifact, write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. (Full guidance: `references/language-guide.md`.) ## Step 0: Parse Arguments diff --git a/skills/ed/SKILL.md b/skills/ed/SKILL.md index 84ebab1..efa7b5b 100644 --- a/skills/ed/SKILL.md +++ b/skills/ed/SKILL.md @@ -29,6 +29,7 @@ Pass your intent directly: - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged +- **Markdown output: soft-wrap prose, never hard-wrap** — when any ed workflow writes a `.md` artifact (study notes, guides, syllabi, quizzes), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. (Full guidance: `references/pedagogy.md`.) ## Step 0: Parse Arguments diff --git a/skills/qa/SKILL.md b/skills/qa/SKILL.md index 85aae31..5b9b691 100644 --- a/skills/qa/SKILL.md +++ b/skills/qa/SKILL.md @@ -5,6 +5,10 @@ license: MIT compatibility: Works on Claude Code. smoke verb requires agent-browser (npx @vercel/agent-browser). --- +## Critical Rules + +- **Markdown output: soft-wrap prose, never hard-wrap** — when any qa workflow writes a `.md` artifact (debug report, smoke report) or markdown posted to GitHub/GitLab/Linear via `report`, write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. + ## Dispatcher ``` diff --git a/skills/spec/SKILL.md b/skills/spec/SKILL.md index 5b61bac..5fa6bab 100644 --- a/skills/spec/SKILL.md +++ b/skills/spec/SKILL.md @@ -34,12 +34,9 @@ command -v npx >/dev/null 2>&1 || echo "MISSING: npx" - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged - **Workflow files are authoritative** — do not duplicate workflow logic in this file - **Coding agents always receive a workflow checklist** — see `references/workflow-checklist.md` +- **Markdown output: soft-wrap prose, never hard-wrap** — when any spec workflow or agent writes a `.md` artifact (plan.md, phase files, user-guide.md, PR body, or any generated document), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. - See `references/workflows/` for per-verb behaviour; see `references/` for all templates -## Global Authoring Rule — Markdown output - -When you (or any spec agent/workflow) write a `.md` artifact — plan.md, phase files, user-guide.md, PR body, or any generated document — **soft-wrap prose, never hard-wrap**. Write each paragraph as a single continuous line; do not insert manual newlines to wrap prose at a fixed column width. Let the renderer wrap. Newlines still separate paragraphs, list items, headings, and code fences — only mid-paragraph line breaks are forbidden. - ## Step 0: Parse Arguments ```bash diff --git a/skills/ux/SKILL.md b/skills/ux/SKILL.md index f685a74..1046df6 100644 --- a/skills/ux/SKILL.md +++ b/skills/ux/SKILL.md @@ -25,6 +25,7 @@ Pass your intent directly on the invocation line — `explore` and `prototype` p - **Step 0 always runs first** — no exceptions - **Unknown verb → run `help.md`** — never error silently - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged +- **Markdown output: soft-wrap prose, never hard-wrap** — when any ux workflow writes a `.md` artifact (style reports, approach notes, prototype docs), write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences. (Full guidance: `references/style-report-template.md`.) ## Step 0: Parse Arguments From 22026f4c755c7cf8d8e1ea4bc5a2d59a88efb61a Mon Sep 17 00:00:00 2001 From: Siddharth Kapoor Date: Fri, 10 Jul 2026 12:56:27 -0400 Subject: [PATCH 6/6] fix(flow): run-time flags override baked step flags at go time Address review on PR #21: - go.md Step 2 now merges PASSTHROUGH_FLAGS deterministically: a run-time flag with the same name as one baked into a step at new time replaces the baked value (explicit run-time value wins) instead of being silently dropped by dedup; same-valued flags stay single; a flag never duplicates. - flow-dir.md: cross-reference the two GLOBAL-parse snippets (minimal loop for status/list/save vs. the fuller argv walker for new/go) so a future maintainer editing one keeps --global handling in sync; document that run-time flags override baked ones. --- skills/flow/references/flow-dir.md | 6 +++++- skills/flow/references/workflows/go.md | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/skills/flow/references/flow-dir.md b/skills/flow/references/flow-dir.md index 26767b8..2d29cff 100644 --- a/skills/flow/references/flow-dir.md +++ b/skills/flow/references/flow-dir.md @@ -15,6 +15,8 @@ A flow is a directory `{slug}/` containing `flow.md` and `implementation/step-N. Work from the **preserved argv** the dispatcher forwarded (each original argument is one array element — a multi-word step string like `/spec new {{objective}}` is a single element, and a quoted flag value like `feature="add OAuth"` stays attached to its flag). Never flatten argv into a string and re-split it: iterate `"$@"` directly. +This minimal loop below sets `GLOBAL` only — it is enough for `status`/`list`/`save`, which do not forward flags. `new`/`go` need the fuller argv walker in [Pass-through flags](#pass-through-flags-branch-and-any-other-unrecognized-flag) below, which sets `GLOBAL` **and** buckets `PASSTHROUGH_FLAGS`/`POSITIONALS`. The two must agree on how `--global` is detected: **if you change the `--global`/`-g` handling in one, change it in the other.** + ```bash GLOBAL=false for a in "$@"; do @@ -35,6 +37,8 @@ While stripping the flow-control flags, collect all remaining flags (any element **Iterate the preserved argv, never a re-split string.** The block below walks `"$@"` — the array the dispatcher forwarded — so each element stays intact: a multi-word step command (`/spec new {{objective}}`) remains one `POSITIONALS` entry, and a flag value carried as one shell word (`feature="add OAuth"`) is not shredded. Do **not** write `set -- $ARGS` (or any unquoted re-split): that word-splits every step string and quoted value on whitespace and corrupts both `POSITIONALS` and `PASSTHROUGH_FLAGS`. +This walker is the **superset** of the minimal `--global`-only loop under [The `--global` flag](#the-global-flag): it detects `--global`/`-g` identically and additionally buckets pass-through flags. `new`/`go` use this one; `status`/`list`/`save` use the minimal loop. Keep the `--global`/`-g` case in the two in sync. + ```bash # Walk argv "$@" left→right; peel flow-control flags, bucket the rest into PASSTHROUGH_FLAGS, # leave true positionals (flow name, steps, input) in POSITIONALS. Each "$@" element is @@ -59,7 +63,7 @@ done - **`--set` handling is verb-specific.** `go.md` parses `--set key=value` out of `PASSTHROUGH_FLAGS` into `PARAMS` and forwards the remainder. For `new`, `--set` is **not** a step flag and must **not** be baked into stored steps: `new.md` explicitly drops any `--set key=value` pair from `PASSTHROUGH_FLAGS` before writing step commands (see new.md Step 2). For `status`/`list`, `--set` is inert and simply ignored. - **Caveat — flag values starting with `-`.** The `[ "${2#-}" = "$2" ]` test treats any following element that starts with `-` as *not* this flag's value, so a legitimate value beginning with `-` (e.g. `--message "-n"`) is read as a value-less flag and the `-n` falls through to `POSITIONALS`. This is acceptable for the `--branch feature/x`-style forwarding flow actually uses; **flag values beginning with `-` are not supported** — pass them in `--flag=value` form (kept whole) if ever needed. -- `PASSTHROUGH_FLAGS` is what `new` bakes into stored step commands (minus `--set`) and what `go` appends to each resolved step command at run time (see those workflows). +- `PASSTHROUGH_FLAGS` is what `new` bakes into stored step commands (minus `--set`) and what `go` merges into each resolved step command at run time (see those workflows). At `go` time, run-time flags **override** any same-named flag baked into the step — the explicit run-time value wins, and a flag never appears twice (see go.md Step 2). ## Resolving a flow by name (for `go`, `status`, `save`) diff --git a/skills/flow/references/workflows/go.md b/skills/flow/references/workflows/go.md index 0f2a04b..db394fb 100644 --- a/skills/flow/references/workflows/go.md +++ b/skills/flow/references/workflows/go.md @@ -45,7 +45,12 @@ Initialize the **flow context** accumulator `CONTEXT`. **On resume:** if `FLOW_D For each pending step in order: -1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`, then append this run's `PASSTHROUGH_FLAGS` (e.g. `--branch feature/x`) to the command string. **Dedup by flag name:** for each flag in `PASSTHROUGH_FLAGS`, skip appending it if its flag name already appears as a token in the step command string (steps that baked the flag in at `new` time already carry it). Concretely, for a flag token `--branch` (the part before any `=`), append it (and its value) only when a whole-word match for `--branch` is absent from the command — this makes the "don't duplicate" rule deterministic rather than best-effort. Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` +1. **Substitute parameters** in the step command: replace every `{{name}}` with `PARAMS[name]`, then merge this run's `PASSTHROUGH_FLAGS` (e.g. `--branch feature/x`) into the command string. **Run-time flags override baked ones (deterministic merge):** a step may already carry a flag baked in at `new` time (e.g. `/spec new {{objective}} --branch feature/x`). For each flag in `PASSTHROUGH_FLAGS`, key on its flag name (the token before any `=`, e.g. `--branch`): + - If that flag name already appears as a whole-word token in the step command **and** the run-time value differs, **replace** the baked flag (and its value) with the run-time one — the explicit run-time value wins. Report the override: `ℹ Step {N}: --branch overridden by run-time value (baked '{old}' → '{new}')`. + - If it appears with the **same** value, leave the single baked copy (no duplicate). + - If it does not appear, append the run-time flag (and its value). + + This makes precedence deterministic: run-time `PASSTHROUGH_FLAGS` always take effect, and a flag never appears twice. Call the result `RESOLVED_COMMAND`. Report: `▶ Step {N}: {RESOLVED_COMMAND}` 2. Read `FLOW_DIR/implementation/step-N.md` to get the agent prompt. Prepare the prompt for this run by filling its injection points: - Replace `{step-command}` occurrences with `RESOLVED_COMMAND` (substitute `{{placeholders}}` here too).