The pin added earlier read the local keychain unconditionally, which was right
for publish.sh and wrong for release-macos.yml: on CI the cert arrives as a
base64 .p12 in CSC_LINK and electron-builder imports it into a temporary
keychain LATER than this preflight runs, so the probe would have found nothing
and aborted every CI mac build before it started. Fixed by branching on where
the cert actually comes from -- keychain locally, the .p12 itself on CI -- and
running the profile check against whichever cert that resolves to, since both
sources can drift from the embedded profile.
Verified with controls on both paths: the .p12 reader returns the true SHA-1 of
a synthetic cert and returns nothing (rather than a bogus hash) under a wrong
password, and the local path still passes against the live cert and profile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wtspwSFzZmjCx9UNPAorQ
The profile shipped with the app listed only the certificate whose private key
was lost with the old Mac, so it would have signed the new build without
complaint and then dropped keychain-access-groups at runtime, taking passkeys
and Touch ID with it. Regenerated against the cert we can actually sign with;
the preflight added alongside it now refuses any build where the two disagree.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wtspwSFzZmjCx9UNPAorQ
find-identity reports five entries all named "Developer ID Application: Haik
Decie (Y26NUZH4NG)": one live cert and four copies of the one whose private key
died with the old Mac. The dead copies still report valid and only fail at the
moment they are asked to sign, so auto-discovery was a coin flip between a real
build and a crash forty minutes into packing a 4.8 GB .app. Pin by SHA-1, then
spend one scratch signature proving the key is actually usable.
The profile check is the same lesson applied to a quieter failure. entitlements
request keychain-access-groups, which macOS honors only when the embedded
profile lists the very cert that signed the app. A profile issued for the
previous cert signs without one complaint and then loses passkeys and Touch ID
at runtime: a green build that ships broken. Verified with both controls, the
old profile fails against the new cert and passes against its own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wtspwSFzZmjCx9UNPAorQ
Every silent-stop fix so far was a detector for a shape somebody had already
found in the field, which is why the class kept coming back wearing a new hat.
turn_spoke.py moves the question down a tier: at the one exit every terminal
path passes through, ask whether anything readable appeared since the user last
spoke, and if not, say the honest line. Cause no longer has to be enumerated for
the user to be answered.
proactive_prune.py is the hermes trigger we were missing. Their own tests say
our bug out loud: on a large window, a percentage-of-window compaction check
almost never fires, so aged tool output rides in history and is re-sent verbatim
every turn. Measured here, our shaping cut 0.0% at every session size; with a
fixed 60K-token cost trigger it cuts 88% at 12 turns, 93% at 30, 94% at 60. The
prompt-cache contract is load-bearing rather than optional, because our prune is
a rebuild: it commits only when it reclaims enough to pay for the busted prefix,
then disarms until history has regrown a full runway.
Also: lane preflight now treats only 401/403 as a dead credential, since
testStatus=="unavailable" conflated a throttled lane with a revoked one and told
users to reconnect a merely rate-limited Gemini; and awaiting_reconnect is
cleared when the retry budget is spent, so a stale flag can no longer muzzle the
floor and end an ask in total silence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014wtspwSFzZmjCx9UNPAorQ