Set-StrictMode -Version Latest function Convert-ToValueArray { param($Value) if ($null -eq $Value) { return @() } if ($Value -is [System.Array]) { return @($Value) } return @($Value) } function Get-ObjectPropertyValue { param( $Value, [Parameter(Mandatory = $true)] [string] $Name ) if ($null -eq $Value) { return $null } if ($Value -is [System.Collections.IDictionary]) { if ($Value.Contains($Name)) { return $Value[$Name] } return $null } $property = $Value.PSObject.Properties[$Name] if ($null -eq $property) { return $null } return $property.Value } function Get-ObjectPropertyString { param( $Value, [Parameter(Mandatory = $true)] [string] $Name ) $value = Get-ObjectPropertyValue -Value $Value -Name $Name if ($null -eq $value) { return $null } $text = [string]$value if ([string]::IsNullOrWhiteSpace($text)) { return $null } return $text } function Format-RelativeTime { param( [string] $Value, [Parameter(Mandatory = $true)] [DateTime] $Now ) if ([string]::IsNullOrWhiteSpace($Value)) { return $null } $timestamp = [DateTime]::MinValue if (-not [DateTime]::TryParse($Value, [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal, [ref]$timestamp)) { return $null } $timestampUtc = $timestamp.ToUniversalTime() $delta = $timestampUtc - $Now.ToUniversalTime() $future = $delta.TotalSeconds -gt 0 $seconds = [Math]::Abs($delta.TotalSeconds) if ($seconds -lt 45) { return "just now" } if ($seconds -lt 5400) { $minutes = [Math]::Max(1, [int][Math]::Round($seconds / 60)) $unit = if ($minutes -eq 1) { "minute" } else { "minutes" } $phrase = "$minutes $unit" } elseif ($seconds -lt 129600) { $hours = [Math]::Max(1, [int][Math]::Round($seconds / 3600)) $unit = if ($hours -eq 1) { "hour" } else { "hours" } $phrase = "$hours $unit" } elseif ($seconds -lt 1814400) { $days = [Math]::Max(1, [int][Math]::Round($seconds / 86400)) $unit = if ($days -eq 1) { "day" } else { "days" } $phrase = "$days $unit" } elseif ($seconds -lt 6048000) { $weeks = [Math]::Max(1, [int][Math]::Round($seconds / 604800)) $unit = if ($weeks -eq 1) { "week" } else { "weeks" } $phrase = "$weeks $unit" } elseif ($seconds -lt 47304000) { $months = [Math]::Max(1, [int][Math]::Round($seconds / 2592000)) $unit = if ($months -eq 1) { "month" } else { "months" } $phrase = "$months $unit" } else { $years = [Math]::Max(1, [int][Math]::Round($seconds / 31536000)) $unit = if ($years -eq 1) { "year" } else { "years" } $phrase = "$years $unit" } if ($future) { return "in $phrase" } return "$phrase ago" } function Project-RelativeTimestamp { param( $Value, [string] $Name, [DateTime] $Now ) return Format-RelativeTime -Value (Get-ObjectPropertyString -Value $Value -Name $Name) -Now $Now } function Project-RecentHistoryForRumination { param($History, [DateTime] $Now) $messages = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $History -Name "messages")) return @{ source = "discord_history" messageCount = [int]$messages.Count messages = @( $messages | ForEach-Object { @{ id = Get-ObjectPropertyString -Value $_ -Name "id" channelId = Get-ObjectPropertyString -Value $_ -Name "channelId" authorId = Get-ObjectPropertyString -Value $_ -Name "authorId" authorName = Get-ObjectPropertyString -Value $_ -Name "authorName" isBot = [bool](Get-ObjectPropertyValue -Value $_ -Name "isBot") when = Project-RelativeTimestamp -Value $_ -Name "timestamp" -Now $Now content = Get-ObjectPropertyString -Value $_ -Name "content" } } ) } } function Project-RecentConversationTargetForRumination { param( $History, [string] $PersonaName, [string] $PersonaAvatarUrl ) $messages = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $History -Name "messages")) if ($messages.Count -eq 0) { return $null } $anchor = $null for ($index = $messages.Count - 1; $index -ge 0; $index--) { $candidate = $messages[$index] $channelId = Get-ObjectPropertyString -Value $candidate -Name "channelId" if ([string]::IsNullOrWhiteSpace($channelId)) { continue } if (-not [bool](Get-ObjectPropertyValue -Value $candidate -Name "isBot")) { $anchor = $candidate break } if ($null -eq $anchor) { $anchor = $candidate } } if ($null -eq $anchor) { return $null } $target = @{ mode = "channel" channelId = Get-ObjectPropertyString -Value $anchor -Name "channelId" replyToMessageId = Get-ObjectPropertyString -Value $anchor -Name "id" personaName = if ([string]::IsNullOrWhiteSpace($PersonaName)) { "Void" } else { $PersonaName.Trim() } reason = "Latest concrete recent-history conversation anchor. Use this instead of the configured public room when replying to that conversation." } if (-not [string]::IsNullOrWhiteSpace($PersonaAvatarUrl)) { $target.personaAvatarUrl = $PersonaAvatarUrl.Trim() } return $target } function Get-UrlsFromText { param([string] $Text) if ([string]::IsNullOrWhiteSpace($Text)) { return @() } return @( [regex]::Matches($Text, "https?://[^\s<>()]+") | ForEach-Object { $_.Value.TrimEnd(".", ",", "!", "?", ":", ";", ")", "]") } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } ) } function Resolve-GameCultUrlSource { param([string] $Url) if ([string]::IsNullOrWhiteSpace($Url)) { return $null } $uri = $null if (-not [System.Uri]::TryCreate($Url, [System.UriKind]::Absolute, [ref]$uri)) { return $null } $uriHost = $uri.Host.ToLowerInvariant() if ($uriHost -notin @("gamecult.org", "www.gamecult.org", "gamecult.games", "www.gamecult.games")) { return $null } $segments = @( $uri.AbsolutePath.Trim("/") -split "/" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { [System.Uri]::UnescapeDataString($_) } ) if ($segments.Count -lt 2 -or $segments[0].ToLowerInvariant() -ne "blog") { return @{ kind = "gamecult_url" host = $uriHost path = $uri.AbsolutePath } } $slugParts = @($segments | Select-Object -Skip 1) $relativePath = "GameCult/Blog/$($slugParts -join "/")" $markdownPath = "$relativePath.md" if ($slugParts.Count -gt 1) { $markdownPath = "$relativePath/index.md" } return @{ kind = "gamecult_blog" host = $uriHost slug = ($slugParts -join "/") repoName = "gamecult-site" path = $markdownPath sourceRef = "gamecult-site:$markdownPath" } } function Project-RecentRoomLinksForRumination { param( $History, [DateTime] $Now, [string] $MinTimestampInclusive, [object[]] $ReceiptReplyMessageIds ) $messages = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $History -Name "messages")) $answeredMessageIds = [System.Collections.Generic.HashSet[string]]::new() foreach ($replyMessageId in @(Convert-ToValueArray -Value $ReceiptReplyMessageIds)) { if (-not [string]::IsNullOrWhiteSpace([string]$replyMessageId)) { [void]$answeredMessageIds.Add([string]$replyMessageId) } } $minTimestampMs = $null if (-not [string]::IsNullOrWhiteSpace($MinTimestampInclusive)) { $parsedMinTimestamp = [DateTime]::MinValue if ([DateTime]::TryParse($MinTimestampInclusive, [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal, [ref]$parsedMinTimestamp)) { $minTimestampMs = $parsedMinTimestamp.ToUniversalTime() } } $items = @() foreach ($message in $messages) { if ([bool](Get-ObjectPropertyValue -Value $message -Name "isBot")) { continue } $messageId = Get-ObjectPropertyString -Value $message -Name "id" if (-not [string]::IsNullOrWhiteSpace($messageId) -and $answeredMessageIds.Contains($messageId)) { continue } $cursorAlreadyPassed = $false if ($null -ne $minTimestampMs) { $messageTimestamp = [DateTime]::MinValue $rawTimestamp = Get-ObjectPropertyString -Value $message -Name "timestamp" if ( [string]::IsNullOrWhiteSpace($rawTimestamp) -or -not [DateTime]::TryParse($rawTimestamp, [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal, [ref]$messageTimestamp) ) { continue } $messageTimestampUtc = $messageTimestamp.ToUniversalTime() if ($messageTimestampUtc -lt $minTimestampMs) { if ($minTimestampMs -gt $Now.ToUniversalTime().AddMinutes(1)) { continue } $cursorAlreadyPassed = $true } } $content = Get-ObjectPropertyString -Value $message -Name "content" $urls = @(Get-UrlsFromText -Text $content) if ($urls.Count -eq 0) { continue } foreach ($url in $urls) { $items += @{ kind = "naked_or_embedded_url" id = $messageId channelId = Get-ObjectPropertyString -Value $message -Name "channelId" authorId = Get-ObjectPropertyString -Value $message -Name "authorId" authorName = Get-ObjectPropertyString -Value $message -Name "authorName" when = Project-RelativeTimestamp -Value $message -Name "timestamp" -Now $Now content = $content url = $url resolvedSource = Resolve-GameCultUrlSource -Url $url carryForwardReason = if ($cursorAlreadyPassed) { "safety cursor already reviewed this message, but no speech receipt answers the URL object yet" } else { "at or after current reviewed cursor" } attentionContract = "A human posting a naked URL is still presenting an object to inspect. Treat it as room context, not as empty speech." } } } return @{ source = "recent_discord_url_scan" itemCount = [int]$items.Count lookback = Get-ObjectPropertyValue -Value $History -Name "hours" items = $items } } function Project-OpenCasesForRumination { param($Cases, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Cases) | Where-Object { -not (Test-RuminationCaseTerminal -Status (Get-ObjectPropertyString -Value $_ -Name "status")) } | ForEach-Object { @{ sourceMessageId = Get-ObjectPropertyString -Value $_ -Name "sourceMessageId" status = Get-ObjectPropertyString -Value $_ -Name "status" summary = Get-ObjectPropertyString -Value $_ -Name "summary" authorName = Get-ObjectPropertyString -Value $_ -Name "authorName" channelId = Get-ObjectPropertyString -Value $_ -Name "channelId" messageUrl = Get-ObjectPropertyString -Value $_ -Name "messageUrl" whyItMatters = Get-ObjectPropertyString -Value $_ -Name "whyItMatters" created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now lastTouched = Project-RelativeTimestamp -Value $_ -Name "lastTouchedAt" -Now $Now resolved = Project-RelativeTimestamp -Value $_ -Name "resolvedAt" -Now $Now tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags")) } } ) } function Test-RuminationCaseTerminal { param([string] $Status) return @("answered", "resolved", "closed", "retired", "dropped").Contains($Status) } function Project-SpeechReceiptsForRumination { param($Receipts, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Receipts) | ForEach-Object { @{ receiptKey = Get-ObjectPropertyString -Value $_ -Name "receiptKey" candidateInterventionId = Get-ObjectPropertyString -Value $_ -Name "candidateInterventionId" sent = Project-RelativeTimestamp -Value $_ -Name "sentAt" -Now $Now mode = Get-ObjectPropertyString -Value $_ -Name "mode" transport = Get-ObjectPropertyString -Value $_ -Name "transport" channelId = Get-ObjectPropertyString -Value $_ -Name "channelId" replyToMessageId = Get-ObjectPropertyString -Value $_ -Name "replyToMessageId" preview = Get-ObjectPropertyString -Value $_ -Name "preview" } } ) } function Project-MemoriesForRumination { param($Memories, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Memories) | ForEach-Object { @{ memoryId = Get-ObjectPropertyString -Value $_ -Name "memoryId" kind = Get-ObjectPropertyString -Value $_ -Name "kind" target = Get-ObjectPropertyValue -Value $_ -Name "target" summary = Get-ObjectPropertyString -Value $_ -Name "summary" claim = Get-ObjectPropertyString -Value $_ -Name "claim" question = Get-ObjectPropertyString -Value $_ -Name "question" tension = Get-ObjectPropertyString -Value $_ -Name "tension" actionImplication = Get-ObjectPropertyString -Value $_ -Name "actionImplication" created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now updated = Project-RelativeTimestamp -Value $_ -Name "updatedAt" -Now $Now retired = Project-RelativeTimestamp -Value $_ -Name "retiredAt" -Now $Now anchorRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "anchorRefs")) evidenceRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "evidenceRefs")) tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags")) } } ) } function Project-IncubationForRumination { param($Threads, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Threads) | ForEach-Object { @{ threadId = Get-ObjectPropertyString -Value $_ -Name "threadId" target = Get-ObjectPropertyValue -Value $_ -Name "target" topic = Get-ObjectPropertyString -Value $_ -Name "topic" summary = Get-ObjectPropertyString -Value $_ -Name "summary" supportMemoryIds = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "supportMemoryIds")) anchorRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "anchorRefs")) evidenceRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "evidenceRefs")) maturation = Get-ObjectPropertyValue -Value $_ -Name "maturation" noveltyToRoom = Get-ObjectPropertyValue -Value $_ -Name "noveltyToRoom" noveltyToSelf = Get-ObjectPropertyValue -Value $_ -Name "noveltyToSelf" desireToSpeak = Get-ObjectPropertyValue -Value $_ -Name "desireToSpeak" saturationScore = Get-ObjectPropertyValue -Value $_ -Name "saturationScore" status = Get-ObjectPropertyString -Value $_ -Name "status" created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now updated = Project-RelativeTimestamp -Value $_ -Name "updatedAt" -Now $Now } } ) } function Project-InterventionsForRumination { param($Interventions, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Interventions) | ForEach-Object { @{ interventionId = Get-ObjectPropertyString -Value $_ -Name "interventionId" kind = Get-ObjectPropertyString -Value $_ -Name "kind" status = Get-ObjectPropertyString -Value $_ -Name "status" target = Get-ObjectPropertyValue -Value $_ -Name "target" summary = Get-ObjectPropertyString -Value $_ -Name "summary" draft = Get-ObjectPropertyString -Value $_ -Name "draft" priority = Get-ObjectPropertyValue -Value $_ -Name "priority" mustEventuallyShare = Get-ObjectPropertyValue -Value $_ -Name "mustEventuallyShare" created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now updated = Project-RelativeTimestamp -Value $_ -Name "updatedAt" -Now $Now spoken = Project-RelativeTimestamp -Value $_ -Name "spokenAt" -Now $Now retired = Project-RelativeTimestamp -Value $_ -Name "retiredAt" -Now $Now tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags")) } } ) } function Project-PublicSpeechTargetForRumination { param( [string] $ChannelId, [string] $PersonaName, [string] $PersonaAvatarUrl ) if ([string]::IsNullOrWhiteSpace($ChannelId)) { return $null } $target = @{ mode = "channel" channelId = $ChannelId.Trim() personaName = if ([string]::IsNullOrWhiteSpace($PersonaName)) { "Void" } else { $PersonaName.Trim() } reason = "Configured public room for Void to present self-authored public artifacts and other ripe public thoughts." } if (-not [string]::IsNullOrWhiteSpace($PersonaAvatarUrl)) { $target.personaAvatarUrl = $PersonaAvatarUrl.Trim() } return $target } function Project-AgencyPressureForRumination { param($Pressures, [DateTime] $Now) return @( @(Convert-ToValueArray -Value $Pressures) | ForEach-Object { @{ pressureId = Get-ObjectPropertyString -Value $_ -Name "pressureId" kind = Get-ObjectPropertyString -Value $_ -Name "kind" status = Get-ObjectPropertyString -Value $_ -Name "status" target = Get-ObjectPropertyValue -Value $_ -Name "target" summary = Get-ObjectPropertyString -Value $_ -Name "summary" claim = Get-ObjectPropertyString -Value $_ -Name "claim" question = Get-ObjectPropertyString -Value $_ -Name "question" tension = Get-ObjectPropertyString -Value $_ -Name "tension" actionImplication = Get-ObjectPropertyString -Value $_ -Name "actionImplication" intensity = Get-ObjectPropertyValue -Value $_ -Name "intensity" created = Project-RelativeTimestamp -Value $_ -Name "createdAt" -Now $Now updated = Project-RelativeTimestamp -Value $_ -Name "updatedAt" -Now $Now resolved = Project-RelativeTimestamp -Value $_ -Name "resolvedAt" -Now $Now retired = Project-RelativeTimestamp -Value $_ -Name "retiredAt" -Now $Now anchorRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "anchorRefs")) evidenceRefs = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "evidenceRefs")) sourceMemoryIds = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "sourceMemoryIds")) tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags")) } } ) } function Project-ScheduledRuntimeForRumination { param($Runtime, [DateTime] $Now) $sleepCycle = Get-ObjectPropertyValue -Value $Runtime -Name "sleepCycle" $speakingPressure = Get-ObjectPropertyValue -Value $Runtime -Name "speakingPressure" $lastRuns = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Runtime -Name "lastRuns")) return @{ sleepCycle = @{ isNapping = [bool](Get-ObjectPropertyValue -Value $sleepCycle -Name "isNapping") currentNapStarted = Project-RelativeTimestamp -Value $sleepCycle -Name "currentNapStartedAt" -Now $Now currentNapEnds = Project-RelativeTimestamp -Value $sleepCycle -Name "currentNapEndsAt" -Now $Now nextNapStarts = Project-RelativeTimestamp -Value $sleepCycle -Name "nextNapStartsAt" -Now $Now activeDreamThemes = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $sleepCycle -Name "activeDreamThemes")) } speakingPressure = @{ needToSpeak = Get-ObjectPropertyValue -Value $speakingPressure -Name "needToSpeak" confessionPressure = Get-ObjectPropertyValue -Value $speakingPressure -Name "confessionPressure" noveltyPressure = Get-ObjectPropertyValue -Value $speakingPressure -Name "noveltyPressure" recentSpeechDamping = Get-ObjectPropertyValue -Value $speakingPressure -Name "recentSpeechDamping" lastSpoke = Project-RelativeTimestamp -Value $speakingPressure -Name "lastSpokeAt" -Now $Now lastHerald = Project-RelativeTimestamp -Value $speakingPressure -Name "lastHeraldAt" -Now $Now } lastRuns = @( $lastRuns | ForEach-Object { @{ runner = Get-ObjectPropertyString -Value $_ -Name "runner" ran = Project-RelativeTimestamp -Value $_ -Name "ranAt" -Now $Now summary = Get-ObjectPropertyString -Value $_ -Name "summary" } } ) } } function Project-CursorForRumination { param($Cursor, [DateTime] $Now) return @{ lastReviewedMessageId = Get-ObjectPropertyString -Value $Cursor -Name "lastReviewedMessageId" lastReviewed = Project-RelativeTimestamp -Value $Cursor -Name "lastReviewedTimestamp" -Now $Now } } function Get-VoidAuthoredArtifactsFromContentHints { param($ContentHints) return @( @(Convert-ToValueArray -Value $ContentHints) | Where-Object { $frontmatter = Get-ObjectPropertyValue -Value $_ -Name "frontmatter" $author = Get-ObjectPropertyValue -Value $frontmatter -Name "author" @(Convert-ToValueArray -Value $author) | Where-Object { ([string]$_).Trim().ToLowerInvariant() -eq "void" } | Select-Object -First 1 } | ForEach-Object { $frontmatter = Get-ObjectPropertyValue -Value $_ -Name "frontmatter" @{ path = Get-ObjectPropertyString -Value $_ -Name "path" title = Get-ObjectPropertyString -Value $frontmatter -Name "title" description = Get-ObjectPropertyString -Value $frontmatter -Name "description" socialDeck = Get-ObjectPropertyString -Value $frontmatter -Name "socialDeck" } } ) } function Project-RepoCommitForRumination { param($Commit, [DateTime] $Now) if ($null -eq $Commit) { return $null } $hash = Get-ObjectPropertyString -Value $Commit -Name "hash" $contentHints = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "contentHints")) $doctrineDocuments = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "doctrineDocuments")) $authoredPublicArtifacts = @(Get-VoidAuthoredArtifactsFromContentHints -ContentHints $contentHints) return @{ hash = if ($hash -and $hash.Length -gt 12) { $hash.Substring(0, 12) } else { $hash } committed = Project-RelativeTimestamp -Value $Commit -Name "committedAt" -Now $Now author = Get-ObjectPropertyString -Value $Commit -Name "author" subject = Get-ObjectPropertyString -Value $Commit -Name "subject" diffstat = Get-ObjectPropertyValue -Value $Commit -Name "diffstat" changedPaths = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "changedPaths")) contentHints = $contentHints doctrineDocuments = $doctrineDocuments authoredPublicArtifacts = $authoredPublicArtifacts } } function Test-RepoHasDoctrineDocument { param($Repo) $commits = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Repo -Name "commits")) foreach ($commit in $commits) { $doctrineDocuments = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $commit -Name "doctrineDocuments")) if ($doctrineDocuments.Count -gt 0) { return $true } } return $false } function Test-RepoIsMovementCoverageLeader { param($Repo) $recent = Get-ObjectPropertyValue -Value $Repo -Name "recentCommitCount" $windowRecent = Get-ObjectPropertyValue -Value $Repo -Name "windowRecentCommitCount" if ((Test-RepoHasDoctrineDocument -Repo $Repo) -or (Test-RepoHasVoidAuthoredArtifact -Repo $Repo)) { return $true } return ( $null -ne $recent -and [int]$recent -ge 3 ) -or ( $null -ne $windowRecent -and [int]$windowRecent -ge 6 ) } function Test-RepoHasVoidAuthoredArtifact { param($Repo) $commits = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Repo -Name "commits")) foreach ($commit in $commits) { $contentHints = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $commit -Name "contentHints")) if (@(Get-VoidAuthoredArtifactsFromContentHints -ContentHints $contentHints).Count -gt 0) { return $true } } return $false } function Select-RuminationRepoActivity { param($RepoActivity, [DateTime] $Now) if ($null -eq $RepoActivity) { return $null } $status = Get-ObjectPropertyString -Value $RepoActivity -Name "status" if ($status -eq "failed") { return @{ status = "failed" error = Get-ObjectPropertyString -Value $RepoActivity -Name "error" } } $repos = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $RepoActivity -Name "repos")) $freshRepos = @( $repos | Where-Object { $recent = Get-ObjectPropertyValue -Value $_ -Name "recentCommitCount" $repoStatus = Get-ObjectPropertyString -Value $_ -Name "status" $null -ne $recent -and $repoStatus -eq "ok" -and [int]$recent -gt 0 } | Sort-Object -Property @{ Expression = { if (Test-RepoHasVoidAuthoredArtifact -Repo $_) { 0 } else { 1 } } }, @{ Expression = { if (Test-RepoIsMovementCoverageLeader -Repo $_) { 0 } else { 1 } } }, @{ Expression = { if (Test-RepoHasDoctrineDocument -Repo $_) { 0 } else { 1 } } }, @{ Expression = { -[int](Get-ObjectPropertyValue -Value $_ -Name "recentCommitCount") } } | Select-Object -First 12 ) $projectedRepos = @( $freshRepos | ForEach-Object { @{ repoName = Get-ObjectPropertyString -Value $_ -Name "repoName" branch = Get-ObjectPropertyString -Value $_ -Name "branch" status = Get-ObjectPropertyString -Value $_ -Name "status" recentCommitCount = Get-ObjectPropertyValue -Value $_ -Name "recentCommitCount" windowRecentCommitCount = Get-ObjectPropertyValue -Value $_ -Name "windowRecentCommitCount" suppressedRecentCommitCount = Get-ObjectPropertyValue -Value $_ -Name "suppressedRecentCommitCount" latestCommit = Project-RepoCommitForRumination -Commit (Get-ObjectPropertyValue -Value $_ -Name "latestCommit") -Now $Now commits = @( @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "commits")) | ForEach-Object { Project-RepoCommitForRumination -Commit $_ -Now $Now } ) } } ) return @{ generated = Project-RelativeTimestamp -Value $RepoActivity -Name "generatedAt" -Now $Now cursorMode = Get-ObjectPropertyString -Value $RepoActivity -Name "cursorMode" summary = if ($projectedRepos.Count -gt 0) { "New tracked repo commits crossed the saved repo-activity cursor." } else { "No new tracked repo commits crossed the saved repo-activity cursor." } movementCoverage = @{ job = "Keep people apace of meaningful movement, not merely the latest commit. Coverage priority follows where the current movement is: heavy fresh commit volume, doctrine-bearing documents, authored public artifacts, or operator-named active lanes." freshMovementLeaders = @( $projectedRepos | Where-Object { Test-RepoIsMovementCoverageLeader -Repo $_ } | ForEach-Object { @{ repoName = Get-ObjectPropertyString -Value $_ -Name "repoName" recentCommitCount = Get-ObjectPropertyValue -Value $_ -Name "recentCommitCount" windowRecentCommitCount = Get-ObjectPropertyValue -Value $_ -Name "windowRecentCommitCount" hasDoctrineDocuments = Test-RepoHasDoctrineDocument -Repo $_ latestSubject = Get-ObjectPropertyString -Value (Get-ObjectPropertyValue -Value $_ -Name "latestCommit") -Name "subject" } } ) currentExamples = @("Aetheria", "AetheriaLore", "CultLib", "Eve", "Odin") coverageRule = "If a fresh movement leader has doctrine, authority, runtime, client, CultMesh, or public-artifact work, read the doctrineDocuments/contentHints and either queue a coverage candidate or preserve a short-term memory explaining the new direction. If tomorrow's movement is Fensalir, Mimir, or another lane, it earns the same treatment. Do not let a repo cursor advance for movement the pass did not actually handle." } freshRepos = $projectedRepos } }