VoidBot/scripts/lib/void-rumination-context-projection.ps1

744 lines
28 KiB
PowerShell
Raw Normal View History

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"
2026-05-23 11:12:18 +01:00
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"
}
}
)
}
}
2026-05-23 11:12:18 +01:00
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"))
}
}
)
}
2026-05-18 04:27:17 +01:00
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
}
2026-05-17 13:02:13 +01:00
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"))
2026-05-17 13:02:13 +01:00
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
}
}
2026-05-18 04:27:17 +01:00
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"
2026-05-18 04:27:17 +01:00
$contentHints = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "contentHints"))
2026-07-08 21:44:02 +01:00
$doctrineDocuments = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $Commit -Name "doctrineDocuments"))
2026-05-18 04:27:17 +01:00
$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"))
2026-05-18 04:27:17 +01:00
contentHints = $contentHints
2026-07-08 21:44:02 +01:00
doctrineDocuments = $doctrineDocuments
2026-05-18 04:27:17 +01:00
authoredPublicArtifacts = $authoredPublicArtifacts
}
}
2026-07-08 21:44:02 +01:00
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
)
}
2026-05-18 04:27:17 +01:00
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
} |
2026-05-18 04:27:17 +01:00
Sort-Object -Property @{
Expression = { if (Test-RepoHasVoidAuthoredArtifact -Repo $_) { 0 } else { 1 } }
2026-07-08 21:44:02 +01:00
}, @{
Expression = { if (Test-RepoIsMovementCoverageLeader -Repo $_) { 0 } else { 1 } }
}, @{
Expression = { if (Test-RepoHasDoctrineDocument -Repo $_) { 0 } else { 1 } }
}, @{
Expression = { -[int](Get-ObjectPropertyValue -Value $_ -Name "recentCommitCount") }
2026-05-18 04:27:17 +01:00
} |
2026-07-08 21:44:02 +01:00
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." }
2026-07-08 21:44:02 +01:00
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
}
}