Skip to content

Commit a9f2fd4

Browse files
feat(.claude): feature-design skill for cross-repo features (DSPX-3302)
For features (or bugs) that touch more than one OpenTDF repo — platform plus the Go / Java / JS SDKs — feature-design captures the work as a single spec at xtest/features/<name>.yaml plus the tests-side artifacts that land first (feature_type entry in tdfs.py, scenario, draft test). The model matches the team's existing pattern: tests-side artifacts merge first, dormant under a `supports("<feature>")` gate, and each per-repo PR activates the gate by adding `supports <feature>` to its cli.sh. PRs land async, in any order; no cross-PR lockstep needed. - `feature-design` SKILL: propose-then-iterate authoring from a Jira ticket (or free-form description). Drafts a complete spec on the first pass, asks one composite redirect question, then writes the spec + patches tdfs.py + invokes scenario-from-ticket internally to produce the dormant scenario and draft test. Bails on Spike or unclear tickets rather than fabricating. - `xtest/features/{README,CLAUDE}.md`: progressive-disclosure docs — human-facing README and agent-facing CLAUDE.md. - `xtest/README.md` gains a brief "Test artifact directories" section pointing at scenarios/ and features/. - `settings.json` + `plugin.json`: Write(xtest/features/**) allowlist, feature-design added to plugin skills array. The complementary feature-orchestrate skill (fanning out per-repo subagents to draft impl PRs in each touched repo) is a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2354be8 commit a9f2fd4

6 files changed

Lines changed: 155 additions & 0 deletions

File tree

.claude/plugin/plugin.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "Jira-ticket-driven scenarios for the OpenTDF test harness. Pulls ticket context from Jira (acli) — any ticket type, including bugs, feature stories, and PR-driven work — provisions pinned platform/KAS/SDK versions or refs (released versions, main, feature branches, PR SHAs), runs the xtest pytest suite, and tears down. Useful for QA, platform/SDK developers writing tests for new features first, and downstream first/third-party integrators.",
55
"skills_dir": "../skills",
66
"skills": [
7+
"feature-design",
78
"scenario-from-ticket",
89
"scenario-matrix",
910
"scenario-up",
@@ -32,6 +33,7 @@
3233
"Bash(acli jira workitem link list *)",
3334
"Bash(acli jira project view *)",
3435
"Write(xtest/scenarios/**)",
36+
"Write(xtest/features/**)",
3537
"Write(xtest/bug_*_test.py)",
3638
"Write(tests/instances/**)"
3739
]

