[CLAUDE] Docs: closeout OUTWARD wave adap đợt-9/10 + vá drift skill-matrix H24
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m43s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m43s
Closeout 4 mục OUTWARD của wave S141-S142 (anh gật @S143): - adap-report 7/7 thư, đủ 5 trường REPORT-FORMAT LOCK, evidence đo thật - email hub báo-nấc (sha 6c94873f72e0, selftest_verify exit 0, log _index cùng lượt) - STAGE-2: 7 thư -> inbox/ai_infra/, _index 0 pending, cross-check 7/7 - squash K=8 wal: -> commit chốt Ngoài wave: agents/README skill-matrix thiếu 2 row H24 (drift S121) -> 15/17 thành 17/17. Nấc cao nhất khai được = executed-file/verified-pending-restart (trio CHƯA spawn). 2 phát hiện khai thẳng theo G-015 (chi tiết trong report + email): - whitelist `tools:` KHÔNG chặn ghi ở runtime: 6 vai read-only bị append Write+Edit - pull-lag do "watch broadcasts/inbox" canh nhầm chỗ TICK H24: counter 16->17 (S143), 3-điều-kiện OK-reachable, không fail-loud. Detector TOTAL 46 == baseline 46, 0 flag mới. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1356,6 +1356,164 @@ else {
|
||||
Write-Host (" H24-4 pending-flip flags = {0} [INFORM-only, LOW, NOT folded; 0 = designed first-run]" -f $h244Flags) -ForegroundColor DarkGray
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# H24-5 - counter-ancestor-classify (INFORM-only, NEW; count SEPARATE, NOT in TOTAL)
|
||||
#
|
||||
# WHAT: a static, read-only replay of the .session-counter.json contract v2 clause
|
||||
# "fail_loud_on_regress" (adopted S141, hub template 58e28bae C-1). The RUNTIME owner of
|
||||
# that clause is the /session-start 2.1.8(b) ritual -- this detector does NOT replace it
|
||||
# and could not: the ritual is the half with teeth (it FAIL-LOUDs to the owner mid-run).
|
||||
# What a detector adds is a between-sessions eye that can be pointed at ANY tree.
|
||||
#
|
||||
# THE CLASSIFICATION (contract order, verbatim shape):
|
||||
# 1. last_ticked_head reachable from HEAD (git merge-base --is-ancestor, exit 0)
|
||||
# -> OK-reachable. No flag.
|
||||
# 2. NOT reachable BUT the object still EXISTS (git cat-file -t = 'commit')
|
||||
# -> SQUASH-BENIGN: a trace line, ZERO flags. This is the expected steady state --
|
||||
# every closeout squashes the session's wal: commits, so the head captured at the
|
||||
# previous tick routinely drops out of history. The 3 historical FAIL-LOUDs recorded
|
||||
# in history[] (S127/S133/S139) all reclassify here; that IS contract v2's purpose.
|
||||
# 3. object MISSING (cat-file fails) -> flag object-missing. The anti-tamper half: a
|
||||
# head that no longer exists AT ALL was not squashed out, it was rewritten away.
|
||||
# [M1] counter-regress, an INDEPENDENT axis and DEFAULT-ON: the stored counter must
|
||||
# never sit BELOW a counter at which an audit already ran (last_audit.light_at_counter
|
||||
# / deep_at_counter). Below = the file was rolled back or hand-edited, and a silent
|
||||
# reset would hide the cadence gap -- the exact thing H24 exists to catch.
|
||||
#
|
||||
# [M2] DAY-ONE = INFORM-SINK. Every H24-5 hit goes through Write-InformFlag: its own
|
||||
# [DETECTOR] line, counted in $script:InformCount, NEVER folded into TOTAL. Same
|
||||
# reasoning as C6/H24-4 (see the Write-InformFlag banner): a brand-new net must not move
|
||||
# the audited baseline on day one, in EITHER direction. No teeth are lost by this --
|
||||
# 2.1.8(b) still FAIL-LOUDs at runtime, independently of this script. Fold + severity
|
||||
# raise is a post-triage, owner-gated call.
|
||||
#
|
||||
# git-based => honours -RepoRoot, so fault-injection on a temp tree isolates completely
|
||||
# (mirror of H25-closeout). Every probe is exit-code checked and never throws: a RepoRoot
|
||||
# that is not a work-tree, or a file that was never ticked, SKIPs WITH A REASON and the
|
||||
# section summary says MEASURED NOTHING -- a 0 printed after a skip is not a green.
|
||||
#
|
||||
# READ-ENCODING (bug E-010/S130): the JSON is read via ReadAllBytes + UTF8.GetString and
|
||||
# NOT Get-Content without -Encoding. A BOM-less UTF-8 file decoded through the ANSI
|
||||
# codepage yields a DIFFERENT document than the one on disk, and a classifier that reads
|
||||
# a different document is worse than no classifier.
|
||||
# ---------------------------------------------------------------------------
|
||||
Write-Section 'H24-5 - counter-ancestor-classify (INFORM-only)'
|
||||
|
||||
$h245Flags = 0
|
||||
$h245Measured = $false
|
||||
$counterP = Join-Path $RepoRoot '.claude\governance\.session-counter.json'
|
||||
if (-not (Test-Path $counterP)) {
|
||||
Write-Host ' [skip] no .claude/governance/.session-counter.json - no tick state to classify' -ForegroundColor DarkGray
|
||||
}
|
||||
elseif (-not (Test-Path (Join-Path $RepoRoot '.git'))) {
|
||||
Write-Host ' [skip] RepoRoot is not a git work-tree - cannot classify head reachability' -ForegroundColor DarkGray
|
||||
}
|
||||
else {
|
||||
# BOM-STRIP (measured S142, NOT theoretical): the file on disk starts EF BB BF -- it is
|
||||
# written by PS 5.1 ConvertTo-Json + Out-File -Encoding utf8, which always emits a BOM.
|
||||
# [Text.Encoding]::UTF8.GetString does NOT consume that BOM (unlike Get-Content or a
|
||||
# StreamReader with detectEncodingFromByteOrderMarks), so the string begins with a
|
||||
# literal U+FEFF and ConvertFrom-Json dies with "Invalid JSON primitive: .".
|
||||
# First run of this detector on the REAL repo raised counter-unparseable on a file that
|
||||
# parses fine -- i.e. the encoding-safe read prescribed to dodge bug E-010 opened a
|
||||
# SECOND failure mode of its own. Both must be handled: read bytes as UTF-8 (never let
|
||||
# the ANSI codepage decode it) AND drop a leading BOM. Written as a code point so this
|
||||
# .ps1 stays ASCII-only (gotcha #30).
|
||||
$ctr = $null
|
||||
try {
|
||||
$ctrJson = [Text.Encoding]::UTF8.GetString([IO.File]::ReadAllBytes($counterP))
|
||||
$ctrJson = $ctrJson.TrimStart([char]0xFEFF)
|
||||
$ctr = ($ctrJson | ConvertFrom-Json)
|
||||
}
|
||||
catch { $ctr = $null }
|
||||
|
||||
if ($null -eq $ctr) {
|
||||
Write-InformFlag (Rel $counterP) `
|
||||
'counter-unparseable: .session-counter.json is not valid JSON - tick state UNREADABLE, classifying NOTHING' `
|
||||
'fix the JSON syntax (the tick contract cannot be replayed against an unparseable file)'
|
||||
$h245Flags++
|
||||
$h245Measured = $true
|
||||
}
|
||||
else {
|
||||
# ---- axis 1: reachability of last_ticked_head ----
|
||||
$head = [string]$ctr.last_ticked_head
|
||||
if ([string]::IsNullOrWhiteSpace($head)) {
|
||||
Write-Host ' [skip-axis1] last_ticked_head is null - never ticked (seed-honesty state), nothing to classify' -ForegroundColor DarkGray
|
||||
}
|
||||
else {
|
||||
$h245Measured = $true
|
||||
$shortHead = $head.Substring(0, [Math]::Min(8, $head.Length))
|
||||
$reachable = $false
|
||||
try {
|
||||
$null = & git -C $RepoRoot merge-base --is-ancestor $head HEAD 2>$null
|
||||
$reachable = ($LASTEXITCODE -eq 0)
|
||||
}
|
||||
catch { $reachable = $false }
|
||||
|
||||
if ($reachable) {
|
||||
Write-Host (" [OK] OK-reachable: last_ticked_head {0} (session {1}) is an ancestor of HEAD" -f `
|
||||
$shortHead, $ctr.last_ticked_session) -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
$objType = ''
|
||||
$catOk = $false
|
||||
try {
|
||||
$objType = & git -C $RepoRoot cat-file -t $head 2>$null
|
||||
$catOk = ($LASTEXITCODE -eq 0)
|
||||
}
|
||||
catch { $catOk = $false }
|
||||
|
||||
if ($catOk -and (("$objType").Trim() -eq 'commit')) {
|
||||
Write-Host (" [trace] SQUASH-BENIGN: last_ticked_head {0} (session {1}) is NOT reachable from HEAD but the object EXISTS (cat-file -t = commit) - expected closeout-squash drift, 0 flag (contract v2)" -f `
|
||||
$shortHead, $ctr.last_ticked_session) -ForegroundColor DarkGray
|
||||
}
|
||||
else {
|
||||
Write-InformFlag (Rel $counterP) `
|
||||
("object-missing: last_ticked_head {0} (session {1}) is neither reachable from HEAD nor present as an object (cat-file -t failed) - NOT the benign squash class" -f `
|
||||
$head, $ctr.last_ticked_session) `
|
||||
'confirm history was not rewritten; re-tick from a real HEAD or record why the object vanished (contract: object-missing => FAIL-LOUD at runtime)'
|
||||
$h245Flags++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ---- axis 2 [M1]: counter-regress vs the already-audited floor (DEFAULT-ON) ----
|
||||
$curCounter = $null
|
||||
if ($null -ne $ctr.counter) { $curCounter = [int]$ctr.counter }
|
||||
$audit = $ctr.last_audit
|
||||
if ($null -eq $curCounter) {
|
||||
Write-InformFlag (Rel $counterP) `
|
||||
'counter-key-missing: no "counter" key - cadence state unreadable, regress-check measuring NOTHING' `
|
||||
'restore the counter key (H24 tick state lives in this file only)'
|
||||
$h245Flags++
|
||||
$h245Measured = $true
|
||||
}
|
||||
elseif ($null -eq $audit) {
|
||||
Write-Host ' [note] no last_audit block - regress-check has no audited floor to compare against' -ForegroundColor DarkGray
|
||||
}
|
||||
else {
|
||||
foreach ($floorKey in @('light_at_counter', 'deep_at_counter')) {
|
||||
$floorVal = $audit.$floorKey
|
||||
if ($null -eq $floorVal) { continue }
|
||||
$h245Measured = $true
|
||||
$floorN = [int]$floorVal
|
||||
if ($curCounter -lt $floorN) {
|
||||
Write-InformFlag (Rel $counterP) `
|
||||
("counter-regress: counter={0} sits BELOW last_audit.{1}={2} - an audit already ran at a HIGHER counter, so this file was rolled back or hand-edited" -f `
|
||||
$curCounter, $floorKey, $floorN) `
|
||||
'restore the counter to its true value; never silently reset (contract: regress => FAIL-LOUD at runtime, cadence gap must stay visible)'
|
||||
$h245Flags++
|
||||
}
|
||||
else {
|
||||
Write-Host (" [ok] counter={0} >= last_audit.{1}={2}" -f $curCounter, $floorKey, $floorN) -ForegroundColor DarkGray
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$h245Note = if ($h245Measured) { '' } else { ' -- MEASURED NOTHING (skipped above); a 0 after a skip is not a green' }
|
||||
Write-Host (" H24-5 counter-ancestor flags = {0} [INFORM-only, LOW, NOT folded into TOTAL baseline]{1}" -f $h245Flags, $h245Note) -ForegroundColor DarkGray
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary + C4 self-exclusion audit (RUNTIME proof)
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -1379,7 +1537,7 @@ if ($selfInScan -eq 0 -and $leaked -eq 0) {
|
||||
|
||||
Write-Host ''
|
||||
Write-Host ("TOTAL FLAGS: {0}" -f $script:FlagCount) -ForegroundColor Cyan
|
||||
Write-Host ("INFORM-ONLY (new nets C6 cite-2-tier + H24-4 pending-flip): {0} - counted SEPARATELY, NOT in TOTAL above (anti-Goodhart, owner-set; fold+sev-raise is post-triage)" -f $script:InformCount) -ForegroundColor DarkGray
|
||||
Write-Host ("INFORM-ONLY (new nets C6 cite-2-tier + H24-4 pending-flip + H24-5 counter-ancestor-classify): {0} - counted SEPARATELY, NOT in TOTAL above (anti-Goodhart, owner-set; fold+sev-raise is post-triage)" -f $script:InformCount) -ForegroundColor DarkGray
|
||||
Write-Host 'NOTE: DETECT-only lowering net. Exit 0 always (never fails build). FLAGs are advisory.' -ForegroundColor DarkGray
|
||||
|
||||
exit 0
|
||||
|
||||
@ -31,7 +31,9 @@
|
||||
promise the next spawn stays on the expected version. This is exactly why H23 section 2(4)
|
||||
makes it informational, not enforcement.
|
||||
(ii) PRECEDENCE vs a frontmatter HARD-PIN - MEASURED at SE 2026-07-16 (S126), after S124
|
||||
re-pinned 14/14 agents to 'model: opus' (ALIAS pin): spawn param 'fable' on agentType
|
||||
re-pinned all 14 agents THEN-EXISTING to 'model: opus' (ALIAS pin; roster is 17 as of
|
||||
S141 - this measurement was taken when N was 14 and was NOT re-run for the 3 new ones):
|
||||
spawn param 'fable' on agentType
|
||||
reviewer (pin 'opus') RESOLVED claude-fable-5 (run wf_f960dae2-fa0, 3 records); a
|
||||
control lane with NO override resolved claude-opus-4-8 under lead=Fable => the pin
|
||||
itself works at runtime AND the spawn param BEATS it. Caveats: alias-pin only (the
|
||||
|
||||
55
scripts/stamp_verify.py
Normal file
55
scripts/stamp_verify.py
Normal file
@ -0,0 +1,55 @@
|
||||
# stamp_verify.py — content_sha256 selftest (G-024(b) / E-024: hash via script-file ONLY)
|
||||
# Recompute canonical body-hash of broadcast/email .md and compare vs declared frontmatter.
|
||||
# Canonical (Harness-3 §N / README): strip \r -> split at 2nd '---' line -> strip ONE leading newline -> sha256 utf-8.
|
||||
# Also prints the NO-STRIP variant to diagnose wrong-method stamps (BVAAU s71 finding class).
|
||||
# Usage: python scripts/stamp_verify.py <file.md> [...] exit 1 if any MISMATCH.
|
||||
# mirror-of AI_INFRA/scripts/stamp_verify.py (ported S141 2026-07-20) - re-pull khi hub đổi §N canon. KHONG sửa logic local.
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def analyze(p: Path):
|
||||
raw = p.read_bytes().decode("utf-8", errors="replace")
|
||||
m = re.search(r'content_sha256:\s*"?([0-9a-fA-F]{8,64})"?', raw)
|
||||
declared = m.group(1).lower() if m else None
|
||||
text = raw.replace("\r", "")
|
||||
parts = re.split(r"(?m)^---\s*$", text, maxsplit=2)
|
||||
if len(parts) < 3:
|
||||
return declared, None, None
|
||||
body_nostrip = parts[2]
|
||||
body_canon = re.sub(r"^\n", "", body_nostrip, count=1)
|
||||
h_canon = hashlib.sha256(body_canon.encode("utf-8")).hexdigest()
|
||||
h_nostrip = hashlib.sha256(body_nostrip.encode("utf-8")).hexdigest()
|
||||
return declared, h_canon, h_nostrip
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("usage: python scripts/stamp_verify.py <file.md> [...]")
|
||||
sys.exit(2)
|
||||
bad = 0
|
||||
for arg in sys.argv[1:]:
|
||||
p = Path(arg)
|
||||
declared, h_canon, h_nostrip = analyze(p)
|
||||
print(f"\n== {p.name}")
|
||||
print(f" declared : {declared}")
|
||||
print(f" canonical: {h_canon}")
|
||||
print(f" no-strip : {h_nostrip}")
|
||||
if declared is None:
|
||||
print(" verdict : NO-DECLARED-SHA (n/a)")
|
||||
elif h_canon and (h_canon.startswith(declared) or declared.startswith(h_canon[: len(declared)])):
|
||||
print(" verdict : OK (canonical match)")
|
||||
elif h_nostrip and (h_nostrip.startswith(declared) or declared.startswith(h_nostrip[: len(declared)])):
|
||||
print(" verdict : WRONG-METHOD (declared = NO-STRIP variant -> re-stamp canonical)")
|
||||
bad += 1
|
||||
else:
|
||||
print(" verdict : MISMATCH (neither variant -> investigate: edit-after-stamp?)")
|
||||
bad += 1
|
||||
sys.exit(1 if bad else 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user