Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"source": {
"source": "npm",
"package": "@sdsrs/agentsmd",
"version": "4.25.4 || 5.0.0"
"version": "5.0.0 || 5.0.1"
},
"policy": {
"installation": "AVAILABLE",
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentsmd",
"version": "5.0.0",
"version": "5.0.1",
"surfaceProtocolVersion": 1,
"description": "CODEX-CODING-SPEC — global Codex workflow and evidence guidance, with native hooks for selected safety/report checks and rule-specific opportunity telemetry.",
"author": {
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
Release history for **agentsmd** (the Codex coding-spec enforcement plugin). The
spec's own rule-level history lives in `spec/AGENTS-CHANGELOG.md`.

## v5.0.1 — 2026-07-28 — registry install-readiness retry (patch)

- Extended the tested post-publish registry helper so one retry attempt includes
both `npm pack` and an isolated `npm install --ignore-scripts` probe. A
successful tarball fetch no longer ends the retry loop while the install
packument can still return transient `ETARGET`.
- Failed install-readiness attempts remove only that attempt's new top-level
tarball and exact temporary install prefix before waiting. Pre-existing
tarballs, nested files, and unrelated destination contents remain untouched.
- Added direct and real CLI regressions for pack-success/install-lag recovery,
install-readiness exhaustion, probe disposal, and operation beneath
`bash -euo pipefail`.
- The immutable v5.0.0 package and GitHub release were published successfully;
their first release-workflow attempt exposed the multi-endpoint propagation
race after publication, and the idempotent failed-job rerun completed every
release gate. This successor patch closes that race in the reusable helper.

## v5.0.0 — 2026-07-28 — single full profile and native Codex orchestration contract (major)

### Breaking profile simplification
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ set -eu
NAME="agentsmd"
DEFAULT_REPO="sdsrss/agentsmd"
# Synchronized by scripts/version-sync.js — must equal package.json version.
INSTALLER_VERSION="5.0.0"
INSTALLER_VERSION="5.0.1"
DEFAULT_REF="v$INSTALLER_VERSION"

ACTION="install"
Expand Down
17 changes: 12 additions & 5 deletions memory/reference_release-registry-propagation.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
verified: 2026-07-27 | source: GitHub Actions release runs 30262890085 and 30264165884
verified: 2026-07-28 | source: GitHub Actions release runs 30262890085, 30264165884, and 30334474903

# Release registry propagation retries

Expand All @@ -9,7 +9,14 @@ forms exited the step before the retry body despite passing under local
`bash -euo pipefail`.

Put the npm subprocess and retry loop inside a tested Node helper. The helper
captures child exit statuses, removes only newly created top-level tarball
paths from its bounded destination between attempts, waits, and returns nonzero
to the workflow only after the configured attempts are exhausted. This keeps
shell `errexit` outside the retryable failure boundary.
must treat the tarball fetch and install metadata as one readiness attempt:
`npm pack` can succeed while the next `npm install name@version` still returns
transient `ETARGET` from a lagging packument endpoint. Run the install probe in
an exact temporary prefix with lifecycle scripts disabled.

Capture both child exit statuses, remove only newly created top-level tarball
paths and the exact temporary install prefix between attempts, wait, and return
nonzero to the workflow only after the configured attempts are exhausted. This
keeps shell `errexit` outside the retryable failure boundary and does not
declare propagation complete before the workflow's install-based signature
audit can resolve the version.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sdsrs/agentsmd",
"version": "5.0.0",
"version": "5.0.1",
"description": "A global coding-discipline spec for Codex with native hooks, reversible global AGENTS.md installation, and rule-specific opportunity telemetry.",
"homepage": "https://github.com/sdsrss/agentsmd#readme",
"bugs": "https://github.com/sdsrss/agentsmd/issues",
Expand Down
53 changes: 47 additions & 6 deletions scripts/lib/registry-pack-retry.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ function cleanTarballs(destination, preserved = new Set()) {
}
}

function installProbeArgs(packageSpec, probeDirectory) {
return [
'install',
packageSpec,
'--prefix',
probeDirectory,
'--ignore-scripts',
'--no-audit',
'--no-fund',
'--package-lock=false',
'--prefer-online',
];
}

async function packRegistryArtifact({
packageSpec,
destination,
Expand All @@ -58,31 +72,58 @@ async function packRegistryArtifact({

const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
let lastStatus = null;
let lastStage = 'pack';
for (let attempt = 1; attempt <= attempts; attempt += 1) {
realDirectoryEntries(destination);
const result = spawn(npm, [
const packResult = spawn(npm, [
'pack',
packageSpec,
'--pack-destination',
destination,
'--json',
'--prefer-online',
], { stdio: 'inherit' });
if (result.error) throw result.error;
if (result.status === 0) return { attempt, status: 0 };
if (packResult.error) {
cleanTarballs(destination, preservedTarballs);
throw packResult.error;
}

lastStage = 'pack';
lastStatus = packResult.status;
if (packResult.status === 0) {
const probeDirectory = fs.mkdtempSync(path.join(destination, '.agentsmd-install-probe.'));
try {
const installResult = spawn(
npm,
installProbeArgs(packageSpec, probeDirectory),
{ stdio: 'inherit' }
);
if (installResult.error) {
cleanTarballs(destination, preservedTarballs);
throw installResult.error;
}
lastStage = 'install';
lastStatus = installResult.status;
if (installResult.status === 0) return { attempt, status: 0 };
} finally {
fs.rmSync(probeDirectory, { recursive: true, force: true });
}
}

lastStatus = result.status;
cleanTarballs(destination, preservedTarballs);
if (attempt < attempts) {
log(`waiting for ${packageSpec} registry bytes (attempt ${attempt}/${attempts}, npm exit ${result.status})`);
const readiness = lastStage === 'pack' ? 'registry bytes' : 'registry install readiness';
log(`waiting for ${packageSpec} ${readiness} (attempt ${attempt}/${attempts}, npm ${lastStage} exit ${lastStatus})`);
await wait(delayMs);
}
}
throw new Error(`${packageSpec} registry bytes were unavailable after ${attempts} attempts (last npm exit ${lastStatus})`);
const readiness = lastStage === 'pack' ? 'registry bytes' : 'registry install readiness';
throw new Error(`${packageSpec} ${readiness} was unavailable after ${attempts} attempts (last npm ${lastStage} exit ${lastStatus})`);
}

module.exports = {
cleanTarballs,
installProbeArgs,
packRegistryArtifact,
positiveInteger,
realDirectoryEntries,
Expand Down
Loading