.claude/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"Bash(acli jira board view *)",
2525
"Bash(acli jira sprint view *)",
2626
"Write(xtest/scenarios/**)",
27+
"Write(xtest/features/**)",
2728
"Write(xtest/bug_*_test.py)",
2829
"Write(tests/instances/**)",
2930
"Write(.claude/tmp/**)"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
name: feature-design
3+
description: Turn a multi-repo feature (or cross-repo bug fix) into a concrete spec at xtest/features/<name>.yaml plus the tests-side artifacts that have to land first (scenario, draft pytest, feature_type entry in tdfs.py). Pulls Jira context, drafts a complete spec from the ticket, then iterates with the user. Use when a feature touches more than one repo (e.g. platform + Go SDK + Java SDK + JS SDK) and you want to set up the cross-repo work in one go without manually authoring each piece.
4+
allowed-tools: Bash, Read, Write, Edit, Grep, Glob, Skill
5+
---
6+
7+
# feature-design
8+
9+
You turn a fuzzy "let's build X across the OpenTDF repos" into a concrete bundle of artifacts that pin down the tests-side work first and stage the cross-repo work for handoff to `feature-orchestrate`.
10+
11+
Two ideas to internalize before reading the steps:
12+
13+
1. **Tests-side artifacts land first, dormant.** The scenario + draft test + `feature_type` entry merge to `tests/main` as a regular PR. They stay "all skipped" until each SDK opens its own PR adding a `supports <feature>` case to its `cli.sh` source — that PR's CI activates the test for that SDK. This means no cross-PR lockstep coordination; per-repo PRs land async, in any order.
14+
2. **Propose, don't ask.** Draft a complete spec from the Jira ticket on the first pass and let the user redirect what's wrong in a single revision. Only ask one composite question. If you're missing information you can't fill in (no Jira ticket, ambiguous scope, unclear feature name), bail — don't fabricate.
15+
16+
## Inputs
17+
18+
- Jira key (Story/Task usually; Bug works the same way), OR a free-text description of the feature.
19+
- (Optional) explicit list of repos to scope to, if the user wants something tighter than the default.
20+
21+
## Steps
22+
23+
### Step 1 — Pull the Jira context
24+
25+
If a Jira key was given, run both — comments often carry scope refinements that aren't in the description:
26+
27+
```bash
28+
acli jira workitem view <JIRA-KEY> --fields '*all' --json
29+
acli jira workitem comment list <JIRA-KEY>
30+
```
31+
32+
Extract Issue Type, summary, description, status, and any comments about scope or implementation notes. If no Jira key, the user's description IS the spec input.
33+
34+
### Step 2 — Propose a complete draft
35+
36+
Draft the full spec body and the per-repo todo lists inline in your reply. Don't ask the user one field at a time — produce a complete first draft they can react to:
37+
38+
- **Feature flag name** — snake_case identifier derived from the Jira summary. Becomes the `supports("<name>")` gate string AND the `feature_type` entry in `xtest/tdfs.py`. Validate it's a valid Python identifier and doesn't collide with an existing `feature_type` member.
39+
- **Touched repos** — default set is `tests, platform, sdk-go, sdk-java, sdk-web`. Trim or expand based on what the ticket says. Pure platform features skip the SDK repos; pure SDK-only features skip platform; `tests` is always present (the dormant scenario + tdfs.py entry has to live there).
40+
- **Per-repo todo lists** — 2-4 bullets per repo, derived from the description plus each repo's known role:
41+
- `tests` — register the feature in `feature_type`, author the scenario, draft the test gated on `supports("<feature>")`.
42+
- `platform` — service-side implementation (KAS path, policy plumbing, etc.) and any env-var handling in the dev harness (e.g. honoring `XT_WITH_<FEATURE>`).
43+
- `sdk-go` / `sdk-java` / `sdk-web` — encrypt/decrypt path implementation, plus a `supports <feature>` case in that SDK's `cli.sh` source. **Don't pin the version bound in the spec** — the implementing engineer sets the `awk` predicate at PR time, since the bound depends on which release will ship the impl.
44+
- **Branch name**`<JIRA-KEY>-<feature-slug>`, the same string across every touched repo so `feature-orchestrate` (and the user) can find each repo's PR by branch alone.
45+
46+
Present the draft, then ask exactly one composite question: "Anything to redirect — feature name, touched repos, todo items, branch?" Apply edits in a single revision rather than turn-by-turn. The user can always drop into plain chat if they want to think out loud — just answer them and re-invoke this skill once the design firms up.
47+
48+
If no Jira key was given AND the user's description doesn't pin down a clear scope (feature flag name, touched repos, intended behavior), bail rather than fabricate:
49+
50+
```
51+
I need either (a) a Jira Story/Task/Bug key, or (b) a description that names
52+
the feature flag, the repos it touches, and the intended behavior. Add either
53+
and re-invoke this skill.
54+
```
55+
56+
### Step 3 — Write the spec
57+
58+
Write `xtest/features/<feature-name>.yaml`. Shape (still informal — no Pydantic model yet):
59+
60+
```yaml
61+
apiVersion: opentdf.io/v1alpha1
62+
kind: Feature
63+
metadata:
64+
name: <feature-name> # supports() string + feature_type entry, snake_case
65+
jira: <JIRA-KEY> # omit if no ticket
66+
title: "<one-line title>"
67+
created: <YYYY-MM-DD>
68+
repos:
69+
tests:
70+
branch: <JIRA-KEY>-<feature-slug>
71+
todo:
72+
- Register "<feature-name>" in xtest/tdfs.py feature_type
73+
- Author scenario + draft test (via scenario-from-ticket)
74+
platform:
75+
branch: <JIRA-KEY>-<feature-slug>
76+
todo: [ ... ]
77+
sdk-go:
78+
branch: <JIRA-KEY>-<feature-slug>
79+
todo:
80+
- Implement <feature> in the encrypt/decrypt path
81+
- Add `supports <feature>` case to cli.sh with version-bound awk predicate
82+
sdk-java: { branch: ..., todo: [ ... ] }
83+
sdk-web: { branch: ..., todo: [ ... ] }
84+
scenarios:
85+
- xtest/scenarios/<jira-key-lowercased>.yaml
86+
```
87+
88+
PR status (open/merged/CI passing) deliberately is NOT in the spec — it's auto-discovered from `gh pr list --search "head:<branch>"` per repo whenever something asks "where are we?" The spec is a declaration of intent.
89+
90+
### Step 4 — Drive the tests-side artifacts
91+
92+
In this order, so each step's output feeds the next:
93+
94+
1. **Add the feature flag to `xtest/tdfs.py`**. Find the `feature_type` Literal alias near the top of the file. Insert the new entry alphabetically. Don't touch any `cli.sh` files — `supports <feature>` cases land per-SDK in their own PRs.
95+
96+
2. **Invoke `scenario-from-ticket`** via the Skill tool (`skill: scenario-from-ticket`, `args: <JIRA-KEY>`). It runs its Story/Task branch and produces the scenario + draft test gated on `supports("<feature>")` — pinning the feature-introducing components to `main` via `source.ref:`. If no Jira key was given, draft the scenario directly using the same shape (`xtest/scenarios/<feature-name>.yaml`).
97+
98+
3. **Validate the scenario**:
99+
100+
```bash
101+
uv run python -m otdf_sdk_mgr.schema validate xtest/scenarios/<jira-key>.yaml
102+
```
103+
104+
### Step 5 — Report
105+
106+
One block summarizing:
107+
108+
- The spec path (`xtest/features/<feature-name>.yaml`).
109+
- The scenario + draft test paths.
110+
- The line(s) added to `xtest/tdfs.py`.
111+
- A one-liner suggesting the next step: `feature-orchestrate xtest/features/<feature-name>.yaml`.
112+
113+
## Notes
114+
115+
- This skill produces **tests-side artifacts only**. It does NOT create branches in other repos, does NOT open PRs, does NOT install platform/SDK builds. That's `feature-orchestrate`'s job.
116+
- Bugs that span repos use the same shape — pass the Bug ticket key and `scenario-from-ticket`'s Bug branch fills `expected:` / `actual:` from the reproduction prose. The cross-repo gating still works: tests land dormant, each per-repo PR activates them by adding the supports case as part of the fix.
117+
- For an existing spec being revised, read it first and propose a diff rather than a full rewrite. The tests-side artifacts (scenario, tdfs.py entry) usually shouldn't be regenerated — just edit them surgically.
118+
- If the user starts the conversation by describing the feature in plain chat rather than invoking this skill, answer normally — re-invoke the skill once the scope firms up. Don't gatekeep.

