Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/release-aggregate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ permissions:
jobs:
release-aggregate:
name: release-aggregate
# Failed stranger runs have no binding receipt; only successful workflow_run events and
# explicit recovery dispatches are valid aggregate inputs.
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
Expand Down
1 change: 1 addition & 0 deletions docs/adr/0055-proactivity-that-meshes.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ clause as the pre-agreed escalation (§3.5); Task-prompt scanning — Fable's re
delegation drift goes to the interrupt tier (§3.7.9).

## Currency log
| 2026-08-03 | Bounded the shared hook-input parser on Windows inherited pipes by draining readable bytes and polling until the existing idle/empty deadlines. | Post-merge main CI run `30847567626` failed only `swarm-slot-recycler.test.mjs` on Windows because the held-open envelope was not surfaced through `data`; `tests/unit/swarm-slot-recycler.test.mjs` and `tests/unit/hook-input.test.mjs` pass 43/43 after the platform-specific parser fix. |
| 2026-08-03 | Re-read the proactive hook mesh after the Codex staged-host correction; no contract change. | PR #100 exact-SHA release evidence is green; Windows unit remains the sole required red lane. |

| Date | What changed | Why (with referents) |
Expand Down
9 changes: 8 additions & 1 deletion docs/adr/0062-remote-durable-release-transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: ADR-062
title: Remote-durable staged release transaction
status: Accepted
date: 2026-08-02
updated: 2026-08-02
updated: 2026-08-03
authors: [Stuart Kerr]
tags: [release, evidence, transaction, npm, github, receipts, recovery]
supersedes: []
Expand All @@ -12,6 +12,7 @@ governs:
- .github/workflows/ci.yml
- .github/workflows/stranger-matrix.yml
- .github/workflows/protected-release.yml
- .github/workflows/release-aggregate.yml
- scripts/release.mjs
- scripts/release-transaction.mjs
- scripts/release-transaction-provider.mjs
Expand All @@ -21,6 +22,12 @@ governs:

# ADR-062 — Remote-durable staged release transaction

## Currency log

| Date | What changed | Why (with referents) |
|---|---|---|
| 2026-08-03 | `release-aggregate` now runs only for a successful stranger-matrix completion or explicit recovery dispatch. | Post-merge run `30847604771` attempted to download `stranger-evidence-9de40a59…` from a failed run with no artifact; run `30848438803` then proved successful stranger evidence still waits for exact-SHA CI convergence. `.github/workflows/release-aggregate.yml`. |

**Status**: Accepted

**Date**: 2026-08-02
Expand Down
12 changes: 12 additions & 0 deletions plugin/scripts/hook-input.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ export function readStdinBounded({ maxBytes = 65536, idleMs = 50, emptyMs = 250
let settled = false;
let idleTimer;
let emptyTimer;
let poll;

const cleanup = () => {
clearTimeout(idleTimer);
clearTimeout(emptyTimer);
clearInterval(poll);
process.stdin.off('data', onData);
process.stdin.off('readable', onReadable);
process.stdin.off('end', onEnd);
process.stdin.off('error', onError);
process.stdin.pause();
Expand Down Expand Up @@ -87,11 +90,20 @@ export function readStdinBounded({ maxBytes = 65536, idleMs = 50, emptyMs = 250
};
const onEnd = () => finish();
const onError = () => finish();
// Windows inherited pipes can expose bytes through the readable interface without emitting
// a data event while the writer keeps stdin open. Drain that interface and poll it briefly;
// the envelope contract is one value, never EOF.
const onReadable = () => {
let chunk;
while ((chunk = process.stdin.read()) !== null) onData(chunk);
};

process.stdin.on('data', onData);
if (process.platform === 'win32') process.stdin.on('readable', onReadable);
process.stdin.once('end', onEnd);
process.stdin.once('error', onError);
emptyTimer = setTimeout(finish, emptyMs);
if (process.platform === 'win32') poll = setInterval(onReadable, 10);
process.stdin.resume();
});
}
Expand Down
Loading