Files
ocp/.github/workflows/alignment.yml
T
9e25160527 refactor: hoist port literal to lib/constants.mjs + CI gate (v3.16.4) (#98)
Closes the structural side of the port-drift cascade addressed by
v3.16.2/v3.16.3. Those releases reverted the literal line-by-line; this
one removes the invitation to drift.

Changes:
  * NEW lib/constants.mjs — exports DEFAULT_PORT=3456, LOCAL_HOST,
    OPENAI_API_BASE, LOCAL_PROXY_URL.
  * server.mjs / setup.mjs / scripts/upgrade.mjs / scripts/doctor.mjs
    (x2) / scripts/sync-openclaw.mjs all import DEFAULT_PORT from
    lib/constants.mjs instead of hardcoding "3456".
  * .github/workflows/alignment.yml:
    - path filter extended to setup.mjs, scripts/**, lib/**,
      ocp, ocp-connect.
    - NEW job port-spot hard-fails any PR that introduces a hardcoded
      "3478" or "3456" literal outside EXEMPT_REGEX (lib/constants.mjs,
      test-features.mjs, ocp/ocp-connect bash CLIs, docs, the workflow
      itself).
  * Doc-comment rewording so CI grep finds zero hits.

No behavior change for any user. CLAUDE_PROXY_PORT env var still wins
at runtime; only the unset-env fallback now flows through one constant.

ALIGNMENT.md note: server.mjs change is one import + one literal swap,
mechanical. No cli.js operation changed; the citation requirement does
not apply.

cli.js: not applicable — mechanical refactor, no behavior change.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
2026-05-13 06:42:15 +10:00

203 lines
7.7 KiB
YAML

name: Alignment Guardrail
on:
pull_request:
paths:
- 'server.mjs'
- 'setup.mjs'
- 'scripts/**'
- 'lib/**'
- 'ocp'
- 'ocp-connect'
- '.github/workflows/alignment.yml'
jobs:
blacklist:
name: server.mjs blacklist (hard fail)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Scan server.mjs for hallucinated tokens
shell: bash
run: |
set -euo pipefail
if [ ! -f server.mjs ]; then
echo "server.mjs not found; nothing to scan."
exit 0
fi
# Known-hallucinated tokens. Extend only via an ALIGNMENT.md amendment PR.
# Each token is matched as a fixed string against server.mjs only.
BLACKLIST=(
"api.anthropic.com/api/oauth/usage"
)
FAIL=0
for token in "${BLACKLIST[@]}"; do
if sed "s|//.*\$||" server.mjs | grep -n -F "$token"; then
echo "::error file=server.mjs::Blacklisted token '$token' detected in server.mjs."
FAIL=1
fi
done
if [ "$FAIL" -ne 0 ]; then
cat <<'EOF'
============================================================
ALIGNMENT GUARDRAIL FAILURE
============================================================
server.mjs contains a token on the OCP alignment blacklist.
These tokens were introduced by LLM hallucinations and do
not appear in cli.js at any shipped Claude Code version.
See ALIGNMENT.md -> "Historical Lesson: The 2026-04-11 Drift"
(commit b87992f) for the full incident record.
Required action:
1. Remove the token from server.mjs.
2. grep the reference cli.js for the operation you
intended and cite the real line numbers.
3. See ALIGNMENT.md Rules 1, 2, and 5.
Do not add allowlist entries to this workflow without an
amendment PR to ALIGNMENT.md (see Amendment Procedure).
============================================================
EOF
exit 1
fi
echo "Blacklist scan clean."
port-spot:
name: port literal SPOT (hard fail)
# Background: from 2026-05-08 (PR #71 dogfood accident) through 2026-05-13
# a hardcoded "3478" in scripts/upgrade.mjs + scripts/doctor.mjs cascaded
# into wrong baseUrl writes for the OpenClaw "claude-local" provider,
# taking out the "大内总管" Telegram agent.
#
# Rule: the only places allowed to write a literal port number in source
# are (a) lib/constants.mjs (the SPOT), (b) bash scripts ocp / ocp-connect
# (which can't import .mjs and must keep the literal in sync — flagged
# with a `// keep in sync with lib/constants.mjs` style comment), and
# (c) test-features.mjs (intentionally pins historical ports for plist /
# systemd parser tests). Everything else MUST import from lib/constants.mjs.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Scan for hardcoded port literals outside SPOT
shell: bash
run: |
set -euo pipefail
# Files/paths exempt from the SPOT requirement.
EXEMPT_REGEX='^(lib/constants\.mjs|test-features\.mjs|ocp|ocp-connect|CHANGELOG\.md|README\.md|docs/|\.github/workflows/alignment\.yml)'
# Hardcoded port literals to forbid in non-exempt source.
FORBIDDEN_PORTS=("3478" "3456")
FAIL=0
for port in "${FORBIDDEN_PORTS[@]}"; do
HITS="$(git ls-files | grep -E '\.(mjs|js|ts|json)$' \
| xargs grep -n -E "[^0-9]${port}[^0-9]" 2>/dev/null \
| grep -v -E "${EXEMPT_REGEX}" \
|| true)"
if [ -n "$HITS" ]; then
echo "::error::Hardcoded port literal '${port}' found outside lib/constants.mjs:"
echo "$HITS"
FAIL=1
fi
done
if [ "$FAIL" -ne 0 ]; then
cat <<'EOF'
============================================================
PORT LITERAL SPOT VIOLATION
============================================================
A hardcoded TCP port literal was found in a source file
that should import from lib/constants.mjs instead.
Background: this rule exists because between 2026-05-08 and
2026-05-13 a stray hardcoded "3478" in scripts/upgrade.mjs
and scripts/doctor.mjs cascaded into downstream OpenClaw
config writes, taking out the OpenClaw Telegram agent.
See v3.16.3 CHANGELOG and lib/constants.mjs header comment.
Required action:
1. Import DEFAULT_PORT (or related constant) from
lib/constants.mjs instead of hardcoding the literal.
2. If the file genuinely cannot import .mjs (e.g. bash
script), add it to EXEMPT_REGEX in this workflow and
add a `keep in sync with lib/constants.mjs` comment
at the reference.
3. For test files that intentionally pin historical ports
(test-features.mjs), the regex already exempts them.
============================================================
EOF
exit 1
fi
echo "Port SPOT scan clean."
commit-citation:
name: commit message citation (soft check)
runs-on: ubuntu-latest
# Soft check: reports a warning but does not block merge. Reviewers
# are expected to enforce per CLAUDE.md. Escalate to hard-fail via
# an ALIGNMENT.md amendment if drift recurs.
continue-on-error: true
steps:
- name: Checkout full history
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan PR commits for uncited assertions
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
if [ -z "${BASE_SHA:-}" ] || [ -z "${HEAD_SHA:-}" ]; then
echo "No PR context; skipping."
exit 0
fi
# Collect commit messages in range.
MSGS="$(git log --format=%B "${BASE_SHA}..${HEAD_SHA}")"
# Look for assertions of the form "Claude Code uses ..." or
# "cli.js uses ..." (case-insensitive). For each hit, require
# a citation in the same commit message in one of the forms:
# cli.js:NNNN (line-number citation)
# cli.js vE4 <name> (version + function-name citation)
# Absence of a citation is a soft finding.
WARN=0
# Split log into per-commit blocks for precise matching.
git log --format="%H" "${BASE_SHA}..${HEAD_SHA}" | while read -r sha; do
BODY="$(git log -1 --format=%B "$sha")"
if echo "$BODY" | grep -E -i -q '(claude[[:space:]]+code|cli\.js)[[:space:]]+uses'; then
if echo "$BODY" | grep -E -q '(cli\.js:[0-9]+|cli\.js[[:space:]]+v[A-Za-z0-9]+[[:space:]]+[A-Za-z_][A-Za-z0-9_]*)'; then
echo "OK $sha: assertion cited."
else
echo "::warning::Commit $sha asserts 'Claude Code uses ...' or 'cli.js uses ...' but does not cite a cli.js line number or versioned function name. See CLAUDE.md -> Commit message conventions."
WARN=1
fi
fi
done
if [ "$WARN" -ne 0 ]; then
echo "Soft check raised warnings. Reviewer: please enforce per CLAUDE.md."
else
echo "Commit citation soft check clean."
fi