feat(tui): OCP_TUI_FULL_TOOLS gate — -p-equivalent tools for single-user TUI #38
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: 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 | |
| # Blacklisted tokens — two kinds (see ALIGNMENT.md "OAuth token-host verification"): | |
| # (1) known LLM hallucinations (e.g. the 2026-04-11 /api/oauth/usage drift), and | |
| # (2) pinned wrong-host variants of a VERIFIED Class A endpoint (a hit means a | |
| # drift to a known-wrong host, not necessarily a hallucination). | |
| # Extend only via an ALIGNMENT.md amendment PR. Matched as fixed strings vs server.mjs. | |
| BLACKLIST=( | |
| "api.anthropic.com/api/oauth/usage" | |
| "console.anthropic.com/v1/oauth/token" | |
| ) | |
| 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 are either LLM hallucinations that never appeared in cli.js, | |
| or pinned wrong-host variants of a verified Class A endpoint (a drift). | |
| 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 |