Add Kael Ecosystem Pulse #232
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |