Add @command behave tags to all feature files#1141
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 32 minutes and 26 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
WalkthroughReplaces single-mode scenario inclusion with a builder-aware system: HTML renders scenarios inline (with optional Changes
Sequence Diagram(s)sequenceDiagram
participant Sphinx as Sphinx App
participant Directive as scenario-include/<br/>scenario-appendix
participant Doctree as Doctree
participant Resolver as doctree-resolved<br/>Handler
participant Env as Sphinx Env
participant Renderer as HTML/PDF Renderer
Directive->>Env: record scenario metadata (PDF mode)
Directive->>Doctree: insert inline nodes (HTML mode) or placeholder node (PDF mode)
Sphinx->>Resolver: emit doctree-resolved
Resolver->>Doctree: find & replace placeholders
Resolver->>Env: retrieve collected scenarios grouped by tag
Resolver->>Doctree: inject appendix sections (PDF mode)
Doctree->>Renderer: final doctree for output generation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@doc/_ext/scenario_directive.py`:
- Around line 179-196: The directive currently hardcodes "Scenario:" when
building the literalinclude bounds; update the parsing code in _all_scenarios()
to preserve the original header token (e.g., header = "Scenario" or "Scenario
Outline") alongside title and use that header when composing the start-after and
end-before lines in directive_rst (e.g., f":start-after: {header}: {title}" and
f":end-before: {header}: {title}" when applicable), ensuring end_before remains
empty for the last item as before.
- Around line 235-249: The appendix entry currently stores a single "source_doc"
and is removed when that one doc is reread, dropping references from other
pages; change the entry shape to track a set of referencing docs (e.g. replace
"source_doc" with "source_docs" as a set when creating the entry in
env.scenario_appendix_entries for feature_abs), update merging logic to add
env.docname into existing["source_docs"] when appending scenarios (alongside
existing["scenarios"].append), and update the purge/remove logic (the code that
deletes entries on document re-read) to remove the current env.docname from
existing["source_docs"] and only delete the whole entry if that set is empty.
Ensure all places that read/write "source_doc" are updated to use the new
"source_docs" set.
- Around line 349-352: The PDF/LaTeX appendix currently always emits the full
feature via _full_feature_content(entry["feature_abs"]) into a
nodes.literal_block (assigned to code and appended to feat_section), which
ignores the :scenario: filter; change the PDF path to either (A) build the
literal_block from only entry["scenarios"] (i.e., render and join only the
selected scenarios instead of calling _full_feature_content) when the builder is
LaTeX/PDF, or (B) detect the LaTeX/PDF builder (via app.builder.name or
buildername) and raise/emit a clear error/warning rejecting the :scenario:
option for PDF builds; update the code that constructs code =
nodes.literal_block(...) and the content source to use the chosen approach so
HTML and PDF outputs remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: f9f4a274-e2f9-42ba-bbf5-f63a6c85d8d2
📒 Files selected for processing (55)
doc/_ext/scenario_directive.pydoc/appendix/scenarios.rstdoc/conf.pydoc/index.rstfeatures/add-project-through-cli.featurefeatures/check-archive.featurefeatures/check-git-repo.featurefeatures/check-report-code-climate.featurefeatures/check-report-jenkins.featurefeatures/check-report-sarif.featurefeatures/check-specific-projects.featurefeatures/check-svn-repo.featurefeatures/checked-project-has-dependencies.featurefeatures/diff-in-git.featurefeatures/diff-in-svn.featurefeatures/fetch-archive.featurefeatures/fetch-checks-destination.featurefeatures/fetch-file-pattern-git.featurefeatures/fetch-file-pattern-svn.featurefeatures/fetch-git-repo-with-submodule.featurefeatures/fetch-git-repo.featurefeatures/fetch-single-file-git.featurefeatures/fetch-single-file-svn.featurefeatures/fetch-specific-project.featurefeatures/fetch-svn-repo.featurefeatures/fetch-with-ignore-git.featurefeatures/fetch-with-ignore-svn.featurefeatures/format-patch-in-git.featurefeatures/format-patch-in-svn.featurefeatures/freeze-archive.featurefeatures/freeze-inplace.featurefeatures/freeze-projects.featurefeatures/freeze-specific-projects.featurefeatures/guard-against-overwriting-git.featurefeatures/guard-against-overwriting-svn.featurefeatures/handle-invalid-metadata.featurefeatures/import-from-git.featurefeatures/import-from-svn.featurefeatures/interactive-add.featurefeatures/journey-basic-patching.featurefeatures/journey-basic-usage.featurefeatures/keep-license-in-project.featurefeatures/list-projects.featurefeatures/patch-after-fetch-git.featurefeatures/patch-after-fetch-svn.featurefeatures/patch-fuzzy-matching-git.featurefeatures/remove-project.featurefeatures/report-sbom-archive.featurefeatures/report-sbom-license.featurefeatures/report-sbom.featurefeatures/suggest-project-name.featurefeatures/update-patch-in-git.featurefeatures/update-patch-in-svn.featurefeatures/updated-project-has-dependencies.featurefeatures/validate-manifest.feature
3abcbbd to
ea1a3ee
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
ea1a3ee to
b32e6eb
Compare
Each feature file now carries a tag identifying the dfetch command it exercises (e.g. @update, @check, @diff, @add, @remove, @report, @Freeze, @import, @Validate, @format-patch, @update-patch). Journey features that exercise multiple commands receive all relevant tags (e.g. @update @check for journey-basic-usage.feature). Files that already had @remote-svn retain it and gain the new command tag on the same line (e.g. @remote-svn @update). This makes it possible to run only the scenarios for a specific command, for example: behave features/ --tags=update https://claude.ai/code/session_01BvyikyxX9c3sDXev8HdP4f
The scenario-include directive now has two modes: HTML (unchanged): Scenarios are rendered inline as collapsible <details> blocks, exactly as before. PDF / LaTeX: Scenarios are moved to an auto-generated appendix page grouped by dfetch command (update, check, add, …). In the place of the directive the text shows a cross-reference: "Scenarios: see <Feature title> in the appendix." The command grouping is derived from the @command behave tag added to each feature file in the previous commit. New directive option :inline: keeps a scenario in its original position even in PDF mode, for cases where the example is essential reading in context: .. scenario-include:: ../features/journey-basic-usage.feature :inline: New directive scenario-appendix renders the collected scenarios in the PDF appendix; in HTML it emits an orientation note instead. doc/appendix/scenarios.rst added as the appendix landing page; it is wired into the index.rst toctree under a new "Appendix" caption. https://claude.ai/code/session_01BvyikyxX9c3sDXev8HdP4f
Three changes:
1. Remove hardcoded _NON_COMMAND_TAGS, _TAG_LABELS, _TAG_ORDER constants.
The directive no longer knows anything about dfetch commands.
2. Sort appendix sections alphabetically by tag. The previous fixed
ordering (update, check, add, …) was dfetch-specific.
3. Expose scenario_non_command_tags as a conf.py config value (default []).
Any tag in that list is skipped when choosing the group tag for a
feature file. dfetch's conf.py now sets:
scenario_non_command_tags = ["remote-svn"]
Section titles are derived from the tag itself (title-cased, hyphens
replaced by spaces), so no label map is needed.
https://claude.ai/code/session_01BvyikyxX9c3sDXev8HdP4f
b32e6eb to
812915d
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@doc/_ext/scenario_directive.py`:
- Around line 306-313: After computing available = _all_scenarios(feature_abs)
and scenario_titles = self._requested_scenarios(available) in run(), validate
the requested titles before calling _render_pdf() and raise a directive error if
any requested title is missing or if the user specified :scenario: but no
matching scenarios were found; specifically, compare scenario_titles against
available (or check for requested vs matched counts) and call self.error(...)
with a clear message when there's a mismatch so the PDF path (_render_pdf) never
receives silently-omitted scenario requests.
- Around line 22-35: Add a user-facing documentation page that explains how to
use the scenario-include and scenario-appendix directives, documents the
scenario_non_command_tags configuration and the :inline: flag, and clearly
describes the PDF/LaTeX appendix behavior and grouping rules; include a short
example showing a scenario-include with and without :inline:, an example
scenario-appendix placement, and a note about how non-excluded behave tags
affect appendix grouping so readers can reproduce the behavior and configure it.
- Around line 257-299: The generated appendix IDs can collide because label =
f"appendix-{basename}" and group_label = f"appendix-{tag}" share a namespace and
basename alone ignores path; fix by introducing and using a collision-resistant
helper (e.g., _appendix_id(value, *, kind)) that slug-normalizes and namespaces
values (include file path or feature_abs for feature IDs, and prefix/group for
group IDs) and replace usages: set label = _appendix_id(feature_abs,
kind="feature"), group_label = _appendix_id(tag, kind="group"), and store the
same stable IDs in env.scenario_appendix_entries and in the ScenarioAppendixRef
node (ensure ScenarioAppendixRef["label"], ["group_label"] and keys in
env.scenario_appendix_entries use the new helper-generated IDs).
- Around line 421-458: When appendix_docname is None we should warn so authors
know examples will be omitted: add "from sphinx.util import logging" and create
a module logger (e.g. logger = logging.getLogger(__name__)), then inside the
loop where appendix_docname is checked (the block that currently does the else:
para += nodes.Text(f"See …")), call logger.warning with a clear message about
the missing ".. scenario-appendix::" directive and its effect (e.g. "PDF build
will omit deferred examples because scenario_appendix_docname is not set; add ..
scenario-appendix:: to include them"), passing location=ref_node to attach the
warning to the source location of the ScenarioAppendixRef node. Ensure the
warning runs only when appendix_docname is falsy so behavior for the existing
branch is unchanged.
In `@features/fetch-git-repo-with-submodule.feature`:
- Line 1: The feature file features/fetch-git-repo-with-submodule.feature is
currently tagged only with `@update` but also exercises dfetch report; add the
missing `@report` tag alongside `@update` at the top of the feature so command-based
test filtering includes both commands (i.e., change the feature tag line to
include both `@update` and `@report`); ensure the tag text matches the existing
tagging style used in other features.
In `@features/guard-against-overwriting-svn.feature`:
- Line 1: The feature file guard-against-overwriting-svn.feature only has the
`@update` tag but should follow the established pattern of using `@remote-svn`
alongside command tags; edit the file header to add the `@remote-svn` tag so the
top line reads both `@remote-svn` and `@update`, mirroring other SVN features like
fetch-svn-repo.feature and check-svn-repo.feature (i.e., include the `@remote-svn`
tag next to `@update`).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 43210ba6-65b1-4d6f-ba4e-099bfd7e98e0
📒 Files selected for processing (57)
AGENTS.mddoc/_ext/scenario_directive.pydoc/appendix/scenarios.rstdoc/conf.pydoc/howto/contributing.rstdoc/index.rstfeatures/add-project-through-cli.featurefeatures/check-archive.featurefeatures/check-git-repo.featurefeatures/check-report-code-climate.featurefeatures/check-report-jenkins.featurefeatures/check-report-sarif.featurefeatures/check-specific-projects.featurefeatures/check-svn-repo.featurefeatures/checked-project-has-dependencies.featurefeatures/diff-in-git.featurefeatures/diff-in-svn.featurefeatures/fetch-archive.featurefeatures/fetch-checks-destination.featurefeatures/fetch-file-pattern-git.featurefeatures/fetch-file-pattern-svn.featurefeatures/fetch-git-repo-with-submodule.featurefeatures/fetch-git-repo.featurefeatures/fetch-single-file-git.featurefeatures/fetch-single-file-svn.featurefeatures/fetch-specific-project.featurefeatures/fetch-svn-repo.featurefeatures/fetch-with-ignore-git.featurefeatures/fetch-with-ignore-svn.featurefeatures/format-patch-in-git.featurefeatures/format-patch-in-svn.featurefeatures/freeze-archive.featurefeatures/freeze-inplace.featurefeatures/freeze-projects.featurefeatures/freeze-specific-projects.featurefeatures/guard-against-overwriting-git.featurefeatures/guard-against-overwriting-svn.featurefeatures/handle-invalid-metadata.featurefeatures/import-from-git.featurefeatures/import-from-svn.featurefeatures/interactive-add.featurefeatures/journey-basic-patching.featurefeatures/journey-basic-usage.featurefeatures/keep-license-in-project.featurefeatures/list-projects.featurefeatures/patch-after-fetch-git.featurefeatures/patch-after-fetch-svn.featurefeatures/patch-fuzzy-matching-git.featurefeatures/remove-project.featurefeatures/report-sbom-archive.featurefeatures/report-sbom-license.featurefeatures/report-sbom.featurefeatures/suggest-project-name.featurefeatures/update-patch-in-git.featurefeatures/update-patch-in-svn.featurefeatures/updated-project-has-dependencies.featurefeatures/validate-manifest.feature
| **PDF / LaTeX builds** automatically move examples to an appendix grouped by | ||
| the first non-excluded behave tag found on the feature file. The directive's | ||
| original location receives a cross-reference to the appendix entry instead. | ||
|
|
||
| Use the ``:inline:`` flag to keep a specific inclusion in place even when | ||
| building a PDF: | ||
|
|
||
| .. scenario-include:: ../features/fetch-git-repo.feature | ||
| :inline: | ||
|
|
||
| The appendix itself is rendered by placing the companion directive anywhere in | ||
| the document tree: | ||
|
|
||
| .. scenario-appendix:: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files doc/ | head -30Repository: dfetch-org/dfetch
Length of output: 1100
🏁 Script executed:
cat -n doc/_ext/scenario_directive.py | head -100Repository: dfetch-org/dfetch
Length of output: 4100
🏁 Script executed:
find doc -type f -name "*.rst" -o -name "*.md" | grep -E "(how-to|reference)" | head -20Repository: dfetch-org/dfetch
Length of output: 226
🏁 Script executed:
rg -l "scenario-include|scenario-appendix|:inline:" doc/Repository: dfetch-org/dfetch
Length of output: 304
🏁 Script executed:
cat -n doc/appendix/scenarios.rst | head -50Repository: dfetch-org/dfetch
Length of output: 681
🏁 Script executed:
cat -n doc/_ext/scenario_directive.py | sed -n '510,530p'Repository: dfetch-org/dfetch
Length of output: 920
🏁 Script executed:
rg "scenario-include|scenario-appendix" doc/appendix/scenarios.rst -B 2 -A 2Repository: dfetch-org/dfetch
Length of output: 191
🏁 Script executed:
find doc/howto -type f -exec grep -l "scenario" {} \;Repository: dfetch-org/dfetch
Length of output: 239
🏁 Script executed:
find doc -type f \( -name "*.rst" -o -name "*.md" \) | grep -E "(howto|reference)" | xargs grep -l "directive" 2>/dev/null | head -10Repository: dfetch-org/dfetch
Length of output: 43
Add user-facing documentation for the scenario directives and PDF behavior.
This adds public directives (scenario-include, scenario-appendix), configuration (scenario_non_command_tags), and features (:inline: flag, PDF appendix grouping). While the module docstring documents these well, the repo requires user-visible changes in doc/howto/ or doc/reference/. Create a dedicated guide explaining how to use the scenario-include directive, the :inline: flag, and how PDF/LaTeX builds automatically organize examples via the appendix.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@doc/_ext/scenario_directive.py` around lines 22 - 35, Add a user-facing
documentation page that explains how to use the scenario-include and
scenario-appendix directives, documents the scenario_non_command_tags
configuration and the :inline: flag, and clearly describes the PDF/LaTeX
appendix behavior and grouping rules; include a short example showing a
scenario-include with and without :inline:, an example scenario-appendix
placement, and a note about how non-excluded behave tags affect appendix
grouping so readers can reproduce the behavior and configure it.
| basename = os.path.splitext(os.path.basename(feature_abs))[0] | ||
| label = f"appendix-{basename}" | ||
| tag = _group_tag(feature_abs, non_group_tags) | ||
| title = _feature_title(feature_abs) | ||
|
|
||
| # ---------------------------------------------------------- | ||
| # Store entry so the appendix directive can render it later. | ||
| # The dict is keyed by absolute path to deduplicate across | ||
| # multiple RST files that reference the same feature. | ||
| # ---------------------------------------------------------- | ||
| if not hasattr(env, "scenario_appendix_entries"): | ||
| env.scenario_appendix_entries = {} | ||
|
|
||
| if feature_abs not in env.scenario_appendix_entries: | ||
| env.scenario_appendix_entries[feature_abs] = { | ||
| "feature_file": feature_file, | ||
| "feature_abs": feature_abs, | ||
| "feature_title": title, | ||
| "group_tag": tag, | ||
| "label": label, | ||
| "source_docs": {env.docname}, | ||
| "scenarios": list(scenario_titles), | ||
| } | ||
| else: | ||
| existing = env.scenario_appendix_entries[feature_abs] | ||
| existing["source_docs"].add(env.docname) | ||
| for s in scenario_titles: | ||
| if s not in existing["scenarios"]: | ||
| existing["scenarios"].append(s) | ||
|
|
||
| # ---------------------------------------------------------- | ||
| # Return a deferred ScenarioAppendixRef block node. It is | ||
| # resolved to a full paragraph with make_refnode links during | ||
| # doctree-resolved, once the appendix document name is known. | ||
| # Sphinx's make_refnode handles LaTeX/HTML builder differences, | ||
| # producing a proper \hyperref in PDF output. | ||
| # ---------------------------------------------------------- | ||
| ref_node = ScenarioAppendixRef() | ||
| ref_node["label"] = label | ||
| ref_node["reftitle"] = title | ||
| ref_node["group_tag"] = tag | ||
| ref_node["group_label"] = f"appendix-{tag}" | ||
| ref_node["scenario_count"] = len(scenario_titles) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find current feature/tag pairs that would generate duplicate appendix IDs.
python - <<'PY'
from collections import defaultdict
from pathlib import Path
non_group_tags = {"remote-svn"}
ids = defaultdict(list)
for path in Path(".").rglob("*.feature"):
tags = []
try:
lines = path.read_text(encoding="utf-8").splitlines()
except UnicodeDecodeError:
continue
for line in lines:
stripped = line.strip()
if stripped.startswith("Feature:"):
break
if stripped.startswith("@"):
tags.extend(token.lstrip("@") for token in stripped.split())
group = next((tag for tag in tags if tag not in non_group_tags), "other")
feature_id = f"appendix-{path.stem}"
group_id = f"appendix-{group}"
ids[feature_id].append(f"feature {path}")
ids[group_id].append(f"group {group} from {path}")
for target_id, owners in sorted(ids.items()):
if len(owners) > 1:
print(target_id)
for owner in owners:
print(f" - {owner}")
PYRepository: dfetch-org/dfetch
Length of output: 3136
🏁 Script executed:
# First, read the actual code at the mentioned lines
head -n 310 doc/_ext/scenario_directive.py | tail -n 55Repository: dfetch-org/dfetch
Length of output: 2676
🏁 Script executed:
# Also check the other location mentioned (lines 378-388)
sed -n '370,395p' doc/_ext/scenario_directive.pyRepository: dfetch-org/dfetch
Length of output: 1095
🏁 Script executed:
# Check if any .feature file has a tag matching its own basename
python - <<'PY'
from pathlib import Path
non_group_tags = {"remote-svn"}
for path in sorted(Path(".").rglob("*.feature")):
basename = path.stem
tags = []
try:
lines = path.read_text(encoding="utf-8").splitlines()
except UnicodeDecodeError:
continue
for line in lines:
stripped = line.strip()
if stripped.startswith("Feature:"):
break
if stripped.startswith("@"):
tags.extend(token.lstrip("@") for token in stripped.split())
# Check for basename matching a tag
for tag in tags:
if tag == basename or tag.startswith(basename + "-") or basename.endswith("-" + tag):
print(f"Match found: {path.stem} has tag {tag}")
print(f" file: {path}")
print(f" tags: {tags}")
break
PYRepository: dfetch-org/dfetch
Length of output: 158
🏁 Script executed:
# Check for duplicate basenames across different directories
python - <<'PY'
from collections import defaultdict
from pathlib import Path
basenames = defaultdict(list)
for path in sorted(Path(".").rglob("*.feature")):
basename = path.stem
basenames[basename].append(str(path))
for basename in sorted(basenames.keys()):
paths = basenames[basename]
if len(paths) > 1:
print(f"Duplicate basename '{basename}':")
for p in paths:
print(f" - {p}")
PYRepository: dfetch-org/dfetch
Length of output: 43
Use collision-safe IDs for group and feature appendix targets.
The current ID generation can produce collisions: label = f"appendix-{basename}" (line 264) and group_label = f"appendix-{tag}" (line 296) share the same namespace. A feature file with basename matching its group tag (e.g., update.feature with @update tag) would generate duplicate section IDs. Additionally, if the same feature basename appears in different directories, they'd share a single ID, causing ambiguous cross-references. The proposed fix using a collision-resistant _appendix_id() helper with slug normalization resolves this:
Proposed fix
+def _appendix_id(prefix: str, value: str) -> str:
+ """Return a stable docutils-safe appendix target id."""
+ slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
+ return f"scenario-appendix-{prefix}-{slug or 'item'}"
+
+
def _tag_section_title(tag: str) -> str:
"""Human-readable section title derived from *tag* alone."""
return tag.replace("-", " ").title()
@@
env = self._env()
non_group_tags = frozenset(getattr(env.config, "scenario_non_command_tags", []))
basename = os.path.splitext(os.path.basename(feature_abs))[0]
- label = f"appendix-{basename}"
tag = _group_tag(feature_abs, non_group_tags)
+ label = _appendix_id("feature", feature_file)
+ group_label = _appendix_id("group", tag)
title = _feature_title(feature_abs)
@@
ref_node["label"] = label
ref_node["reftitle"] = title
ref_node["group_tag"] = tag
- ref_node["group_label"] = f"appendix-{tag}"
+ ref_node["group_label"] = group_label
ref_node["scenario_count"] = len(scenario_titles)
return [ref_node]
@@
for tag in sorted(by_tag):
tag_entries = sorted(by_tag[tag], key=lambda e: e["feature_title"])
- label = f"appendix-{tag}"
+ label = _appendix_id("group", tag)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@doc/_ext/scenario_directive.py` around lines 257 - 299, The generated
appendix IDs can collide because label = f"appendix-{basename}" and group_label
= f"appendix-{tag}" share a namespace and basename alone ignores path; fix by
introducing and using a collision-resistant helper (e.g., _appendix_id(value, *,
kind)) that slug-normalizes and namespaces values (include file path or
feature_abs for feature IDs, and prefix/group for group IDs) and replace usages:
set label = _appendix_id(feature_abs, kind="feature"), group_label =
_appendix_id(tag, kind="group"), and store the same stable IDs in
env.scenario_appendix_entries and in the ScenarioAppendixRef node (ensure
ScenarioAppendixRef["label"], ["group_label"] and keys in
env.scenario_appendix_entries use the new helper-generated IDs).
Each feature file now carries a tag identifying the dfetch command it
exercises (e.g. @update, @check, @diff, @add, @remove, @report,
@Freeze, @import, @Validate, @format-patch, @update-patch).
Journey features that exercise multiple commands receive all relevant
tags (e.g. @update @check for journey-basic-usage.feature).
Files that already had @remote-svn retain it and gain the new command
tag on the same line (e.g. @remote-svn @update).
This makes it possible to run only the scenarios for a specific
command, for example:
https://claude.ai/code/session_01BvyikyxX9c3sDXev8HdP4f
Summary by CodeRabbit
Documentation
Chores