@yottagraph-app/aether-instructions 1.1.22 → 1.1.24

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.
@@ -92,7 +92,47 @@ Key capabilities:
92
92
 
93
93
  ---
94
94
 
95
- ## Step 4: Design the UX
95
+ ## Step 4: Verify Data Availability
96
+
97
+ Before designing UX, verify that the data your app needs actually exists
98
+ in the knowledge graph. This prevents building features around empty data.
99
+
100
+ **If MCP tools are available:**
101
+
102
+ ```
103
+ elemental_get_schema() → list entity types, confirm your target types exist
104
+ elemental_get_entity(entity="Microsoft") → verify entity lookup works with a known entity
105
+ elemental_get_entity(entity="Apple Inc") → try another known entity
106
+ ```
107
+
108
+ If schema calls succeed but entity lookups return "not found," that means
109
+ the entity type exists in the schema but has no data. That's a **data
110
+ issue**, not a broken server. Try different, well-known entity names.
111
+
112
+ **If MCP tools are NOT available, use curl:**
113
+
114
+ ```bash
115
+ # Read credentials from broadchurch.yaml
116
+ GW=$(grep 'url:' broadchurch.yaml | head -1 | sed 's/.*"\(.*\)".*/\1/')
117
+ ORG=$(grep 'org_id:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
118
+ KEY=$(grep 'qs_api_key:' broadchurch.yaml | sed 's/.*"\(.*\)".*/\1/')
119
+
120
+ # List entity types
121
+ curl -s "$GW/api/qs/$ORG/elemental/metadata/schema" -H "X-Api-Key: $KEY"
122
+
123
+ # Search for a known entity
124
+ curl -s "$GW/api/qs/$ORG/entities/search" \
125
+ -X POST -H "Content-Type: application/json" -H "X-Api-Key: $KEY" \
126
+ -d '{"queries":[{"queryId":1,"query":"Microsoft"}],"maxResults":3,"includeNames":true}'
127
+ ```
128
+
129
+ **Document what you find.** If certain entity types have sparse data, note
130
+ it in your UX plan. Design features around data that actually exists, and
131
+ mark aspirational features (that need more data) as future work.
132
+
133
+ ---
134
+
135
+ ## Step 5: Design the UX
96
136
 
97
137
  Based on the brief, think about the right UX for this specific problem. Do NOT default to a sidebar-with-tabs layout. Consider:
98
138
 
@@ -116,7 +156,7 @@ Present the plan to the user and ask for approval before proceeding.
116
156
 
117
157
  ---
118
158
 
119
- ## Step 5: Build
159
+ ## Step 6: Build
120
160
 
121
161
  Implement the plan:
122
162
 
@@ -128,6 +168,13 @@ Implement the plan:
128
168
  6. Use Vuetify components and the project's dark theme
129
169
  7. Update `DESIGN.md` with what you built
130
170
 
171
+ **Use the pre-built platform utilities:**
172
+
173
+ - `useElementalSchema()` — schema discovery with caching, flavor/PID lookup helpers
174
+ - `buildGatewayUrl()`, `getApiKey()`, `padNeid()` from `utils/elementalHelpers`
175
+ - `searchEntities()`, `getEntityName()` from `utils/elementalHelpers`
176
+ - `useElementalClient()` from `@yottagraph-app/elemental-api/client`
177
+
131
178
  **Follow the project's coding conventions:**
132
179
 
133
180
  - `<script setup lang="ts">` for all Vue components
@@ -136,7 +183,7 @@ Implement the plan:
136
183
 
137
184
  ---
138
185
 
139
- ## Step 6: Verify
186
+ ## Step 7: Verify
140
187
 
141
188
  After building, check dependencies are installed and run a build:
142
189
 
@@ -151,7 +198,7 @@ Then suggest the user run `npm run dev` to preview their app locally.
151
198
 
152
199
  ---
153
200
 
154
- ## Step 7: Next Steps
201
+ ## Step 8: Next Steps
155
202
 
156
203
  > Your app is taking shape! Here's what you can do next:
