feat(agentctl): federation stability and hub ingest journeys
Add runFederationStabilitySoakJourney and runFederationHubIngestJourney for Phase 650 federation mesh validation. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -29,6 +29,8 @@ Both modes use **`pearcord-ui-flow` `dispatchUiMessage`** — the same handler a
|
||||
|
||||
**v0.8.617 (Phase 645):** `runLargeGuildMemberJourney` (seed 120+ members → partial bundle → member page request); IPC `request-member-page`. Bundle: `npm run test:phase645-large-guild-mesh`. See [LARGE_GUILD_MESH.md](../../docs/LARGE_GUILD_MESH.md).
|
||||
|
||||
**v0.8.622 (Phase 650):** `runFederationStabilitySoakJourney` (`PEARCORD_FEDERATION_SOAK_MS`), `runFederationHubIngestJourney`; async `exportFederationSyncDiagnostics`. Bundle: `npm run test:phase650-live-federation-mesh`.
|
||||
|
||||
**v0.8.621 (Phase 649):** `runFederationMeshSoakJourney` (multi-guild + listing refresh + federation export); `exportFederationSyncDiagnostics`. Bundle: `npm run test:phase649-federation-discovery`. See [FEDERATION_MESH.md](../../docs/FEDERATION_MESH.md).
|
||||
|
||||
**v0.8.620 (Phase 648):** `runMultiGuildMeshSoakJourney` (two guilds, switch, multi export); `exportMultiGuildSyncDiagnostics`. Bundle: `npm run test:phase648-multi-guild-mesh`. See [MULTI_GUILD_MESH.md](../../docs/MULTI_GUILD_MESH.md).
|
||||
|
||||
+63
-2
@@ -1147,7 +1147,7 @@ class HeadlessSession {
|
||||
return this.platform.exportMultiGuildSyncDiagnostics()
|
||||
}
|
||||
|
||||
exportFederationSyncDiagnostics () {
|
||||
async exportFederationSyncDiagnostics () {
|
||||
if (!this.platform) throw new Error('headless not started')
|
||||
return this.platform.exportFederationSyncDiagnostics()
|
||||
}
|
||||
@@ -1187,11 +1187,72 @@ class HeadlessSession {
|
||||
syncMesh: false
|
||||
})
|
||||
}
|
||||
const federation = this.platform.exportFederationSyncDiagnostics()
|
||||
const federation = await this.platform.exportFederationSyncDiagnostics()
|
||||
await this.refreshView()
|
||||
return { ...multiOut, listingRefresh, federation }
|
||||
}
|
||||
|
||||
/** Journey: federation stability ticks (CI-safe; use PEARCORD_FEDERATION_SOAK_MS for long soak). */
|
||||
async runFederationStabilitySoakJourney (guildId) {
|
||||
if (!this.platform) throw new Error('headless not started')
|
||||
const gid = guildId || this.platform.guild?.guild?.id
|
||||
if (!gid) throw new Error('no guild')
|
||||
const soakMs = Math.max(
|
||||
3000,
|
||||
Number(process.env.PEARCORD_FEDERATION_SOAK_MS) || 12000
|
||||
)
|
||||
const deadline = Date.now() + soakMs
|
||||
const ticks = []
|
||||
while (Date.now() < deadline) {
|
||||
const scoped = await this.platform.refreshDiscoveryExploreScoped().catch(() => null)
|
||||
const health = await this.getGuildSyncHealth(gid)
|
||||
ticks.push({
|
||||
at: Date.now(),
|
||||
scoped,
|
||||
pending: !!health?.pending,
|
||||
peers: Number(health?.peers) || 0
|
||||
})
|
||||
await new Promise((r) => setTimeout(r, 2000))
|
||||
}
|
||||
const federation = await this.platform.exportFederationSyncDiagnostics()
|
||||
await this.refreshView()
|
||||
return { guildId: gid, soakMs, tickCount: ticks.length, ticks, federation }
|
||||
}
|
||||
|
||||
/** Journey: simulate remote hub post ingest on discovery mesh. */
|
||||
async runFederationHubIngestJourney () {
|
||||
if (!this.platform) throw new Error('headless not started')
|
||||
const g = await this.createGuild({ name: 'Hub Src', publicListing: true })
|
||||
const gid = g?.guild?.id || this.platform.guild?.guild?.id
|
||||
const ch = this.platform.activeChannelId
|
||||
if (!gid || !ch) throw new Error('guild/channel required')
|
||||
await this.sendGuildMessage(`hub-${Date.now()}`)
|
||||
const targetGuild = await this.createGuild({ name: 'Hub Sub' })
|
||||
const subGid = targetGuild?.guild?.id || this.platform.guild?.guild?.id
|
||||
const targetCh = this.platform.activeChannelId
|
||||
if (!subGid || !targetCh) throw new Error('subscriber guild required')
|
||||
await this.platform.announcementHub.subscribe({
|
||||
subscriberGuildId: subGid,
|
||||
sourceGuildId: gid,
|
||||
sourceChannelId: ch,
|
||||
targetChannelId: targetCh
|
||||
})
|
||||
const payload = {
|
||||
sourceGuildId: gid,
|
||||
sourceChannelId: ch,
|
||||
sourceGuildName: 'Hub Src',
|
||||
sourceChannelName: 'general',
|
||||
messageId: `hub-msg-${Date.now()}`,
|
||||
content: 'federation hub ingest smoke',
|
||||
authorName: 'Smoke',
|
||||
postedAt: Date.now()
|
||||
}
|
||||
const mirrored = await this.platform._ingestAnnouncementHubPost(payload)
|
||||
const federation = await this.platform.exportFederationSyncDiagnostics()
|
||||
await this.refreshView()
|
||||
return { payload, mirrored: mirrored?.length || 0, federation }
|
||||
}
|
||||
|
||||
/** Journey: mesh stability + partition heal + message + diagnostics (production soak). */
|
||||
async runProductionMeshSoakJourney (guildId) {
|
||||
if (!this.platform) throw new Error('headless not started')
|
||||
|
||||
Reference in New Issue
Block a user