xtest/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,10 @@ pytest
122122
rm -rf tmp
123123
pytest test_tdfs.py
124124
```
125+
126+
## Test artifact directories
127+
128+
- **`scenarios/`** — Per-ticket scenario YAMLs that pin a platform / KAS / SDK topology to a specific pytest selection. Consumed by `otdf-local scenario run`.
129+
- **`features/`** — Multi-repo feature specs: features that touch more than one OpenTDF repo (platform + SDKs) authored as a single declaration of intent. See `features/README.md`.
130+
131+
Both are produced by the Claude Code skills under `tests/.claude/skills/` (`scenario-from-ticket`, `feature-design`, etc.) and can also be hand-authored.

xtest/features/CLAUDE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Agent guidance for xtest/features
2+
3+
This directory is owned by two skills:
4+
5+
- **`feature-design`** drafts new spec files here from a Jira ticket (or free-form description) using propose-then-iterate authoring. It also writes the tests-side artifacts that have to land first: the `feature_type` entry in `xtest/tdfs.py`, the scenario under `xtest/scenarios/`, and (if needed) a draft pytest.
6+
- **`feature-orchestrate`** reads spec files and fans out per-repo subagents that implement the feature in each touched repo and open draft PRs.
7+
8+
When you see a `xtest/features/<name>.yaml` referenced:
9+
10+
- It is canonical for the feature's flag name, scope, and per-repo todos.
11+
- It is NOT canonical for status — query `gh pr list --search "head:<branch>"` per repo.
12+
13+
Don't hand-author spec files in this directory unless you've also done what `feature-design` would do (add the entry to `feature_type` in `xtest/tdfs.py`, generate the scenario + draft test). Those side effects keep the spec consistent with the tests it depends on.

xtest/features/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# xtest/features
2+
3+
Specs for features that touch more than one OpenTDF repo (e.g. platform + Go SDK + Java SDK + JS SDK).
4+
5+
Each `<feature-name>.yaml` captures:
6+
7+
- The feature flag name — the `supports("<name>")` gate string in `xtest/tdfs.py`.
8+
- The Jira ticket driving the work, if any.
9+
- Per-repo todo lists and the shared branch name to use across them.
10+
- The scenario(s) under `xtest/scenarios/` that exercise the feature once each repo's PR lands.
11+
12+
Specs are declarative — they describe intent, not status. PR state (open / merged / CI passing) is auto-discovered from `gh pr list --search "head:<branch>"` per repo, not stored here.
13+
14+
See `CLAUDE.md` in this directory for how Claude Code skills produce and consume these files.

0 commit comments

Comments
 (0)