backend-manager 5.8.2 → 5.8.3
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.
- package/CHANGELOG.md +9 -0
- package/PROGRESS.md +4 -4
- package/docs/ghostii.md +8 -5
- package/package.json +1 -1
- package/src/manager/events/cron/daily/ghostii-auto-publisher.js +7 -5
- package/test/helpers/content/ghostii-auto-publisher.js +8 -1
- package/test/helpers/content/ghostii-feed-integration.js +12 -19
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.8.3] - 2026-06-19
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **Ghostii feed tracking timestamps.** `trackFeedItem` now uses BEM's standard `metadata.{created,updated}` with `timestamp` (ISO) and `timestampUNIX` (seconds) instead of `FieldValue.serverTimestamp()`, which failed in the emulator context.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- **Firestore collection rename.** `ghostii-feed-items` → `ghostii-sources` for clarity and consistency with the OMEGA naming convention.
|
|
24
|
+
- **Extended feed test coverage.** Added 5 marketing/social feeds (Social Media Examiner, Hootsuite, Sprout Social, Buffer, Digiday) to the real-feed integration test. Threshold bumped from 2/3 to 5/7.
|
|
25
|
+
|
|
17
26
|
# [5.8.2] - 2026-06-19
|
|
18
27
|
|
|
19
28
|
### Fixed
|
package/PROGRESS.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
> Agents and maintainers should update this file regularly to reflect the current state of the project.
|
|
3
3
|
|
|
4
4
|
## 🎯 Current Focus
|
|
5
|
-
* **Goal:**
|
|
6
|
-
* **Current Phase:**
|
|
5
|
+
* **Goal:** Ghostii feed sources cleanup + Somiibo consumer config
|
|
6
|
+
* **Current Phase:** Fixes applied + tested, pending commit + publish
|
|
7
7
|
* **Priority:** High
|
|
8
|
-
* **Last Updated:** 2026-06-
|
|
9
|
-
* **Notes:**
|
|
8
|
+
* **Last Updated:** 2026-06-19 3:05 AM PDT
|
|
9
|
+
* **Notes:** Renamed Firestore collection `ghostii-feed-items` → `ghostii-sources`. Fixed `FieldValue.serverTimestamp()` → BEM metadata pattern. Added 5 marketing/social feeds to extended tests. All tests passing (20/20 standard, 9/9 extended). Somiibo consumer has feeds configured + `ghostii-sources.md` evaluation doc. Needs BEM publish + consumer deploy.
|
|
10
10
|
|
|
11
11
|
## 📌 Active Task List
|
|
12
12
|
* [ ] Phase 6: Setup scaffolds essential configs for fresh projects
|
package/docs/ghostii.md
CHANGED
|
@@ -46,27 +46,30 @@ Each `ghostii[]` entry has a `sources` array. The cron picks one at random per a
|
|
|
46
46
|
### Feed source flow
|
|
47
47
|
|
|
48
48
|
1. Fetch and parse the RSS/Atom/JSON feed via `feed-parser.parseFeed()`
|
|
49
|
-
2. Query `ghostii-
|
|
49
|
+
2. Query `ghostii-sources` in Firestore for already-processed items
|
|
50
50
|
3. Filter to unprocessed items, pick the newest
|
|
51
51
|
4. Extract full article content from the item URL via `feed-parser.extractArticleContent()`
|
|
52
52
|
5. Pass extracted text as `sourceContent` to the Ghostii API (separate from the `description` prompt)
|
|
53
|
-
6. After publish, write a tracking doc to `ghostii-
|
|
53
|
+
6. After publish, write a tracking doc to `ghostii-sources/{hash}`
|
|
54
54
|
7. On failure (feed unreachable, unparseable, exhausted): fall back to `$app` behavior
|
|
55
55
|
|
|
56
56
|
### Feed item tracking
|
|
57
57
|
|
|
58
|
-
Collection: `ghostii-
|
|
58
|
+
Collection: `ghostii-sources` (consumer project Firestore)
|
|
59
59
|
|
|
60
60
|
```js
|
|
61
|
-
ghostii-
|
|
61
|
+
ghostii-sources/{sha256(feedUrl + '::' + itemId).slice(0,20)}: {
|
|
62
62
|
feedUrl: 'https://...',
|
|
63
63
|
itemId: 'guid-or-url',
|
|
64
64
|
itemUrl: 'https://...',
|
|
65
65
|
itemTitle: '...',
|
|
66
|
-
processedAt: Timestamp,
|
|
67
66
|
brandId: '...',
|
|
68
67
|
postUrl: '...',
|
|
69
68
|
postSlug: '...',
|
|
69
|
+
metadata: {
|
|
70
|
+
created: { timestamp: '...ISO...', timestampUNIX: 1234567890 },
|
|
71
|
+
updated: { timestamp: '...ISO...', timestampUNIX: 1234567890 },
|
|
72
|
+
},
|
|
70
73
|
metadata: { created: Timestamp },
|
|
71
74
|
}
|
|
72
75
|
```
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* 'https://...' — fetch URL content as prompt seed
|
|
13
13
|
* '<text>' — use directly as prompt seed
|
|
14
14
|
*
|
|
15
|
-
* Feed items are tracked in Firestore (`ghostii-
|
|
15
|
+
* Feed items are tracked in Firestore (`ghostii-sources`) so the same
|
|
16
16
|
* article is never processed twice. When a feed is unreachable or exhausted,
|
|
17
17
|
* the entry falls back to $app behavior.
|
|
18
18
|
*/
|
|
@@ -381,7 +381,7 @@ async function getProcessedItemIds(admin, feedUrl) {
|
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
const snapshot = await admin.firestore()
|
|
384
|
-
.collection('ghostii-
|
|
384
|
+
.collection('ghostii-sources')
|
|
385
385
|
.where('feedUrl', '==', feedUrl)
|
|
386
386
|
.select('itemId', 'itemUrl')
|
|
387
387
|
.get();
|
|
@@ -413,18 +413,20 @@ async function getProcessedItemIds(admin, feedUrl) {
|
|
|
413
413
|
*/
|
|
414
414
|
async function trackFeedItem(admin, { feedUrl, item, brandId, postUrl, postSlug }) {
|
|
415
415
|
const docId = feedItemHash(feedUrl, item.id || item.url);
|
|
416
|
+
const nowISO = new Date().toISOString();
|
|
417
|
+
const nowUNIX = Math.round(Date.now() / 1000);
|
|
416
418
|
|
|
417
|
-
await admin.firestore().doc(`ghostii-
|
|
419
|
+
await admin.firestore().doc(`ghostii-sources/${docId}`).set({
|
|
418
420
|
feedUrl: feedUrl,
|
|
419
421
|
itemId: item.id || item.url,
|
|
420
422
|
itemUrl: item.url,
|
|
421
423
|
itemTitle: item.title,
|
|
422
|
-
processedAt: admin.firestore.FieldValue.serverTimestamp(),
|
|
423
424
|
brandId: brandId,
|
|
424
425
|
postUrl: postUrl || null,
|
|
425
426
|
postSlug: postSlug || null,
|
|
426
427
|
metadata: {
|
|
427
|
-
created:
|
|
428
|
+
created: { timestamp: nowISO, timestampUNIX: nowUNIX },
|
|
429
|
+
updated: { timestamp: nowISO, timestampUNIX: nowUNIX },
|
|
428
430
|
},
|
|
429
431
|
});
|
|
430
432
|
}
|
|
@@ -176,7 +176,7 @@ module.exports = {
|
|
|
176
176
|
postSlug: 'test-article',
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
-
const doc = await admin.firestore().doc(`ghostii-
|
|
179
|
+
const doc = await admin.firestore().doc(`ghostii-sources/${docId}`).get();
|
|
180
180
|
assert.ok(doc.exists, 'tracking doc created');
|
|
181
181
|
|
|
182
182
|
const data = doc.data();
|
|
@@ -188,6 +188,13 @@ module.exports = {
|
|
|
188
188
|
assert.equal(data.postUrl, 'https://test-brand.com/blog/test-article');
|
|
189
189
|
assert.equal(data.postSlug, 'test-article');
|
|
190
190
|
assert.ok(data.metadata, 'has metadata object');
|
|
191
|
+
assert.ok(data.metadata.created, 'has metadata.created');
|
|
192
|
+
assert.ok(data.metadata.updated, 'has metadata.updated');
|
|
193
|
+
assert.equal(typeof data.metadata.created.timestamp, 'string', 'created.timestamp is ISO string');
|
|
194
|
+
assert.equal(typeof data.metadata.created.timestampUNIX, 'number', 'created.timestampUNIX is number');
|
|
195
|
+
assert.equal(typeof data.metadata.updated.timestamp, 'string', 'updated.timestamp is ISO string');
|
|
196
|
+
assert.equal(typeof data.metadata.updated.timestampUNIX, 'number', 'updated.timestampUNIX is number');
|
|
197
|
+
assert.ok(!data.processedAt, 'no legacy processedAt field');
|
|
191
198
|
},
|
|
192
199
|
},
|
|
193
200
|
|
|
@@ -26,22 +26,15 @@ const TEMP_DIR = path.join(BEM_ROOT, '.temp', 'ghostii-feed', `run-${new Date().
|
|
|
26
26
|
|
|
27
27
|
// --- Real feed URLs for extended tests ---
|
|
28
28
|
// Chosen for stability and confirmed to work with wonderful-fetch.
|
|
29
|
+
// Includes generic tech feeds + marketing/social feeds used by OMEGA consumers.
|
|
29
30
|
const REAL_FEEDS = [
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
url: 'https://techcrunch.com/feed/',
|
|
38
|
-
format: 'rss',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'Politico',
|
|
42
|
-
url: 'https://rss.politico.com/politics-news.xml',
|
|
43
|
-
format: 'rss',
|
|
44
|
-
},
|
|
31
|
+
{ name: 'Ars Technica', url: 'https://feeds.arstechnica.com/arstechnica/technology-lab', format: 'rss' },
|
|
32
|
+
{ name: 'TechCrunch', url: 'https://techcrunch.com/feed/', format: 'rss' },
|
|
33
|
+
{ name: 'Social Media Examiner', url: 'https://www.socialmediaexaminer.com/feed/', format: 'rss' },
|
|
34
|
+
{ name: 'Hootsuite Blog', url: 'https://blog.hootsuite.com/feed/', format: 'rss' },
|
|
35
|
+
{ name: 'Sprout Social Insights', url: 'https://sproutsocial.com/insights/feed/', format: 'rss' },
|
|
36
|
+
{ name: 'Buffer Resources', url: 'https://buffer.com/resources/feed/', format: 'rss' },
|
|
37
|
+
{ name: 'Digiday', url: 'https://digiday.com/feed/', format: 'rss' },
|
|
45
38
|
];
|
|
46
39
|
|
|
47
40
|
/**
|
|
@@ -333,7 +326,7 @@ module.exports = {
|
|
|
333
326
|
|
|
334
327
|
// Verify the doc was stored with the expected hash ID
|
|
335
328
|
const expectedDocId = feedItemHash(realFeedUrl, firstItem.id || firstItem.url);
|
|
336
|
-
const doc = await admin.firestore().doc(`ghostii-
|
|
329
|
+
const doc = await admin.firestore().doc(`ghostii-sources/${expectedDocId}`).get();
|
|
337
330
|
|
|
338
331
|
assert.ok(doc.exists, 'tracking doc exists at expected hash-based ID');
|
|
339
332
|
|
|
@@ -375,11 +368,11 @@ module.exports = {
|
|
|
375
368
|
saveArtifact(`feed-${feed.name.toLowerCase().replace(/\s+/g, '-')}-raw.xml`, text);
|
|
376
369
|
}
|
|
377
370
|
|
|
378
|
-
// At least
|
|
371
|
+
// At least 5 of 7 feeds should parse successfully
|
|
379
372
|
const successful = results.filter((r) => r.items > 0);
|
|
380
373
|
assert.ok(
|
|
381
|
-
successful.length >=
|
|
382
|
-
`at least
|
|
374
|
+
successful.length >= 5,
|
|
375
|
+
`at least 5 of ${REAL_FEEDS.length} real feeds parsed successfully: ${JSON.stringify(results)}`,
|
|
383
376
|
);
|
|
384
377
|
|
|
385
378
|
// Save summary
|