Skip to content
Open
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
54 changes: 54 additions & 0 deletions .github/workflows/ctx-pipeline-receive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,67 @@ jobs:
token: ${{ secrets.DOCS_READ_TOKEN }}
path: .docs-src
persist-credentials: false
# Full history: the monotonicity guard below needs ancestry.
fetch-depth: 0

# Reject out-of-order deliveries: an older dispatch arriving after a
# newer import must not roll skills back. The incoming commit must
# descend from the last imported one. Fail-closed semantics: only a
# genuinely absent prior state proceeds (bootstrap); a state that
# exists but cannot be validated FAILS the run — silence here is how
# rollbacks happen.
- name: Monotonicity guard
run: |
# True absence only — a directory, FIFO, or broken symlink at this
# path is NOT absence; it falls through and fails validation below.
if [ ! -e .ctx-gen/state.json ] && [ ! -L .ctx-gen/state.json ]; then
echo "no prior import state — bootstrap import proceeds"
exit 0
fi
# Present but not a regular file (dir/FIFO/symlink): fail before
# node — require() on a dir can execute code, on a FIFO can hang.
if [ ! -f .ctx-gen/state.json ] || [ -L .ctx-gen/state.json ]; then
echo "::error::state.json exists but is not a regular file — failing closed"
exit 1
fi
# Exactly one distinct recorded docsCommit, and it must look like a
# commit hash. Disagreement or garbage = corrupt state: fail, don't guess.
last=$(node -e '
const s = require("./.ctx-gen/state.json");
const entries = Object.values(s);
if (entries.length === 0) { console.error("state.json is present but empty — not a bootstrap; reset it explicitly if intended"); process.exit(1); }
// EVERY entry must carry the same valid commit — a partial state
// (some entries missing docsCommit) is corruption, not a quorum.
const commits = entries.map(v => v && v.docsCommit);
if (commits.some(c => !/^[0-9a-f]{40}$/.test(c || ""))) { console.error("state entry missing or malformed docsCommit — state is partial/corrupt"); process.exit(1); }
const distinct = [...new Set(commits)];
if (distinct.length > 1) { console.error("state.json records conflicting docsCommit values: " + distinct.join(", ")); process.exit(1); }
console.log(distinct[0]);
') || { echo "::error::import state exists but cannot be validated — failing closed (fix or explicitly reset .ctx-gen/state.json)"; exit 1; }
incoming=$(git -C .docs-src rev-parse HEAD)
if ! git -C .docs-src cat-file -e "$last" 2>/dev/null; then
echo "::error::last imported commit $last is not present in the docs checkout — cannot establish ordering (incomplete fetch or rewritten history); failing closed"
exit 1
fi
rc=0
git -C .docs-src merge-base --is-ancestor "$last" "$incoming" || rc=$?
if [ "$rc" = 1 ]; then
echo "::notice::stale delivery: incoming $incoming does not descend from last imported $last — skipping (a newer import already covers this)"
echo "SKIP_IMPORT=1" >> "$GITHUB_ENV"
elif [ "$rc" != 0 ]; then
echo "::error::merge-base failed with status $rc — cannot establish ordering; failing closed"
exit 1
fi



- name: Resolve docs commit
id: docs
run: echo "sha=$(git -C .docs-src rev-parse HEAD)" >> "$GITHUB_OUTPUT"

- name: Import changed skills
id: import
if: env.SKIP_IMPORT != '1'
run: node scripts/ctx-receive.mjs --docs .docs-src --docs-commit "${{ steps.docs.outputs.sha }}"

- name: Open or update the rolling sync PR
Expand Down
Loading