2026-05-17 08:52:36 +01:00
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
}
2026-05-17 19:22:52 +01:00
if ( $Value -is [ System.Collections.IDictionary ] ) {
if ( $Value . Contains ( $Name ) ) {
return $Value [ $Name ]
}
return $null
}
2026-05-17 08:52:36 +01:00
$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 "
2026-05-17 08:52:36 +01:00
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
}
2026-06-09 05:50:01 +01:00
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
}
}
2026-05-17 08:52:36 +01:00
function Project-OpenCasesForRumination {
param ( $Cases , [ DateTime ] $Now )
return @ (
2026-05-18 03:43:20 +01:00
@ ( Convert-ToValueArray -Value $Cases ) |
Where-Object {
-not ( Test-RuminationCaseTerminal -Status ( Get-ObjectPropertyString -Value $_ -Name " status " ) )
} |
ForEach-Object {
2026-05-17 08:52:36 +01:00
@ {
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 " ) )
}
}
)
}
2026-05-18 03:43:20 +01:00
function Test-RuminationCaseTerminal {
param ( [ string ] $Status )
return @ ( " answered " , " resolved " , " closed " , " retired " , " dropped " ) . Contains ( $Status )
}
2026-05-17 08:52:36 +01:00
function Project-SpeechReceiptsForRumination {
param ( $Receipts , [ DateTime ] $Now )
return @ (
@ ( Convert-ToValueArray -Value $Receipts ) | ForEach-Object {
@ {
receiptKey = Get-ObjectPropertyString -Value $_ -Name " receiptKey "
2026-05-17 20:17:28 +01:00
candidateInterventionId = Get-ObjectPropertyString -Value $_ -Name " candidateInterventionId "
2026-05-17 08:52:36 +01:00
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
2026-05-17 13:25:55 +01:00
anchorRefs = @ ( Convert-ToValueArray -Value ( Get-ObjectPropertyValue -Value $_ -Name " anchorRefs " ) )
2026-05-17 08:52:36 +01:00
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 " ) )
2026-05-17 13:25:55 +01:00
anchorRefs = @ ( Convert-ToValueArray -Value ( Get-ObjectPropertyValue -Value $_ -Name " anchorRefs " ) )
2026-05-17 08:52:36 +01:00
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
2026-05-17 13:25:55 +01:00
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 " ) )
}
}
)
}
2026-05-17 08:52:36 +01:00
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 "
}
}
)
}
2026-05-17 08:52:36 +01:00
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 )
2026-05-17 08:52:36 +01:00
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-05-17 08:52:36 +01:00
}
}
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
}
2026-05-17 08:52:36 +01:00
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
2026-05-17 08:52:36 +01:00
)
$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. "
}
2026-05-17 08:52:36 +01:00
freshRepos = $projectedRepos
}
}