Skip to content

Add Kael Ecosystem Pulse #232

Add Kael Ecosystem Pulse

Add Kael Ecosystem Pulse #232

Workflow file for this run

name: Submission Gate
# WHY pull_request_target AND NOT pull_request (fixed 2026-08-30)
# --------------------------------------------------------------
# Every submission we get is from a FORK. A `pull_request` event from a fork is
# issued a READ-ONLY GITHUB_TOKEN no matter what `permissions:` below says. So the
# final steps -- the verdict comment, the label -- could never succeed. This gate ran
# 21 times and failed 21 times, always AFTER passing the endpoint, purely on the
# comment write. No operator ever received the automated verdict; every warm comment
# on this door was posted by hand, which is what hid the failure for four months.
#
# THE SAFETY PROPERTY THAT MAKES THIS SAFE -- DO NOT BREAK IT:
# This job NEVER checks out or executes code from the pull request.
# `actions/checkout` below is pinned to `ref: main`, and `scripts/submit_check.py`
# therefore always runs the version on main, never the submitter's version.
# PR-derived values (body, extracted URL, example body) reach the shell ONLY as
# environment variables -- never spliced into `run:` as expression text.
#
# IF YOU ARE ABOUT TO ADD `ref: ${{ github.event.pull_request.head.sha }}` OR ANY
# OTHER CHECKOUT OF SUBMITTED CODE: STOP. That combination hands a write-scoped
# token to a stranger's code. Split the job in two (untrusted job -> artifact ->
# privileged `workflow_run` job) instead.
on:
pull_request_target:
# `edited` added 2026-09-05. Without it a submitter who corrects their PR
# body -- which is exactly what #183 did after the gate probed the wrong
# URL -- is left with a red X standing against a body that no longer
# exists, and no way to clear it. Safe here only because this job never
# checks out PR code; see the header.
types: [opened, synchronize, edited]
paths:
- 'directory/**'
- 'README.md'
permissions:
contents: read # we only read main; nothing here writes to the repo
pull-requests: write # update PR state / labels
issues: write # PR comments go through the issues endpoint -- this is
# the permission whose absence broke the gate
jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
# WHICH URL IS THE DOOR (moved into a script 2026-09-05)
# This was two greps and it picked wrong twice in three days -- once on a
# `Contact URL:` line (#183), once on an inline npm link with the trailing
# `),` of the markdown attached (#185). Both times a stranger was told
# their service failed a probe that never touched their service. The
# ordered rule, its reasons and its tests live in scripts/extract_url.py,
# which runs from main like everything else here.
- name: Extract submitted URL from PR body
id: extract_url
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: python scripts/extract_url.py
- name: Fail if no URL found
if: steps.extract_url.outputs.url == ''
run: |
echo "No URL found in PR body. Please include your endpoint URL."
echo "Format: URL: https://your-service.com/api/endpoint"
exit 1
- name: Note whether a contact was supplied (optional, never blocks)
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
# An ASK, not a bar. No entry is ever rejected for leaving this out.
# Default contact route is an issue on the submitter's own repo; the
# Contact: line only exists for people who'd rather be reached elsewhere.
# Added 2026-08-26 (Sean's direction).
CONTACT=$(echo "$PR_BODY" | grep -oP '(?i)^\s*contact:\s*\K\S.*' | head -1)
if [ -z "$CONTACT" ]; then
echo "::notice::No 'Contact:' line -- that's fine, it's optional."
echo "::notice::If your listing needs attention we'll open an issue on your repo."
echo "::notice::Prefer somewhere else? See CONTRIBUTING.md -> How we reach you."
else
echo "::notice::Contact supplied. Kept out of the directory, per CONTRIBUTING.md -> How we reach you."
fi
- name: Extract example request body from PR body (optional)
id: extract_example
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
# Optional `Example: {json}` line -- endpoints that validate request
# parameters BEFORE the 402 challenge (e.g. OpenAI-compatible APIs)
# cannot answer an empty-body probe with 402. When present, the gate
# probes with this body instead of {}.
EXAMPLE=$(echo "$PR_BODY" | \
grep -oP '(?i)example\s*:\s*\K\{.*\}' | head -1)
echo "example=$EXAMPLE" >> "$GITHUB_OUTPUT"
if [ -n "$EXAMPLE" ]; then
echo "Extracted example request body: ${EXAMPLE:0:120}"
else
echo "No example request body -- empty-body probe will be used"
fi
# WHICH ACCEPTANCE RULE APPLIES (added 2026-09-05)
# CONTRIBUTING.md has always carried two: services must answer 402, while
# a library/framework/learning/community resource with no payable endpoint
# only has to be publicly reachable. The gate implemented the first and
# ignored the second, so every learning-resource submission failed by
# design while the guide said accept it. Found on #183 by the submitter.
#
# The mode is derived from the SHELF FILES the PR touches -- never from
# anything the submitter writes. A submitter cannot ask for the looser
# rule; they can only put their line on a shelf, and a human still merges.
# Reading the changed-file list is metadata, not code: safe under
# pull_request_target.
- name: Decide which acceptance rule applies
id: mode
uses: actions/github-script@v7
with:
script: |
const RESOURCE_SHELVES = [
'directory/learning.md',
'directory/community.md',
'directory/sdks.md',
'directory/frameworks.md',
]
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 100,
})
const touched = files
.map(f => f.filename)
.filter(n => n.startsWith('directory/'))
// Default is the STRICTER rule. Resource mode only when every
// directory file touched is a resource shelf -- a PR that also
// touches a service shelf gets probed for 402, as it should be.
const isResource =
touched.length > 0 && touched.every(n => RESOURCE_SHELVES.includes(n))
const mode = isResource ? 'resource' : 'service'
core.info('directory files touched: ' + (touched.join(', ') || '(none)'))
core.info('acceptance rule: ' + mode)
core.setOutput('mode', mode)
- name: Run submission check
id: check
env:
# PR-derived values reach the shell as environment variables only --
# never spliced into the command line as expression text.
SUBMIT_URL: ${{ steps.extract_url.outputs.url }}
EXAMPLE_BODY: ${{ steps.extract_example.outputs.example }}
SUBMIT_MODE: ${{ steps.mode.outputs.mode }}
# WHY continue-on-error (2026-09-05, found on #185).
# submit_check.py exits 1 on a fail. Every step after a failed step
# carries an implicit success(), so the fail COMMENT and the needs-work
# LABEL were skipped on every failing submission this gate ever saw --
# #185 got a red X and total silence, while CONTRIBUTING.md promises
# "never a silent rejection". The run still goes red: the last step
# reads the result and exits 1.
continue-on-error: true
run: python scripts/submit_check.py "$SUBMIT_URL" "$EXAMPLE_BODY" "$SUBMIT_MODE"
- name: Read check result
id: result
if: always()
run: |
if [ -f submit_result.json ]; then
RESULT=$(python -c "import json; d=json.load(open('submit_result.json')); print(d['result'])")
REASON=$(python -c "import json; d=json.load(open('submit_result.json')); print(d['reason'])")
echo "result=$RESULT" >> "$GITHUB_OUTPUT"
echo "reason=$REASON" >> "$GITHUB_OUTPUT"
else
echo "result=fail" >> "$GITHUB_OUTPUT"
echo "reason=check script did not produce output" >> "$GITHUB_OUTPUT"
fi
- name: Comment on PR -- pass
if: steps.result.outputs.result == 'pass'
uses: actions/github-script@v7
env:
SUBMIT_URL: ${{ steps.extract_url.outputs.url }}
CHECK_REASON: ${{ steps.result.outputs.reason }}
SUBMIT_MODE: ${{ steps.mode.outputs.mode }}
URL_WHY: ${{ steps.extract_url.outputs.why }}
with:
script: |
const url = process.env.SUBMIT_URL || '';
const why = process.env.URL_WHY || '';
const reason = process.env.CHECK_REASON || '';
const mode = process.env.SUBMIT_MODE || 'service';
const rule = mode === 'resource'
? "resource -- publicly reachable, no 402 required"
: "service -- must answer HTTP 402";
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: [
"**Gold-402 Verification: PASSED**",
"",
"URL checked: `" + url + "`" + (why ? " _(" + why + ")_" : ""),
"Rule applied: " + rule,
"Result: " + reason,
"",
"This PR has been approved for merge. A maintainer will review and merge shortly.",
"",
"---",
mode === 'resource'
? "*Verified by Gold-402. This check confirms the resource is publicly reachable. It makes no claim about x402 compliance, and is not an endorsement.*"
: "*Verified by Gold-402. Verification confirms x402 technical compliance only. It does not constitute endorsement of the service.*"
].join('\n')
})
- name: Comment on PR -- fail
if: steps.result.outputs.result != 'pass'
uses: actions/github-script@v7
env:
SUBMIT_URL: ${{ steps.extract_url.outputs.url }}
CHECK_REASON: ${{ steps.result.outputs.reason }}
SUBMIT_MODE: ${{ steps.mode.outputs.mode }}
URL_WHY: ${{ steps.extract_url.outputs.why }}
with:
script: |
const url = process.env.SUBMIT_URL || '';
const why = process.env.URL_WHY || '';
const reason = process.env.CHECK_REASON || '';
const mode = process.env.SUBMIT_MODE || 'service';
const rule = mode === 'resource'
? "resource -- publicly reachable, no 402 required"
: "service -- must answer HTTP 402";
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: [
"**Gold-402 Verification: FAILED**",
"",
"URL checked: `" + url + "`" + (why ? " _(" + why + ")_" : ""),
"Rule applied: " + rule,
"Reason: " + reason,
"",
"A maintainer will still read this by hand -- the PR stays open.",
"Push a new commit, or edit the PR body, to re-run the check.",
"",
"Common causes:",
"- The URL checked above is not the one you meant. The gate picks it in",
" this order: a line starting `URL:`, `Endpoint:`, `Service:`, `Manifest:`",
" or `API:`; then any `/.well-known/x402` manifest link; then the first",
" link that is not a package, repo or social page. Add a `URL:` line to",
" say it outright.",
"- A service endpoint that does not answer HTTP 402 unauthenticated.",
"- A URL that is unreachable, times out, or sits behind a login.",
"- The entry is already listed.",
"",
"---",
mode === 'resource'
? "*Gold-402 checked whether this resource is publicly reachable.*"
: "*Gold-402 verifies x402 technical compliance only.*"
].join('\n')
})
# DELIBERATELY NOT AUTO-CLOSING (2026-08-30).
# This step existed but had never once run -- the fail-comment step ahead of it
# died on the same permission bug, so the auto-close never fired in four months.
# Fixing the permission would have switched it on silently, and slamming a
# stranger's PR shut contradicts the door doctrine: a hold gets a warm comment
# and a route back in. A closed PR is not a route back in -- pushing a fix to a
# closed PR does not reopen it. So: label it, leave it open, let a hand look.
- name: Label failing PRs for a human look
if: steps.result.outputs.result != 'pass'
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['needs-work']
})
} catch(e) {
console.log('Label not found, skipping:', e.message)
}
- name: Label passing PRs
if: steps.result.outputs.result == 'pass'
uses: actions/github-script@v7
with:
script: |
try {
await github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['verified', 'ready-to-merge']
})
} catch(e) {
console.log('Labels not found, skipping:', e.message)
}
- name: Fail workflow if check failed
if: steps.result.outputs.result != 'pass'
run: exit 1