fix(agent99): select newest evidence by mtime
All checks were successful
CD Pipeline / select-latest-carrier (push) Successful in 47s
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 4m53s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 29s
CD Pipeline / build-and-deploy (push) Successful in 14m53s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 0s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 9s
CD Pipeline / revalidate-post-deploy-carrier (push) Successful in 47s
CD Pipeline / post-deploy-checks (push) Successful in 3m10s

This commit is contained in:
Your Name
2026-07-22 13:48:05 +08:00
parent 1c81f8afc6
commit 30e0be8987
2 changed files with 161 additions and 7 deletions

View File

@@ -242,9 +242,10 @@ function Get-AgentBoundedNewestFiles {
[int]$CandidateWindow = 32
)
# Agent99 evidence names contain a fixed sortable timestamp and are immutable.
# Keep only the lexically newest bounded window in a native .NET set so a
# large evidence directory never becomes a PowerShell object-sort workload.
# Evidence freshness is authoritative from filesystem UTC mtime. Some
# evidence names are stable aliases and are not chronologically sortable.
# Keep only the newest bounded window in a native .NET set so a large
# evidence directory never becomes a PowerShell object-sort workload.
$selected = [Collections.Generic.SortedSet[string]]::new(
[StringComparer]::OrdinalIgnoreCase
)
@@ -256,16 +257,25 @@ function Get-AgentBoundedNewestFiles {
[IO.SearchOption]::TopDirectoryOnly
)) {
$enumeratedCount += 1
$null = $selected.Add($candidatePath)
$candidate = [IO.FileInfo]::new($candidatePath)
$sortKey = (
$candidate.LastWriteTimeUtc.Ticks.ToString(
"D19",
[Globalization.CultureInfo]::InvariantCulture
) + "|" + $candidate.FullName
)
$null = $selected.Add($sortKey)
if ($selected.Count -gt $CandidateWindow) {
$null = $selected.Remove($selected.Min)
}
}
}
$selectedKeys = [string[]]@($selected)
[Array]::Reverse($selectedKeys)
return [pscustomobject]@{
files = @($selected | Sort-Object -Descending | ForEach-Object {
[IO.FileInfo]::new($_)
files = @($selectedKeys | ForEach-Object {
[IO.FileInfo]::new($_.Substring(20))
})
enumeratedCount = [int]$enumeratedCount
candidateWindow = [int]$CandidateWindow
@@ -685,7 +695,11 @@ function Get-EvidenceReadback {
$deferredCandidates.Count -eq $candidates.Count
)
$ageMinutes = if ($file) { [math]::Round(((Get-Date) - $file.LastWriteTime).TotalMinutes, 1) } else { $null }
$fresh = [bool]($file -and $ageMinutes -le $MaxAgeMinutes)
$fresh = [bool](
$file -and
$ageMinutes -ge 0 -and
$ageMinutes -le $MaxAgeMinutes
)
$freshnessHeadroomMinutes = if ($file) { [math]::Round($MaxAgeMinutes - $ageMinutes, 1) } else { $null }
$freshnessReserveReady = [bool](
$fresh -and

View File

@@ -156,6 +156,11 @@ def test_warning_receipt_is_visible_without_weakening_integrity_blockers() -> No
assert "deferredCandidateCount = [int]$deferredCandidates.Count" in source
assert "[IO.Directory]::EnumerateFiles(" in source
assert "[Collections.Generic.SortedSet[string]]::new(" in source
assert "$candidate.LastWriteTimeUtc.Ticks.ToString(" in source
assert "[Globalization.CultureInfo]::InvariantCulture" in source
assert "[IO.FileInfo]::new($_.Substring(20))" in source
assert "[Array]::Reverse($selectedKeys)" in source
assert "$ageMinutes -ge 0" in source
assert "$null = $selected.Remove($selected.Min)" in source
assert "candidateWindowExhausted = $candidateWindowExhausted" in source
assert "function Get-AgentControlLoopMaintenanceReadback" in source
@@ -278,6 +283,141 @@ Get-EvidenceReadback 'self_check' 'agent99-SelfCheck-*.json' 20 $true |
assert malformed_payload["healthy"] is False
@pytest.mark.parametrize(
("name", "pattern", "stale_alias", "fresh_file"),
[
(
"status",
"agent99-Status-*.json",
"agent99-Status-same-run-status-{index:02d}.json",
"agent99-Status-20260722-133422.json",
),
(
"performance",
"agent99-Perf-*.json",
"agent99-Perf-latest-summary-{index:02d}.json",
"agent99-Perf-20260722-133722.json",
),
],
)
def test_evidence_selection_uses_mtime_when_stable_aliases_sort_after_timestamps(
tmp_path: Path,
name: str,
pattern: str,
stale_alias: str,
fresh_file: str,
) -> None:
powershell = shutil.which("pwsh") or shutil.which("powershell")
if powershell is None:
pytest.skip("PowerShell runtime is not installed on this macOS verifier")
evidence = tmp_path / "evidence"
evidence.mkdir()
fresh = evidence / fresh_file
fresh.write_text(json.dumps({"mode": name}), encoding="utf-8")
base_time = fresh.stat().st_mtime
for index in range(40):
alias = evidence / stale_alias.format(index=index)
alias.write_text(json.dumps({"mode": name}), encoding="utf-8")
stale_time = base_time - 3600 - index
os.utime(alias, (stale_time, stale_time))
os.utime(fresh, (base_time, base_time))
agent_root = str(tmp_path).replace("'", "''")
command = f"""
{_evidence_function_source()}
$AgentRoot = '{agent_root}'
Get-EvidenceReadback '{name}' '{pattern}' 20 $true |
ConvertTo-Json -Depth 8 -Compress
"""
result = subprocess.run(
[powershell, "-NoProfile", "-NonInteractive", "-Command", command],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(
next(line for line in reversed(result.stdout.splitlines()) if line.startswith("{"))
)
assert payload["file"] == fresh.name
assert payload["fresh"] is True
assert payload["candidateWindowTruncated"] is True
def test_evidence_selection_rejects_future_mtime(tmp_path: Path) -> None:
powershell = shutil.which("pwsh") or shutil.which("powershell")
if powershell is None:
pytest.skip("PowerShell runtime is not installed on this macOS verifier")
evidence = tmp_path / "evidence"
evidence.mkdir()
future = evidence / "agent99-Status-20260722-140000.json"
future.write_text(json.dumps({"mode": "Status"}), encoding="utf-8")
future_time = future.stat().st_mtime + 120
os.utime(future, (future_time, future_time))
agent_root = str(tmp_path).replace("'", "''")
command = f"""
{_evidence_function_source()}
$AgentRoot = '{agent_root}'
Get-EvidenceReadback 'status' 'agent99-Status-*.json' 20 $true |
ConvertTo-Json -Depth 8 -Compress
"""
result = subprocess.run(
[powershell, "-NoProfile", "-NonInteractive", "-Command", command],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(
next(line for line in reversed(result.stdout.splitlines()) if line.startswith("{"))
)
assert payload["file"] == future.name
assert payload["ageMinutes"] < 0
assert payload["fresh"] is False
assert payload["usable"] is False
assert payload["healthy"] is False
def test_evidence_selection_equal_mtime_uses_ordinal_path_tie_break(
tmp_path: Path,
) -> None:
powershell = shutil.which("pwsh") or shutil.which("powershell")
if powershell is None:
pytest.skip("PowerShell runtime is not installed on this macOS verifier")
evidence = tmp_path / "evidence"
evidence.mkdir()
lower = evidence / "agent99-Status-equal-a.json"
higher = evidence / "agent99-Status-equal-z.json"
lower.write_text(json.dumps({"mode": "Status"}), encoding="utf-8")
higher.write_text(json.dumps({"mode": "Status"}), encoding="utf-8")
equal_time = lower.stat().st_mtime
os.utime(lower, (equal_time, equal_time))
os.utime(higher, (equal_time, equal_time))
agent_root = str(tmp_path).replace("'", "''")
command = f"""
{_evidence_function_source()}
$AgentRoot = '{agent_root}'
Get-EvidenceReadback 'status' 'agent99-Status-*.json' 20 $true |
ConvertTo-Json -Depth 8 -Compress
"""
result = subprocess.run(
[powershell, "-NoProfile", "-NonInteractive", "-Command", command],
check=False,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
payload = json.loads(
next(line for line in reversed(result.stdout.splitlines()) if line.startswith("{"))
)
assert payload["file"] == higher.name
def test_evidence_reserve_rejects_fresh_but_expiring_evidence_without_extending_slo(
tmp_path: Path,
) -> None: