vortexa-claude-skills 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ # Vortexa API Guardrails
2
+
3
+ > These rules prevent the most common and costly mistakes when querying Vortexa's API.
4
+ > Loaded automatically for every /vortexa: skill.
5
+
6
+ ---
7
+
8
+ ## Activity Filters (Most Common Errors)
9
+
10
+ The #1 source of incorrect results is choosing the wrong `filter_activity`.
11
+
12
+ | User Intent | CORRECT filter | WRONG Choice | Why Wrong |
13
+ |---|---|---|---|
14
+ | "exports", "shipped out", "departures" | `loading_end` | `loading_state` | `loading_state` = currently loading at berth, not departed. Volumes will be far too low. |
15
+ | "imports", "arrivals", "received" | `unloading_start` | `unloading_state` | `unloading_state` = currently discharging, not arrived. Misses completed discharges. |
16
+ | "loadings", "loading at port" | `loading_state` | `loading_end` | `loading_end` = already departed, not actively loading. |
17
+ | "on the water", "afloat", "in transit" | `cargo_on_water_state` | `oil_on_water_state` | `oil_on_water_state` is DEPRECATED. Always use `cargo_on_water_state`. |
18
+ | "floating storage" | `storing_state` | `cargo_on_water_state` | `cargo_on_water_state` includes all transit, not just storage. |
19
+ | "everything", "all activity" | `any_activity` | omitting the parameter | Omitting defaults to `any_activity` but should always be explicit. |
20
+
21
+ **NEVER** use `loading_state` when the user says "exports" -- this is the single most common mistake.
22
+
23
+ **NEVER** use `oil_on_water_state` -- it is deprecated. Always use `cargo_on_water_state`.
24
+
25
+ **ALWAYS** pair exports/imports with `intra_movements="exclude_intra_country"` to exclude domestic flows.
26
+
27
+ **ALWAYS** set `filter_activity` explicitly -- never rely on the default.
28
+
29
+ ### Activity + Geography Logic
30
+
31
+ | Query Pattern | filter_activity | Geography Filter | intra_movements |
32
+ |---|---|---|---|
33
+ | "Saudi exports" | `loading_end` | `filter_origins=[saudi_id]` | `exclude_intra_country` |
34
+ | "China imports" | `unloading_start` | `filter_destinations=[china_id]` | `exclude_intra_country` |
35
+ | "Saudi exports to China" | `loading_end` | `filter_origins=[saudi_id]` + `filter_destinations=[china_id]` | `exclude_intra_country` |
36
+ | "crude on the water" | `cargo_on_water_state` | (none or storage_locations) | `all` |
37
+ | "floating storage in MEG" | `storing_state` | `filter_storage_locations=[meg_id]` | `all` |
38
+
39
+ ---
40
+
41
+ ## Entity Resolution
42
+
43
+ **NEVER** use entity names directly in API calls -- all filters require 64-character hex Vortexa IDs. Resolve names first using reference endpoints.
44
+
45
+ **ALWAYS** specify `filter_layer` when searching geographies. Without it, "China" returns ports, regions, and the country -- the wrong ID will silently produce wrong results.
46
+
47
+ **ALWAYS** handle multiple search matches with disambiguation -- present top matches to the user and ask them to confirm. NEVER guess which match is correct.
48
+
49
+ **ALWAYS** use full 64-character hex IDs for API calls. Truncated 16-character IDs appear in `cargo_movement_id` joins but are not valid for filter parameters.
50
+
51
+ | Common Mistake | Why It Fails | Correct Approach |
52
+ |---|---|---|
53
+ | `filter_origins=["Saudi Arabia"]` | Names are not valid filter values | Resolve via `Geographies().search(term="Saudi Arabia", filter_layer="country")` |
54
+ | `Geographies().search(term="China")` without layer | Returns ports, regions, country -- wrong ID likely picked | Add `filter_layer="country"` |
55
+ | Using 16-char ID from CM join | Filter APIs require full 64-char IDs | Use `.id` field from reference search results |
56
+ | `Products().search(term="crude")` without checking layer | Returns group, group_product, category, grade matches | Verify `layer` column matches intent (usually `group`) |
57
+
58
+ ---
59
+
60
+ ## Date Ranges
61
+
62
+ **NEVER** leave end dates at midnight (00:00:00) -- data from the last day will be excluded entirely. This is a silent data loss bug.
63
+
64
+ **ALWAYS** use `datetime(year, month, day, 23, 59, 59)` for historical end dates.
65
+
66
+ **ALWAYS** use calendar convention for named periods:
67
+ - "last year" = full previous calendar year (Jan 1 to Dec 31)
68
+ - "last month" = full previous calendar month
69
+ - "last quarter" = full previous calendar quarter
70
+
71
+ **ALWAYS** use trailing convention for number+period:
72
+ - "last 6 months" = today minus 6 months to now (rolling)
73
+ - "last 30 days" = today minus 30 days to now (rolling)
74
+
75
+ See `date-units.md` for the complete date parsing and unit selection reference.
76
+
77
+ ---
78
+
79
+ ## API Defaults That Bite
80
+
81
+ These defaults produce silently wrong results if not overridden.
82
+
83
+ | Parameter | Default | Problem | Fix |
84
+ |---|---|---|---|
85
+ | `size` (search endpoints) | `1` | Returns only 1 record -- looks like data exists but is incomplete | Always set `size=500` for batch retrieval |
86
+ | `filter_activity` | `any_activity` | Matches ALL cargo states, giving misleading aggregates | Always specify explicitly |
87
+ | `timeseries_unit` | `b` (barrels) | Wrong for LNG (should be `t`) and rate queries (should be `bpd`) | Match unit to commodity: oil=`bpd`, LNG=`t`, LPG=`t` |
88
+ | `intra_movements` | `all` | Includes domestic flows in export/import queries, inflating volumes | Set `exclude_intra_country` for international trade |
89
+ | `quantity_at_time_of` | `load` | For LNG, loaded volume differs from discharged due to boil-off | Use `unload` when analyzing delivered volumes |
90
+ | `filter_confidence` | `low` | Includes all confidence levels including uncertain data | Usually fine; set higher for high-confidence analysis |
91
+
92
+ **NEVER** rely on `size=1` default for search endpoints. Always set to 500 or appropriate value.
93
+
94
+ **ALWAYS** set both `timeseries_activity` AND `filter_activity` when they should differ (filter on one state, aggregate on another). They serve different purposes.
95
+
96
+ ---
97
+
98
+ ## Voyages-Specific
99
+
100
+ **NEVER** mix V1 underscore naming with V2 hyphen naming:
101
+ - V1: `origin_country`, `vessel_count`, `tonne_miles`
102
+ - V2: `origin-country`, `voyage-count`, `tonne-miles-sum`
103
+ - V2 metric and breakdown are constructor args; V1 they are search params
104
+
105
+ **NEVER** forget the `exclude_overlapping_entries` behavior difference:
106
+ - Timeseries: defaults to `true` (de-duplicates)
107
+ - Search: defaults to `false` (may count overlapping days)
108
+
109
+ **ALWAYS** check ballast voyage origin/destination inversion:
110
+ - For ballast voyages, `origins` = last discharge port (not where the ballast voyage started)
111
+ - For ballast voyages, `destinations` = next load port (not where the ballast voyage ends)
112
+ - This is the opposite of laden voyage logic
113
+
114
+ **ALWAYS** match `voyage_date_range_activity` to user intent:
115
+ - "active vessels" = `active` (broadest, any overlap with time window)
116
+ - "arrivals" = `arrivals` (voyage ended in window)
117
+ - "departures" = `departures` (voyage started in window)
118
+
119
+ ---
120
+
121
+ ## Confirmation Step (Mandatory)
122
+
123
+ **NEVER** execute an API call without showing filter confirmation to the user first.
124
+
125
+ Before any data query, present:
126
+
127
+ ```
128
+ Query: [restate the user's question]
129
+ Endpoint: [CargoTimeSeries / CargoMovements / VoyagesSearchEnriched / etc.]
130
+ Activity: [filter_activity value] ([user's term])
131
+ Origin: [name (layer)] or "not specified"
132
+ Destination: [name (layer)] or "not specified"
133
+ Product: [name (layer)] or "not specified"
134
+ Time: [start datetime] -> [end datetime]
135
+ Unit: [unit (reason for default or user-specified)]
136
+ Frequency: [value or "Please specify: daily/weekly/monthly?"]
137
+ Intra: [exclude_intra_country for exports/imports, all otherwise]
138
+ Additional: [any other filters applied]
139
+
140
+ Confirm or adjust?
141
+ ```
142
+
143
+ **ALWAYS** include the unit and its rationale in the confirmation.
144
+
145
+ **ALWAYS** include the frequency. If not specified and not determinable from context, ask the user.
146
+
147
+ **ALWAYS** wait for user confirmation or adjustment before executing.
148
+
149
+ ---
150
+
151
+ ## Quick Decision Table
152
+
153
+ | If the user says... | Use endpoint | Key filter | Common trap |
154
+ |---|---|---|---|
155
+ | "how much crude exported" | CargoTimeSeries | `loading_end` | Using `loading_state` instead |
156
+ | "list cargoes from X" | CargoMovements search | varies | Forgetting to set `size=500` |
157
+ | "show voyages to Y" | VoyagesSearchEnriched | `destinations` | Forgetting to set `size=500` |
158
+ | "monthly vessel count" | VoyagesTimeseries | `vessel_count` | Using V2 syntax with V1 |
159
+ | "tonne-miles by class" | VoyagesTimeseriesV2 | `tonne-miles-sum` | Using V1 underscore names |
160
+ | "port congestion" | Congestion breakdown | `breakdown_property` | Using VoyagesTimeseries instead |
161
+ | "floating storage levels" | CargoTimeSeries | `storing_state` | Using `cargo_on_water_state` |