Skip to content

Commit 77e0d3e

Browse files
jpicklykclaude
andauthored
feat: session tracking, schema improvements, SDK upgrade, test infrastructure (#70)
* feat: add session retrospective skill and run manifest tracking Introduces structured post-implementation analysis — a /session-retrospective skill that evaluates schema effectiveness, delegation alignment, note quality, plan-to-execution fit, and friction across five dimensions. Includes run manifest instructions in the shared orchestrator output style for durable session telemetry, and a retrospective nudge after implementation completions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: improve session-retrospective skill robustness Address issues found during skill review: remove hardcoded absolute paths, add early exit for empty sessions, expand trigger description, simplify terminal advancement, and add recency filter to fallback mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: upgrade MCP Kotlin SDK from 0.8.4 to 0.9.0 Co-Authored-By: Claude <noreply@anthropic.com> * feat: adopt ClientConnection and kotlin-sdk-testing from MCP SDK 0.9.0 Co-Authored-By: Claude <noreply@anthropic.com> * fix: enforce note schema gates on cascade-to-TERMINAL transitions Cascade auto-completion previously bypassed gate enforcement, allowing parent items with schema tags to reach TERMINAL with required notes unfilled. Now cascade-to-TERMINAL checks all required notes (matching the "complete" trigger behavior). Schema-free parents cascade freely. Also extracts buildMissingNotesArray() to NoteSchemaJsonHelpers and replaces inline filledKeys computation with buildFilledKeys() across all three gate-check paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: document local-first squash flow in implement skill and CLAUDE.md Update the /implement skill to reflect the local-first git workflow: branches stay local, squash-merge into local main, and PRs are batched. Align CLAUDE.md git workflow section to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: schema improvement — role validation, AdvanceItemTool consolidation - Add VALID_SCHEMA_ROLES validation in YamlNoteSchemaService.parseEntry() to catch typos and invalid role values at config load time (warns + skips) - Consolidate AdvanceItemTool response fields to use shared computePhaseNoteContext() instead of manual NoteSchemaJsonHelpers calls - Remove findGuidancePointer() and buildNoteProgress() from NoteSchemaJsonHelpers (now gate-check helpers only) - Add plugin-change schema to config.yaml (gitignored, local only) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(test): create shared test infrastructure — TestBase, fixtures, and helpers Add reusable test utilities under current/src/test/.../test/: - TestFixtures.kt: makeItem(), blocksDep(), makeNote(), JSON param helpers, response extractors - BaseRepositoryTest.kt: H2 in-memory DB base class with createPersistedItem/Note/Dependency - MockRepositoryProvider.kt: MockK-based RepositoryProvider factory with context() builder - TestNoteSchemaService.kt: In-memory NoteSchemaService with FEATURE_IMPLEMENTATION/BUG_FIX presets - TestInfrastructureTest.kt: 27 validation tests covering all shared infrastructure Migrated SQLiteWorkItemRepositoryTest to extend BaseRepositoryTest as proof of concept. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: broaden retrospective nudge to cover all terminal transition paths The retrospective nudge previously only fired after complete_tree, missing single-item runs that reach terminal via advance_item. Updated the nudge condition to trigger on any terminal transition during an /implement run, with single-item, multi-item, and fallback detection rules. Also: minor AdvanceItemTool optimization — derive existingKeys from notesByKey instead of a separate .map() pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: replace session manifest with distributed session-tracking notes The monolithic run manifest (stored as a CC session task) had a 100% creation failure rate. Replace it with distributed `session-tracking` notes on each work item, enforced by schema gates. - Add default schema fallback to YamlNoteSchemaService (one-line change) - Add session-tracking note to feature-implementation, bug-fix, and plugin-change schemas in config.yaml - Create new `default` schema as catch-all for untagged items - Rewrite session-retrospective skill to aggregate distributed notes - Remove manifest section from workflow-orchestrator output style - Add lightweight delegation-metadata pattern for orchestrator-side data - Simplify retrospective nudge (remove manifest references) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add feature-task schema for lighter child work item gates Split feature work into two schema tiers: feature-implementation for the parent container (full spec, holistic review with /simplify) and feature-task for child work items (task-scope, task-level review). Plugin skills updated to remain generic — they reference config.yaml for schema discovery rather than hardcoding specific tag names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3bf0e67 commit 77e0d3e

File tree

17 files changed

+1009
-188
lines changed

17 files changed

+1009
-188
lines changed

.claude/skills/feature-implementation/SKILL.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
---
2-
description: Guide the full lifecycle of a feature-implementation tagged MCP item — from queue through review
2+
description: Guide the full lifecycle of a feature-implementation tagged MCP item (the feature container) — from queue through review
33
---
44

55
# Feature Implementation Workflow
66

7-
End-to-end workflow for a `feature-implementation` tagged item. Covers all three phases
8-
(queue → work → review) with gate-enforced notes at each transition.
7+
End-to-end workflow for a `feature-implementation` tagged item — the **feature container**
8+
that holds the specification, plan, and holistic review. Child work items under this
9+
container use the `feature-task` tag with lighter gates (task-scope instead of full
10+
specification, task-level review without `/simplify`).
11+
12+
Covers all three phases (queue → work → review) with gate-enforced notes at each transition.
913

1014
**Usage:** `/feature-implementation [item-uuid]`
1115

.claude/skills/implement/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ its own isolated branch automatically. See Worktree Isolation below. Worktree
6969
branches are also local-only and get squash-merged into `main` after review.
7070

7171
**Branch naming** (for local working branches):
72-
- `feat/<short-description>` — feature-implementation items
72+
- `feat/<short-description>` — feature-implementation / feature-task items
7373
- `fix/<short-description>` — bug-fix items
7474
- `fix/<grouped-description>` — batch of related bug fixes
7575
- `chore/<short-description>` — tech debt, refactoring, observations

.claude/skills/session-retrospective/SKILL.md

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: session-retrospective
33
description: "Analyze the current implementation run — evaluate schema effectiveness, delegation alignment, note quality, plan-to-execution fit. Captures cross-session trends and proposes improvements when patterns repeat. Use after implementation runs, or when user says 'retrospective', 'session review', 'what did we learn', 'analyze this run', 'how did that go', 'evaluate our process', 'wrap up', 'end of session review'. Also use when the output style's retrospective nudge fires after complete_tree."
4-
argument-hint: "[optional: --dry-run to preview without creating items]"
4+
argument-hint: "[optional: root item UUID] [--dry-run to preview without creating items]"
55
---
66

77
# Session Retrospective
@@ -16,64 +16,82 @@ If `$ARGUMENTS` contains `--dry-run`, set **DRY_RUN = true**. In dry-run mode, p
1616

1717
---
1818

19-
## Step 1 — Load the Manifest
19+
## Step 1 — Gather Scope
2020

21-
Search for the run manifest session task:
21+
Determine which items to analyze by collecting distributed `session-tracking` notes.
22+
23+
### 1a. Identify items in scope
24+
25+
**If `$ARGUMENTS` contains a UUID (root item ID):**
2226

2327
```
24-
TaskList — scan for tasks named `run-manifest:*`
25-
TaskGet — read the most recent one
28+
query_items(operation="overview", itemId="<root-uuid>")
2629
```
2730

28-
Parse the JSON manifest. Extract: `runId`, `scope`, `delegations`, `schemaInteractions`, `observations`, `friction`, `extensions`.
31+
This returns the root item and its children. Collect all item UUIDs from the overview.
2932

30-
**If no manifest exists (fallback):** Query MCP for recently active items:
33+
**If no root item ID provided:**
3134

3235
```
3336
get_context() — active, blocked, stalled items
34-
query_items(operation="search", role="terminal", sortBy="modifiedAt", sortOrder="desc", limit=20) — recently completed items
37+
query_items(operation="search", role="terminal", sortBy="modifiedAt", sortOrder="desc", limit=20)
38+
```
39+
40+
Build scope from recently completed items (compare `modifiedAt` to current date). Discard items that appear stale (modified more than 24 hours ago).
41+
42+
### 1b. Collect distributed notes
43+
44+
For each item in scope (up to 20):
45+
46+
```
47+
query_notes(itemId="<uuid>", includeBody=true)
3548
```
3649

37-
Build a synthetic scope from items modified today (compare `modifiedAt` to current date). Discard items that appear stale. Note in the report: `**Fallback mode** — no run manifest found. Analysis based on MCP state queries.`
50+
Extract:
51+
- Notes with key `session-tracking` — these contain per-item outcome, files changed, deviations, friction, observations, and test results
52+
- Notes with key `delegation-metadata` (optional) — orchestrator-recorded model and isolation data
3853

39-
**If neither manifest nor fallback produces any items in scope:** Exit early with:
54+
### 1c. Early exit
55+
56+
**If no items are found in scope, or no `session-tracking` notes exist on any item:** Exit early with:
4057
```
41-
No implementation run data found. Nothing to retrospect — run `/implement` first, then try again.
58+
No implementation run data found. Nothing to retrospect — run `/implement` first, then try again.
4259
```
4360

4461
---
4562

46-
## Step 2 — Gather Retrospective Data
63+
## Step 2 — Aggregate Note Data
64+
65+
From the collected `session-tracking` notes, aggregate across all items:
4766

48-
Run these calls in parallel:
67+
- **Total item count** and **outcome distribution** (success, partial, failed, skipped)
68+
- **Combined files list** — all files changed across items, deduplicated
69+
- **Combined friction list** — all friction entries from all items
70+
- **Combined observations** — notable observations from all items
71+
- **Test results summary** — pass/fail counts across items
4972

50-
1. For each item in `scope.preExistingItems` + `scope.adHocItems` (up to 20):
51-
```
52-
query_notes(itemId="<uuid>", includeBody=true)
53-
```
54-
2. `get_context()` — current state snapshot
73+
If `delegation-metadata` notes exist on any items, extract:
74+
- Model used per delegation (haiku, sonnet, opus)
75+
- Isolation mode (inline, worktree)
76+
- These feed into delegation alignment analysis (step 3b)
5577

56-
Cross-reference:
57-
- Match `delegations[].itemId` to items — verify outcomes
58-
- Match `schemaInteractions[].itemId` to notes — evaluate content quality
59-
- Identify items in scope with no delegation entry → self-implemented
60-
- Identify items with delegations but no notes → incomplete work
78+
Run `get_context()` in parallel for the current state snapshot.
6179

6280
---
6381

6482
## Step 3 — Evaluate Across Dimensions
6583

6684
### 3a. Schema Effectiveness
6785

68-
For each `schemaInteraction` entry:
69-
- Was `guidanceFollowed: true` for all notes?
86+
For each item in scope, examine its actual notes (from step 1b):
87+
- Which schema-required notes exist? Check for non-empty content.
7088
- Token count per note: **<50 = sparse** (flag), **50-500 = appropriate**, **>500 = potentially verbose** (flag for status-type notes; specification notes are exempt from the upper bound)
71-
- Were gate failures recorded? What caused them?
72-
- **Score:** Fraction of notes where guidance was followed and content was appropriately sized
89+
- Were any items missing required notes (indicating gate failures or schema-free items)?
90+
- **Score:** Fraction of expected schema notes that exist with appropriately sized content
7391

7492
### 3b. Delegation Alignment
7593

76-
Cross-reference `delegations[]` against the output style delegation table:
94+
**If `delegation-metadata` notes exist on items**, cross-reference against the delegation table:
7795

7896
| Task type | Expected model |
7997
|-----------|---------------|
@@ -82,27 +100,28 @@ Cross-reference `delegations[]` against the output style delegation table:
82100
| Architecture, complex tradeoffs, multi-file synthesis | `opus` |
83101

84102
- Flag misalignments (e.g., opus for bulk MCP ops, haiku for architecture)
85-
- Flag `selfImplemented: true` entries — the orchestrator should delegate, not implement directly
86-
- **Score:** Fraction of delegations matching expected model for their rationale
103+
- **Score:** Fraction of delegations matching expected model for their task type
104+
105+
**If no `delegation-metadata` notes exist:** Note "delegation metadata not recorded" and skip scoring for this dimension.
87106

88107
### 3c. Note Effectiveness
89108

90109
For items with both queue-phase notes (specs) and work-phase notes (implementation):
91110
- Compare spec content themes to implementation note themes
92-
- If implementation notes mention "deviated from spec", "unexpected", or "assumption was wrong" flag as a spec gap
93-
- If work-phase notes are nearly empty (<30 tokens) flag as context loss for downstream agents
111+
- If implementation notes mention "deviated from spec", "unexpected", or "assumption was wrong" -> flag as a spec gap
112+
- If work-phase notes are nearly empty (<30 tokens) -> flag as context loss for downstream agents
94113
- **Score:** Qualitative (effective / mixed / ineffective)
95114

96115
### 3d. Plan-to-Execution Alignment
97116

98-
From `scope`:
99-
- `adHocItems` not in `preExistingItems` = scope change (may be necessary or scope creep)
100-
- Items in `preExistingItems` still in queue role = planned but skipped
117+
Compare item creation timestamps to the root item's creation time (or the earliest item in scope if no root provided):
118+
- Items created significantly after the root (>1 hour) = ad-hoc additions (may be necessary or scope creep)
119+
- Items still in queue role under the root = planned but skipped
101120
- **Score:** Fraction of planned items that reached terminal
102121

103122
### 3e. Friction Synthesis
104123

105-
Group `friction[]` entries and `observations[]` by type:
124+
Extract friction entries from each item's `session-tracking` note. Group by type:
106125
- `tool-error` — MCP or tool failures
107126
- `excessive-roundtrips` — more calls than necessary
108127
- `workaround` — agent had to work around a limitation
@@ -166,7 +185,7 @@ manage_notes(operation="upsert", notes=[
166185
itemId: "<retro-uuid>",
167186
key: "session-metrics",
168187
role: "queue",
169-
body: "<Step 3 quantitative data: item counts, delegation breakdown, schema usage, token estimates>"
188+
body: "<Step 3 quantitative data: item counts, outcome distribution, schema usage, token estimates, files changed>"
170189
},
171190
{
172191
itemId: "<retro-uuid>",
@@ -178,7 +197,7 @@ manage_notes(operation="upsert", notes=[
178197
itemId: "<retro-uuid>",
179198
key: "improvement-signals",
180199
role: "queue",
181-
body: "<Step 4 trend analysis: new trends, reinforced trends, proposals, extension promotions>"
200+
body: "<Step 4 trend analysis: new trends, reinforced trends, proposals>"
182201
}
183202
])
184203
```
@@ -202,7 +221,6 @@ Read `memory/retrospectives.md` from the auto memory directory. Update each sect
202221
- **Schema Effectiveness:** Add or update entries from dimension 3a findings. Format: `- <schema-note-key>: <observation>. Sessions: N. Last seen: YYYY-MM-DD`
203222
- **Delegation Patterns:** Add or update from dimension 3b. Format: `- <pattern>. Sessions: N. Last seen: YYYY-MM-DD`
204223
- **Note Quality:** Add or update from dimension 3c. Format: `- <note-key>: <observation>. Sessions: N. Last seen: YYYY-MM-DD`
205-
- **Extension Candidates:** Add entries from `manifest.extensions[]` that appeared in this run. Format: `- <key>: <value pattern>. Sessions: N. Promoted: no`
206224
- **Improvement Proposals:** Add new proposal references if created in step 7.
207225

208226
Write the updated file.
@@ -243,14 +261,6 @@ The proposal should include a **concrete suggestion** — not just "this is a pr
243261
- Output style adjustments: specify the zone and content
244262
- Hook additions: specify the event, matcher, and purpose
245263

246-
### 7c. Extension promotions
247-
248-
If any `extensions[]` keys from the trend memory have `Sessions >= 2`:
249-
- Create a proposal item with the proposed JSON schema addition (field name, type, enum values if applicable)
250-
- Note which runs used the extension and what values they recorded
251-
252-
Update the Extension Candidates section in trend memory: set `Promoted: yes` for graduated entries.
253-
254264
---
255265

256266
## Step 8 — Meta-Evaluation
@@ -287,16 +297,16 @@ manage_notes(operation="upsert", notes=[{
287297
Render a dashboard using the output style visual conventions:
288298

289299
```
290-
## Session Retrospective — <root-item-title>
300+
## Session Retrospective — <root-item-title>
291301
292-
**<YYYY-MM-DD> · <N> items · <N> delegations · <N> schemas used**
302+
**<YYYY-MM-DD> · <N> items · <N> schemas used**
293303
294304
### Dimension Scores
295305
296306
| Dimension | Score | Key Finding |
297307
|-----------|-------|-------------|
298308
| Schema effectiveness | <fraction or qualitative> | <one-line summary> |
299-
| Delegation alignment | <fraction> | <one-line summary> |
309+
| Delegation alignment | <fraction or "not recorded"> | <one-line summary> |
300310
| Note effectiveness | <qualitative> | <one-line summary> |
301311
| Plan-to-execution | <fraction> | <one-line summary> |
302312
| Friction | <count> entries, <N> themes | <top theme> |
@@ -312,27 +322,20 @@ Render a dashboard using the output style visual conventions:
312322
| ID | Proposal | Trigger |
313323
|----|----------|---------|
314324
| `<short-id>` | <description> | <trend that graduated> |
315-
316-
### Extension Promotions
317-
318-
| Key | Value Pattern | Sessions | Recommendation |
319-
|-----|---------------|----------|----------------|
320-
| <key> | <typical value> | N | promote to core / keep observing |
321325
```
322326

323-
**Conditional prefixes:**
327+
**Conditional prefix:**
324328
- Dry-run: `**Dry run** — no items created, no memory updated.`
325-
- Fallback: `**Fallback mode** — no run manifest found. Analysis based on MCP state queries.`
326329

327-
Omit sections with no data (e.g., no extension promotions → omit that table).
330+
Omit sections with no data (e.g., no improvement proposals -> omit that table). If `delegation-metadata` notes were present, include a delegations count in the header line.
328331

329332
---
330333

331334
## Troubleshooting
332335

333-
**No manifest found**
334-
- Cause: Implementation run did not maintain a run manifest (output style not active, or short session)
335-
- Solution: Skill falls back to MCP queries. Less precise but functional. The report notes fallback mode.
336+
**No session-tracking notes found**
337+
- Cause: Implementing agents did not fill their `session-tracking` notes. This happens when items have no matching note schema (schema-free items skip gate enforcement).
338+
- Solution: Check `.taskorchestrator/config.yaml` for a `default` schema that includes `session-tracking` as a required note. Adding it ensures agents are prompted to fill tracking data.
336339

337340
**Schema not recognized (`expectedNotes` empty)**
338341
- Cause: `session-retrospective` tag not in `.taskorchestrator/config.yaml`, or MCP not reconnected after config edit

claude-plugins/task-orchestrator/output-styles/workflow-orchestrator.md

Lines changed: 8 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -62,74 +62,18 @@ Every delegation prompt must include: entity IDs, exact tool operations, expecte
6262

6363
Session tasks are ephemeral — they exist for the current session only. Do NOT use them for items that need to persist across sessions.
6464

65-
## Implementation Run Manifest
66-
67-
During any implementation run (triggered by `/implement` or `post-plan-workflow`), maintain a structured run manifest as a **session task** using `TaskCreate`/`TaskUpdate`. This provides durable in-session storage that survives context compaction.
68-
69-
**Lifecycle:**
70-
1. At run start, `TaskCreate` a session task named `run-manifest:<root-item-short-id>` with the initial JSON
71-
2. After each delegation return, `TaskUpdate` to append to `delegations[]`
72-
3. After each schema interaction (note fill, gate failure), `TaskUpdate` to append to `schemaInteractions[]`
73-
4. After each observation logged, `TaskUpdate` to append the UUID to `observations[]`
74-
5. When friction is encountered, `TaskUpdate` to append to `friction[]`
75-
6. For data worth tracking that does not fit core fields, append to `extensions[]` with a reason
76-
77-
**Manifest schema (fixed core + extensions):**
78-
79-
```json
80-
{
81-
"runId": "<root-item-uuid>",
82-
"startedAt": "<ISO-8601>",
83-
"scope": {
84-
"rootItemId": "<uuid>",
85-
"rootItemTitle": "<string>",
86-
"preExistingItems": ["<uuid>"],
87-
"adHocItems": ["<uuid>"]
88-
},
89-
"delegations": [
90-
{
91-
"itemId": "<uuid>",
92-
"model": "sonnet | haiku | opus",
93-
"rationale": "bulk-mcp-ops | code-implementation | test-writing | architecture-review | research | materialization",
94-
"isolation": "worktree | none",
95-
"selfImplemented": false,
96-
"outcome": "success | failure | partial"
97-
}
98-
],
99-
"schemaInteractions": [
100-
{
101-
"itemId": "<uuid>",
102-
"schemaTag": "<string>",
103-
"notesFilled": [
104-
{ "key": "<string>", "phase": "queue | work | review", "approxTokens": 0, "guidanceFollowed": true }
105-
],
106-
"gateFailures": 0
107-
}
108-
],
109-
"observations": ["<observation-uuid>"],
110-
"friction": [
111-
{ "type": "tool-error | excessive-roundtrips | workaround | api-confusion", "brief": "<string>" }
112-
],
113-
"extensions": [
114-
{
115-
"key": "<agent-suggested-key>",
116-
"value": "<typed value>",
117-
"reason": "<why this was worth tracking>",
118-
"sessionCount": 1
119-
}
120-
]
121-
}
122-
```
65+
## Delegation Metadata
66+
67+
After dispatching a subagent and receiving its return, optionally record delegation context on the work item via `manage_notes(operation="upsert")`:
68+
69+
- **key:** `delegation-metadata`, **role:** `work`
70+
- **body:** Model used, isolation mode, rationale, outcome
12371

124-
**Rules:**
125-
- `model` and `rationale` use closed enums. Unknown rationales go in `extensions[]`.
126-
- `selfImplemented: true` when the orchestrator implements directly instead of delegating.
127-
- The manifest is consumed by `/session-retrospective` — do not display it to the user directly.
128-
- If no implementation run is active, do not create a manifest.
72+
This is orchestrator-side data that agents cannot self-report. The `/session-retrospective` skill uses it for delegation alignment analysis when present.
12973

13074
## Retrospective Nudge
13175

132-
After `complete_tree` completes, or when 3+ items transition to terminal during a session, append a one-line suggestion:
76+
When items reach terminal after an implementation run — whether via `advance_item`, `complete_tree`, or auto-cascade — suggest running `/session-retrospective` to capture learnings.
13377

13478
```
13579
↳ Implementation run complete. Consider running `/session-retrospective` to capture learnings.

claude-plugins/task-orchestrator/skills/create-item/SKILL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ Read `.taskorchestrator/config.yaml` to discover available note schemas (this is
8181

8282
| Context signal | Schema to apply |
8383
|----------------|-----------------|
84-
| Feature, enhancement, new capability | `feature-implementation` (if it exists in config) |
85-
| Bug, error, crash, unexpected behavior | `bug-fix` (if it exists in config) |
86-
| Observation, friction, optimization, missing capability | `agent-observation` (if it exists in config) |
84+
| Feature, enhancement, new capability | Match against feature-related schema keys in config (if any exist) |
85+
| Bug, error, crash, unexpected behavior | Match against bug-related schema keys in config (if any exist) |
86+
| Observation, friction, optimization, missing capability | Match against observation-related schema keys in config (if any exist) |
8787

8888
If the inferred schema key exists in the config, apply it as the item's `tags` value. If the key does not exist in the config (e.g., no `bug-fix` schema defined), leave tags empty — do not apply a tag that has no matching schema.
8989

0 commit comments

Comments
 (0)