Tools for the Atmosphere
tools.slices.network
quickslice
atproto
html
1# External Dependency Validation Implementation Plan
2
3> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
5**Goal:** Warn users when their lexicons reference external dependencies that aren't published.
6
7**Architecture:** After honk validation passes, extract all `ref` properties pointing to external NSIDs, batch-query the lexicons quickslice to check which exist, show yellow warnings for missing ones.
8
9**Tech Stack:** JavaScript, GraphQL (lexicons quickslice)
10
11---
12
13### Task 1: Add missingDependencies to state
14
15**Files:**
16- Modify: `lexicon-publisher.html:642-644`
17
18**Step 1: Add the new state property**
19
20Find this block:
21```javascript
22 // New: DNS instructions
23 dnsInstructions: [], // [{ domain, record, lexicons }]
24 };
25```
26
27Change to:
28```javascript
29 // New: DNS instructions
30 dnsInstructions: [], // [{ domain, record, lexicons }]
31
32 // New: External dependency check
33 missingDependencies: [], // [nsid, ...]
34 };
35```
36
37**Step 2: Commit**
38
39```bash
40git add lexicon-publisher.html
41git commit -m "feat(lexicon-publisher): add missingDependencies state"
42```
43
44---
45
46### Task 2: Add extractExternalRefs function
47
48**Files:**
49- Modify: `lexicon-publisher.html` (after `getPublishableLexicons` function, around line 1097)
50
51**Step 1: Add the function**
52
53After the `getPublishableLexicons` function closing brace, add:
54
55```javascript
56 function extractExternalRefs(lexicons, userDomain) {
57 const refs = new Set();
58
59 function walk(obj) {
60 if (!obj || typeof obj !== 'object') return;
61 if (obj.ref && typeof obj.ref === 'string') {
62 const nsid = obj.ref.split('#')[0];
63 const domain = getDomainAuthority(nsid);
64 if (domain !== userDomain && !domain.endsWith('.' + userDomain)) {
65 refs.add(nsid);
66 }
67 }
68 for (const val of Object.values(obj)) walk(val);
69 }
70
71 for (const lex of lexicons) walk(lex);
72 return [...refs];
73 }
74```
75
76**Step 2: Commit**
77
78```bash
79git add lexicon-publisher.html
80git commit -m "feat(lexicon-publisher): add extractExternalRefs function"
81```
82
83---
84
85### Task 3: Add checkExternalDependencies function
86
87**Files:**
88- Modify: `lexicon-publisher.html` (after `extractExternalRefs`)
89
90**Step 1: Add the function**
91
92After `extractExternalRefs`, add:
93
94```javascript
95 async function checkExternalDependencies(externalNsids) {
96 if (externalNsids.length === 0) return { found: [], missing: [] };
97
98 try {
99 const result = await state.client.query(`
100 query CheckDeps($nsids: [String!]!) {
101 comAtprotoLexiconSchema(
102 first: 100,
103 where: { id: { in: $nsids } }
104 ) {
105 edges {
106 node { id }
107 }
108 }
109 }
110 `, { nsids: externalNsids });
111
112 const foundIds = (result?.comAtprotoLexiconSchema?.edges || [])
113 .map(e => e.node.id);
114
115 return {
116 found: externalNsids.filter(n => foundIds.includes(n)),
117 missing: externalNsids.filter(n => !foundIds.includes(n)),
118 };
119 } catch (err) {
120 console.error("External dependency check failed:", err);
121 return { found: [], missing: [] };
122 }
123 }
124```
125
126**Step 2: Commit**
127
128```bash
129git add lexicon-publisher.html
130git commit -m "feat(lexicon-publisher): add checkExternalDependencies function"
131```
132
133---
134
135### Task 4: Integrate into validateLexicons
136
137**Files:**
138- Modify: `lexicon-publisher.html:1396-1440` (validateLexicons function)
139
140**Step 1: Reset missingDependencies at start**
141
142Find:
143```javascript
144 state.result = null;
145 state.validationPassed = false;
146 state.conflicts = [];
147```
148
149Change to:
150```javascript
151 state.result = null;
152 state.validationPassed = false;
153 state.conflicts = [];
154 state.missingDependencies = [];
155```
156
157**Step 2: Add external dependency check after honk passes**
158
159Find:
160```javascript
161 // Check for conflicts if authenticated
162 if (state.viewer) {
163 await checkConflicts();
164 }
165```
166
167Change to:
168```javascript
169 // Check for external dependencies and conflicts if authenticated
170 if (state.viewer) {
171 const userDomain = getUserDomain(state.viewer.handle);
172 const externalRefs = extractExternalRefs(parseResult.lexicons, userDomain);
173 const { missing } = await checkExternalDependencies(externalRefs);
174 state.missingDependencies = missing;
175
176 await checkConflicts();
177 }
178```
179
180**Step 3: Commit**
181
182```bash
183git add lexicon-publisher.html
184git commit -m "feat(lexicon-publisher): check external deps during validation"
185```
186
187---
188
189### Task 5: Update renderResult to show warnings
190
191**Files:**
192- Modify: `lexicon-publisher.html:1251-1266` (renderResult function)
193
194**Step 1: Add warning display after success message**
195
196Find:
197```javascript
198 section.innerHTML = `
199 <div class="result ${cls}" style="margin-top: 0.75rem;">${icon} ${esc(state.result.message)}</div>
200 `;
201 }
202```
203
204Change to:
205```javascript
206 let html = `<div class="result ${cls}" style="margin-top: 0.75rem;">${icon} ${esc(state.result.message)}</div>`;
207
208 if (state.missingDependencies.length > 0) {
209 html += `
210 <div class="warning-box" style="margin-top: 0.75rem;">
211 <strong>Missing external dependencies:</strong>
212 <ul style="margin: 0.5rem 0 0 1.25rem;">
213 ${state.missingDependencies.map(d => `<li>${esc(d)}</li>`).join('')}
214 </ul>
215 </div>
216 `;
217 }
218
219 section.innerHTML = html;
220 }
221```
222
223**Step 2: Commit**
224
225```bash
226git add lexicon-publisher.html
227git commit -m "feat(lexicon-publisher): display missing dependency warnings"
228```
229
230---
231
232### Task 6: Manual test
233
234**Step 1: Test with external refs**
235
2361. Open lexicon-publisher in browser
2372. Sign in
2383. Upload or paste a lexicon that references an external type, e.g.:
239 ```json
240 {
241 "lexicon": 1,
242 "id": "slices.network.test",
243 "defs": {
244 "main": {
245 "type": "record",
246 "record": {
247 "type": "object",
248 "properties": {
249 "actor": { "type": "ref", "ref": "app.bsky.actor.defs#profileViewBasic" },
250 "fake": { "type": "ref", "ref": "com.fake.nonexistent#thing" }
251 }
252 }
253 }
254 }
255 }
256 ```
2574. Click Validate
2585. Verify: Success message appears, plus yellow warning box showing `com.fake.nonexistent` as missing (but not `app.bsky.actor.defs` if it's published)
259
260**Step 2: Final commit if needed**
261
262```bash
263git add lexicon-publisher.html
264git commit -m "feat(lexicon-publisher): complete external dependency validation"
265```