157
204
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yottagraph-app/aether-instructions",
3
- "version": "1.1.22",
3
+ "version": "1.1.24",
4
4
  "description": "Cursor rules, commands, and skills for Aether development",
5
5
  "files": [
6
6
  "rules",
package/rules/api.mdc CHANGED
@@ -50,6 +50,29 @@ elemental_get_related(entity="Apple",
50
50
  MCP tells you the correct flavor IDs, property IDs, and data shapes. Use
51
51
  these to inform your REST implementation.
52
52
 
53
+ **Verify MCP is working with known-good queries:**
54
+
55
+ ```
56
+ elemental_get_schema() → should return flavors + properties
57
+ elemental_get_entity(entity="Microsoft") → should resolve to a company
58
+ elemental_get_entity(entity="Apple Inc") → another known entity
59
+ elemental_health() → server health check
60
+ ```
61
+
62
+ **Interpreting MCP errors — do NOT assume the server is broken:**
63
+
64
+ - `entity not found` or 404 in entity lookup → the entity doesn't exist
65
+ in the knowledge graph, not a connectivity problem. Try a different entity.
66
+ - `failed to get property values: 404` → the entity was resolved but has
67
+ no data for those properties. The MCP server is working correctly.
68
+ - Schema calls succeed but entity calls fail → data is sparse for that
69
+ entity type. Try well-known entities (Microsoft, Apple Inc, JPMorgan).
70
+ - If `elemental_health()` fails → actual connectivity problem.
71
+
72
+ **Key insight:** A 404 from an MCP entity/property call means "not found,"
73
+ not "server broken." Always test with known entities before concluding
74
+ the server is down.
75
+
53
76
  ### Step 2: curl (verify exact request/response shapes)
54
77
 
55
78
  MCP doesn't cover every REST endpoint (e.g. `/elemental/find` expressions).
@@ -101,11 +124,44 @@ All other endpoints accept `application/json`.
101
124
  **Interpreting errors:** 400 = expression syntax is wrong. 500 = expression
102
125
  is valid but the query failed (wrong PID, unsupported operator for that
103
126
  property type). 200 + empty `eids` = query worked but no results match.
127
+ 404 from entity/property endpoints = entity or data doesn't exist (not a
128
+ server error). Always test with known entities (e.g. search for "Microsoft")
129
+ before assuming the API is broken.
104
130
 
105
131
  ### Step 3: Implement with confidence
106
132
 
107
133
  Now write your composable or server route, knowing the exact API shapes.
108
134
 
135
+ ## Pre-Built Helpers
136
+
137
+ The template includes composables and utilities that handle common
138
+ Elemental API patterns. **Use these instead of writing from scratch:**
139
+
140
+ ### `useElementalSchema()` — Schema Discovery with Caching
141
+
142
+ ```typescript
143
+ const { flavors, properties, flavorByName, pidByName, refresh } = useElementalSchema();
144
+ await refresh(); // fetches once, then cached
145
+ const articleFid = flavorByName('article'); // → number | null
146
+ const namePid = pidByName('name'); // → typically 8
147
+ ```
148
+
149
+ Handles the dual response shapes (`res.schema.flavors` vs `res.flavors`)
150
+ and the `fid`/`findex` naming inconsistency automatically.
151
+
152
+ ### `utils/elementalHelpers` — Gateway URL Helpers
153
+
154
+ ```typescript
155
+ import { buildGatewayUrl, getApiKey, padNeid, searchEntities, getEntityName } from '~/utils/elementalHelpers';
156
+
157
+ const url = buildGatewayUrl('entities/search'); // full gateway URL
158
+ const key = getApiKey(); // from runtimeConfig
159
+ const neid = padNeid('4926132345040704022'); // → "04926132345040704022"
160
+
161
+ const results = await searchEntities('Microsoft'); // batch name search
162
+ const name = await getEntityName(neid); // display name lookup
163
+ ```
164
+
109
165
  ## Client Usage
110
166
 
111
167
  All API calls go through `useElementalClient()` from `@yottagraph-app/elemental-api/client`.
@@ -388,7 +388,169 @@ Two-column layout with selectable list and detail panel.
388
388
  </script>
389
389
  ```
390
390
 
391
- ## 7. Get Filings for a Company
391
+ ## 7. News Feed Recent Articles with Sentiment
392
+
393
+ Fetch recent articles from the knowledge graph. Uses `useElementalSchema()`
394
+ for runtime flavor/PID discovery and `buildGatewayUrl()` for gateway access.
395
+
396
+ ```vue
397
+ <template>
398
+ <div class="d-flex flex-column fill-height pa-4">
399
+ <h1 class="text-h5 mb-4">Recent News</h1>
400
+ <v-alert v-if="error" type="error" variant="tonal" class="mb-4" closable>
401
+ {{ error }}
402
+ </v-alert>
403
+ <v-progress-linear v-if="loading" indeterminate class="mb-4" />
404
+ <v-list v-if="articles.length" lines="three">
405
+ <v-list-item v-for="a in articles" :key="a.neid">
406
+ <template #title>
407
+ <span>{{ a.name || a.neid }}</span>
408
+ <v-chip
409
+ v-if="a.sentiment"
410
+ size="x-small"
411
+ class="ml-2"
412
+ :color="a.sentiment > 0 ? 'success' : a.sentiment < 0 ? 'error' : 'grey'"
413
+ >
414
+ {{ a.sentiment > 0 ? 'Bullish' : a.sentiment < 0 ? 'Bearish' : 'Neutral' }}
415
+ </v-chip>
416
+ </template>
417
+ <template #subtitle>{{ a.neid }}</template>
418
+ </v-list-item>
419
+ </v-list>
420
+ <v-empty-state
421
+ v-else-if="!loading"
422
+ headline="No articles found"
423
+ icon="mdi-newspaper-variant-outline"
424
+ />
425
+ </div>
426
+ </template>
427
+
428
+ <script setup lang="ts">
429
+ import { useElementalClient } from '@yottagraph-app/elemental-api/client';
430
+ import { padNeid } from '~/utils/elementalHelpers';
431
+
432
+ const client = useElementalClient();
433
+ const { flavorByName, pidByName, refresh: loadSchema } = useElementalSchema();
434
+
435
+ const articles = ref<{ neid: string; name: string; sentiment: number | null }[]>([]);
436
+ const loading = ref(false);
437
+ const error = ref<string | null>(null);
438
+
439
+ onMounted(async () => {
440
+ loading.value = true;
441
+ try {
442
+ await loadSchema();
443
+ const articleFid = flavorByName('article');
444
+ if (!articleFid) {
445
+ error.value = 'Article entity type not found in schema';
446
+ return;
447
+ }
448
+
449
+ const res = await client.findEntities({
450
+ expression: JSON.stringify({ type: 'is_type', is_type: { fid: articleFid } }),
451
+ limit: 20,
452
+ });
453
+ const neids: string[] = (res as any).eids ?? [];
454
+
455
+ if (!neids.length) {
456
+ return;
457
+ }
458
+
459
+ const namePid = pidByName('name');
460
+ const sentimentPid = pidByName('sentiment');
461
+ const pids = [namePid, sentimentPid].filter((p): p is number => p !== null);
462
+
463
+ const props = await client.getPropertyValues({
464
+ eids: JSON.stringify(neids),
465
+ pids: JSON.stringify(pids),
466
+ });
467
+
468
+ const valueMap = new Map<string, Record<number, any>>();
469
+ for (const v of (props as any).values ?? []) {
470
+ const eid = padNeid(v.eid ?? v.entity_id ?? '');
471
+ if (!valueMap.has(eid)) valueMap.set(eid, {});
472
+ valueMap.get(eid)![v.pid] = v.value;
473
+ }
474
+
475
+ articles.value = neids.map((neid) => {
476
+ const vals = valueMap.get(neid) ?? {};
477
+ return {
478
+ neid,
479
+ name: namePid ? (vals[namePid] as string) ?? neid : neid,
480
+ sentiment: sentimentPid ? (vals[sentimentPid] as number) ?? null : null,
481
+ };
482
+ });
483
+ } catch (e: any) {
484
+ error.value = e.message || 'Failed to load articles';
485
+ } finally {
486
+ loading.value = false;
487
+ }
488
+ });
489
+ </script>
490
+ ```
491
+
492
+ ## 8. Entity Search with Gateway Helpers
493
+
494
+ Simpler version of recipe #1 using the pre-built `searchEntities()` helper.
495
+
496
+ ```vue
497
+ <template>
498
+ <div class="d-flex flex-column fill-height pa-4">
499
+ <h1 class="text-h5 mb-4">Entity Search</h1>
500
+ <v-text-field
501
+ v-model="query"
502
+ label="Search entities"
503
+ prepend-inner-icon="mdi-magnify"
504
+ variant="outlined"
505
+ @keyup.enter="search"
506
+ :loading="loading"
507
+ />
508
+ <v-alert v-if="error" type="error" variant="tonal" class="mt-2" closable>
509
+ {{ error }}
510
+ </v-alert>
511
+ <v-list v-if="results.length" class="mt-4">
512
+ <v-list-item
513
+ v-for="r in results"
514
+ :key="r.neid"
515
+ :title="r.name"
516
+ :subtitle="r.neid"
517
+ />
518
+ </v-list>
519
+ <v-empty-state
520
+ v-else-if="searched && !loading"
521
+ headline="No results"
522
+ icon="mdi-magnify-remove-outline"
523
+ />
524
+ </div>
525
+ </template>
526
+
527
+ <script setup lang="ts">
528
+ import { searchEntities } from '~/utils/elementalHelpers';
529
+
530
+ const query = ref('');
531
+ const results = ref<{ neid: string; name: string }[]>([]);
532
+ const loading = ref(false);
533
+ const error = ref<string | null>(null);
534
+ const searched = ref(false);
535
+
536
+ async function search() {
537
+ if (!query.value.trim()) return;
538
+ loading.value = true;
539
+ error.value = null;
540
+ searched.value = true;
541
+ try {
542
+ results.value = await searchEntities(query.value.trim());
543
+ } catch (e: any) {
544
+ error.value = e.message || 'Search failed';
545
+ results.value = [];
546
+ } finally {
547
+ loading.value = false;
548
+ }
549
+ }
550
+ </script>
551
+ ```
552
+
553
+ ## 9. Get Filings for a Company
392
554
 
393
555
  Fetch Edgar filings (or any relationship-linked documents) for an organization.
394
556
  Uses `$fetch` for the initial entity search because `POST /entities/search`
@@ -0,0 +1,184 @@
1
+ # Data Dictionary — BNY Arbitrage Rebate Analysis
2
+
3
+ ## Source Description
4
+
5
+ Interim Arbitrage Rebate Analysis reports prepared by BLX Group LLC for
6
+ municipal bond issuers. Each report computes the arbitrage rebate liability
7
+ under IRC Section 148 for a bond issue across a computation period. Reports
8
+ include transmittal letters, legal opinions, notes/assumptions, summary
9
+ schedules (rebate analysis, sources/uses of funds), and detailed per-fund
10
+ cash flow and investment data.
11
+
12
+ BNY (Bank of New York Mellon) serves as trustee. Orrick, Herrington &
13
+ Sutcliffe LLP provides the accompanying legal opinion.
14
+
15
+ ## Entity Types
16
+
17
+ ### bond
18
+
19
+ The municipal bond issue itself. One entity per unique bond across all reports.
20
+
21
+ | Property | Type | Description |
22
+ |----------|------|-------------|
23
+ | `client_matter_number` | string | **Strong ID.** BLX Group client matter number (e.g. `42182-2748`). Invariant across all reports for the same bond. |
24
+ | `bonds_name` | string | Full name of the bonds (e.g. "Multifamily Housing Revenue Refunding Bonds (Presidential Plaza at Newport Project-FHA Insured Mortgages) 1991 Series 1") |
25
+ | `par_amount` | string | Total par amount of the bond issue (e.g. "$142,235,000") |
26
+ | `dated_date` | string | Dated date of the bonds (e.g. "September 15, 1991") |
27
+ | `issue_date` | string | Issue date of the bonds (e.g. "October 17, 1991") |
28
+ | `bond_yield` | string | Arbitrage yield / allowable yield on investments (e.g. "7.420898%") |
29
+ | `computation_period` | string | The computation period for this report (e.g. "October 17, 1991 through October 16, 2024") |
30
+ | `rebate_computation_date` | string | End date of the computation period (e.g. "October 16, 2024") |
31
+ | `cumulative_rebate_liability` | string | Total cumulative rebate liability (e.g. "$0.00") |
32
+ | `rebate_payment_due` | string | Amount due to the US (e.g. "$0.00") |
33
+ | `return_on_investments` | string | Weighted return on investments since prior computation date (e.g. "6.447251%") |
34
+ | `shortfall_pct` | string | Shortfall percentage: return minus yield (e.g. "-0.973647%") |
35
+ | `actual_gross_earnings` | string | Total actual gross earnings across all funds |
36
+ | `allowable_gross_earnings` | string | Total allowable gross earnings at bond yield |
37
+ | `excess_earnings` | string | Total excess (negative = under yield) |
38
+ | `report_date` | string | Date the report was issued (e.g. "December 6, 2024") |
39
+
40
+ ### organization
41
+
42
+ Companies, agencies, law firms, and financial institutions named in the reports.
43
+
44
+ | Property | Type | Description |
45
+ |----------|------|-------------|
46
+ | `org_type` | string | Type: `government_agency`, `law_firm`, `financial_services`, `financial_institution`, `trust_company` |
47
+ | `description` | string | Role in the deal (e.g. "Issuer", "Trustee", "Bond Counsel") |
48
+
49
+ Expected entities:
50
+ - New Jersey Housing and Mortgage Finance Agency (Issuer)
51
+ - BLX Group LLC (Rebate Analyst)
52
+ - Orrick, Herrington & Sutcliffe LLP (Bond Counsel)
53
+ - BNY / Bank of New York Mellon (Trustee)
54
+ - Willdan Financial Services (Prior Report preparer)
55
+ - U.S. Department of the Treasury
56
+
57
+ ### fund_account
58
+
59
+ Bond proceeds sub-accounts tracked for rebate computation purposes.
60
+
61
+ | Property | Type | Description |
62
+ |----------|------|-------------|
63
+ | `fund_status` | string | Current status: `Active` or `Inactive` |
64
+ | `computation_date_valuation` | string | Fair market value at computation date |
65
+ | `gross_earnings` | string | Total gross earnings for the fund |
66
+ | `internal_rate_of_return` | string | IRR on the fund's investments |
67
+ | `excess_earnings` | string | Excess earnings (negative = below yield) |
68
+
69
+ Expected entities:
70
+ - Reserve I Account
71
+ - Reserve II Account
72
+ - Prior Rebate Liability
73
+ - Liquidity I Account
74
+ - Liquidity II Account
75
+
76
+ Additional fund accounts mentioned in Notes and Assumptions:
77
+ - Revenue Account (bona fide debt service fund, excluded from rebate)
78
+ - Escrow Fund
79
+ - Debt Service Reserve Account
80
+ - Construction Account
81
+
82
+ ### financial_instrument
83
+
84
+ Securities held within fund accounts (investments of bond proceeds).
85
+
86
+ | Property | Type | Description |
87
+ |----------|------|-------------|
88
+ | `par_amount` | string | Par amount of the security |
89
+ | `coupon` | string | Coupon rate (e.g. "7.000%" or "Variable") |
90
+ | `maturity_date` | string | Maturity date |
91
+ | `settlement_date` | string | Settlement date |
92
+ | `settlement_price` | string | Settlement price (e.g. "100.000") |
93
+ | `yield` | string | Yield on the security |
94
+ | `accreted_price` | string | Accreted price |
95
+ | `accrued_interest` | string | Accrued interest amount |
96
+ | `value` | string | Total value (par + accrued interest) |
97
+
98
+ Expected entities (scoped per fund via `entity_context_from_title`):
99
+ - Morgan IA (7% coupon, institutional investment)
100
+ - Federated MM (variable rate money market fund)
101
+
102
+ ### legal_agreement
103
+
104
+ Governing legal documents referenced in the reports.
105
+
106
+ | Property | Type | Description |
107
+ |----------|------|-------------|
108
+ | `agreement_type` | string | Type: `arbitrage_certificate`, `trust_indenture`, `engagement_letter` |
109
+ | `description` | string | Description of the agreement's role |
110
+
111
+ Expected entities:
112
+ - Certificate as to Arbitrage (Section 8, Section 21 referenced)
113
+ - Prior Report (Willdan Financial Services, December 17, 2008)
114
+
115
+ ### location
116
+
117
+ Addresses and jurisdictions.
118
+
119
+ Expected entities:
120
+ - Trenton, NJ (Issuer address)
121
+ - Dallas, TX (BLX Group address)
122
+ - New York, NY (Orrick address)
123
+ - State of New Jersey
124
+
125
+ ### person
126
+
127
+ Signatories, if extractable from signatures on transmittal letters and opinions.
128
+ May be sparse — many PDFs have illegible or absent signature blocks.
129
+
130
+ ## Relationships
131
+
132
+ | Relationship | Domain | Target | Description |
133
+ |-------------|--------|--------|-------------|
134
+ | `issuer_of` | organization | bond | Issuer issued the bonds |
135
+ | `trustee_of` | organization | bond | Trustee of the bond issue |
136
+ | `advisor_to` | organization | bond | BLX Group as rebate analyst; Orrick as bond counsel |
137
+ | `fund_of` | fund_account | bond | Fund account belongs to the bond issue |
138
+ | `holds_investment` | fund_account | financial_instrument | Fund holds a security as an investment |
139
+ | `party_to` | organization | legal_agreement | Entity is party to an agreement |
140
+ | `located_at` | organization | location | Organization's address |
141
+ | `predecessor_of` | legal_agreement | legal_agreement | Prior Report preceded current Report |
142
+
143
+ ## Events
144
+
145
+ | Event | Severity | Description |
146
+ |-------|----------|-------------|
147
+ | Rebate computation | medium | Periodic computation of arbitrage rebate liability |
148
+ | Bond issuance | high | Original issuance of the bonds (October 17, 1991) |
149
+ | Bond refunding | high | Refunding of prior 1985 Series F and G bonds |
150
+ | Fund valuation | medium | Valuation of fund accounts at computation date |
151
+ | Rebate payment determination | high | Determination that rebate payment is/isn't due |
152
+ | Report issuance | low | Issuance of this rebate analysis report |
153
+
154
+ ## Table Extraction
155
+
156
+ ### Schedule A — Summary of Rebate Analysis
157
+
158
+ Maps to `rebate_analysis` table config. Key column: Fund Description.
159
+ Each row becomes a `fund_account` entity with properties for status,
160
+ valuation, earnings, IRR, and excess earnings.
161
+
162
+ ### Schedule B — Sources & Uses of Funds
163
+
164
+ Maps to `sources_of_funds` and `uses_of_funds` table configs. Uses
165
+ `document_entity_from_title` mode to attach line items as properties
166
+ on the bond entity.
167
+
168
+ ### Security Holdings (Schedules C1, D1, F1, G1)
169
+
170
+ Maps to `security_tables` config. Key column: Security Type. Each
171
+ security becomes a `financial_instrument` entity scoped by fund
172
+ account title. Properties: par amount, coupon, maturity, yield, etc.
173
+
174
+ ### Cash Flow Tables (Schedules C2, D2, E1, F2, G2)
175
+
176
+ New table config needed. These are the largest tables — hundreds of rows
177
+ of deposit/withdrawal transactions for each fund account. Columns:
178
+ Date, Description, Cash Flow, Muni-Days/Computation Date, FV Factor
179
+ at bond yield, FV As Of at bond yield, FV Factor at IRR, FV As Of at IRR.
180
+
181
+ ## Citations
182
+
183
+ All extracted data should cite the specific schedule and page number
184
+ within the source PDF. The PDF filename serves as the document identifier.