Commit Graph
11 Commits
Author SHA1 Message Date
taodengandClaude Opus 5 79656b485f ci: drop the alignment.yml paths change — it would have been a green check for a check that never ran
Reverting my own half of this PR. The reviewer proved the alignment.yml change
was theatre, and my issue #198 was filed on a wrong premise.

What I verified myself before reverting:
  - the alignment job BODY references models.json zero times; only the paths
    filter I added mentioned it
  - test.yml is `pull_request:` with NO paths filter, so it already runs on
    every PR, models.json-only ones included
  - on a deliberately corrupted models.json (contextWindow 1000000, a typo'd
    openclawName), `npm test` catches it: 455 passed, 2 failed

So the real guard on models.json already existed and always ran. Adding the
path would only have made a job execute that inspects nothing about
models.json, and then reported a green "Alignment Guardrail" — implying a check
that did not happen. That is strictly worse than the job not running, which is
precisely the reviewer's point.

#198's premise was also wrong. The blacklist greps server.mjs for known
hallucinated tokens. A models.json-only PR cannot introduce a token into
server.mjs, so CLAUDE.md hard-requirement #2 is INAPPLICABLE on such a PR, not
vacuous. I read "the guardrail didn't run" as "coverage gap" without checking
what the guardrail actually inspects.

The release.yml half is unaffected and stays: that one is a real, reproduced
failure (the no-CHANGELOG branch never wrote the file the create step consumes).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8
2026-07-27 09:05:33 +10:00
taodengandClaude Opus 5 66096cdcab ci: cover models.json in the alignment guardrail; fix release.yml's no-CHANGELOG path
Two CI-layer fixes found during the v3.25.0 review. Same layer, both small, so
they land together (Iron Rule 11).

#198 — alignment.yml did not run on a models.json-only PR.
The `paths:` filter listed server.mjs / setup.mjs / scripts / lib / ocp /
ocp-connect, but not models.json. That made CLAUDE.md hard-requirement #2 ("CI
blacklist pass") vacuous for exactly the PRs that change model routing:
confirmed on #192, where only gitleaks and test-features ran.

models.json is not inert data. It drives MODEL_MAP and VALID_MODELS, the
default request model, and — since ADR 0009 — the global MAX_PROMPT_CHARS
truncation budget. A models.json-only PR can therefore change server.mjs's
runtime behavior with the guardrail never running. models.schema.json is
included for the same reason one level up: loosening the schema is what would
let a malformed models.json through.

Adding paths only widens coverage; the blacklist grep still scans server.mjs,
so this costs nothing and closes the gap. Per CLAUDE.md this is a PR amendment
to alignment.yml, which is the sanctioned way to change it (removing entries
would need an ALIGNMENT.md amendment; nothing is removed here).

#202 — release.yml's no-CHANGELOG fallback would have failed the release job.
The branch wrote an output named `notes` and exited WITHOUT creating
/tmp/release-notes.md, while the create step hard-codes
`--notes-file /tmp/release-notes.md`. So the one path that exists to "degrade
to minimal notes" instead produced a failed release.

Verified both directions by extracting the step's actual script from the YAML
and running it, rather than reading it:

  PRE-FIX,  no CHANGELOG -> exit 0, GITHUB_OUTPUT="notes=Release v3.25.0",
                            /tmp/release-notes.md MISSING
                            -> gh release create --notes-file WOULD FAIL
  POST-FIX, no CHANGELOG -> exit 0, notes_file set, file present ("Release v3.25.0")
  POST-FIX, with CHANGELOG -> file present, first line "## v3.25.0 — 2026-07-27"

Fixed by writing the file in that branch, and by removing the hard-coded-path
coupling entirely: both branches now set `notes_file` and the create step
consumes `steps.notes.outputs.notes_file`. That output existed already and was
never read — the two only agreed by accident.

Also added `set -euo pipefail` (the step previously ran unset-tolerant) and an
echo of the resolved notes before creating the release, so a wrong-looking body
is visible in the job log instead of only on the published release.

NOT changed: the empty-extraction guard. My issue text suggested adding one,
but `if [ ! -s "$NOTES" ]` was already there and already handles a heading
mismatch. Correcting that claim on #202 rather than taking credit for it.

Both workflows re-validated as YAML.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8
2026-07-27 08:51:09 +10:00
6dff36959a chore(governance): pin legacy OAuth host in alignment.yml + ALIGNMENT.md amendment (#123) (#128)
Follow-up to the 2026-05-31 audit (deferred from #112). The OAuth token host
platform.claude.com/v1/oauth/token was verified against the compiled cli.js in #119;
this pins the legacy WRONG host so a future accidental revert hard-fails CI.

- .github/workflows/alignment.yml: add "console.anthropic.com/v1/oauth/token" to the
  BLACKLIST; rewrite the comment + failure message so the blacklist now documents TWO
  kinds of token — known hallucinations AND pinned wrong-host variants of a verified
  Class A endpoint (a hit means a drift, not necessarily a hallucination). The pinned
  token is absent from server.mjs (which uses platform.claude.com), so CI stays green.
- ALIGNMENT.md: new "OAuth token-host verification (2026-05-31)" subsection recording the
  binary verification (claude.exe 2.1.154, strings, no live probe) and the dual-purpose
  blacklist policy. Purely additive; Rules / audit pin / Historical Lesson untouched.

Per ALIGNMENT.md Amendment Procedure: (a) motivating evidence cited (issues #112/#119/#123),
(b) independent fresh-context opus reviewer APPROVE — verified the pinned token does not
trip the build (absent from server.mjs; live host not blacklisted), YAML valid, amendment
consistent with the server.mjs verification comment, purely additive scope. (c) not
incident-driven (a confirming verification, not a new drift) so Historical Lesson unchanged.

Closes #123.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 08:43:36 +10:00
1dd6fb9440 docs(governance): add ADR 0006 OpenAI shim scope + Class A/B taxonomy (#100)
Introduces an explicit two-class taxonomy of OCP endpoints to resolve
the structural ambiguity surfaced by PR #99 (external response_format
honoring on /v1/chat/completions):

- Class A: cli.js-mirror endpoints. Rules 1-5 of ALIGNMENT.md apply
  verbatim. Citation requirement unchanged (cli.js:NNNN). The 2026-04-11
  drift discipline is preserved without weakening.

- Class B: OCP-owned compatibility endpoints. Anchored to OpenAI's
  /v1/chat/completions specification (B.1) or to an authorizing ADR
  (B.2). Citation shifts to spec section + ADR number. Class B inherits
  the same anti-invention discipline; the anchor differs.

Grandfather provision in ADR 0006 retroactively authorizes the 12
existing B.2 administrative endpoints at v3.16.4 behaviour (one-time,
contract-frozen). New B.2 endpoints or any new method on a grandfathered
endpoint requires its own ADR per Rule 4 (Class B mapping).

Changes:
- new: docs/adr/0006-openai-shim-scope.md
- new: docs/openai-compat-pin.md (placeholder for first B.1 audit)
- mod: ALIGNMENT.md (Class B section, rule mapping table, updated
  Unalignable Policy and Annual Audit scope; Rules 1-5 byte-identical)
- mod: .github/PULL_REQUEST_TEMPLATE.md (Endpoint Class radio, separate
  evidence sections for A vs B, reviewer checklist updated)
- mod: docs/adr/README.md (index entry for ADR 0006)

Independent reviewer (fresh-context opus per Iron Rule 10) verified:
all 12 load-bearing checks pass — Rules 1-5 byte-identical to
origin/main, 2026-04-11 drift narrative unchanged, explicit
non-relitigation safeguard present in ADR 0006, grandfather provision
narrowly scoped, Class B inventory matches server.mjs reality (14/14),
single governance layer per Iron Rule 11 (no server.mjs touch),
alignment.yml unmodified. Verdict: APPROVE_WITH_MINOR with 3 of 5
non-blocking suggestions folded in (operations-vs-endpoints precision,
PR template Hybrid wording, openai-compat-pin.md stub).

Triggering incident: PR #99 by external contributor (response_format
honoring on /v1/chat/completions). This governance PR ships separately
per Iron Rule 11 (IDR); the feature PR #99 will rebase on this and
declare Class B with ADR 0006 citation.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 21:01:10 +10:00
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
342a0a44f5 docs(funding): add Buy Me a Coffee link + GitHub FUNDING.yml (#68)
- .github/FUNDING.yml — enables GitHub's native "Sponsor" button on the
  repo page, pointing to buymeacoffee.com/dtzp555. Other platforms
  (GitHub Sponsors, Ko-fi) are commented out and can be enabled later
  by uncommenting + filling in handles.
- README.md § Support OCP — new section just before License. States the
  free-and-open-source commitment, lists the kinds of work that don't
  show up in commits (multi-machine debugging, IDE validation, drift
  incidents, concurrency leaks), and offers a single  link for users
  who want to support continued maintenance. Explicitly disclaims paid
  tiers / premium features so the open-source posture stays unambiguous.

server.mjs is not modified; this commit is doc-only and therefore exempt
from the cli.js citation requirement (ALIGNMENT.md Rule 5 applies only
to commits that touch server.mjs).

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 03:29:27 +10:00
c998d21a4f chore(ci): add gitleaks workflow to scan PRs and pushes to main (#64)
The repo has shipped `.gitleaks.toml` (with project-specific allowlist
entries — public OAuth client ID, README placeholders, an old plan doc)
since the privacy remediation work, but no GitHub Action invoked it.
The config was orphan: real protection only when someone ran gitleaks
locally, never gating merges.

This workflow wires `.gitleaks.toml` into CI:

- Triggers on every `pull_request` (any branch) and `push` to `main`.
- Uses `gitleaks/gitleaks-action@v2`, which auto-detects the repo-root
  `.gitleaks.toml` and applies its allowlist.
- Hard-fails on any leak. No `continue-on-error`. Public repo policy.
- `permissions: contents: read` — minimum required scope.
- `fetch-depth: 0` so the action can scan full history (the action's
  default behavior; explicit here for clarity).

Verification path:
- The workflow runs on this PR itself; if any secret were ever committed
  to the repo, the scan fails here. Prior audit confirmed the tracked
  tree is clean of real secrets, so this PR's own scan should pass.

Refs: audit side-finding 4 of 4.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 09:54:36 +10:00
0752f666fb test: wire test-features.mjs to npm test + add minimal CI smoke workflow (#60)
`test-features.mjs` shipped at v3.8.0 (per CHANGELOG of the keys.mjs
quota+cache work) and is referenced from AGENTS.md as the project's only
test artifact, but until now nothing actually ran it — no `npm test`
script, no CI step. Wiring it up so it runs on every push and PR.

### Changes

- `package.json`: add `"test": "node test-features.mjs"` to scripts.
- `.github/workflows/test.yml` (new): single-job workflow that runs
  `npm test` on push to main and on every PR. Uses Node 24 because
  `keys.mjs` imports `node:sqlite`, which is stable in Node 23+ (Node
  24 is the current LTS; Node 22 would need `--experimental-sqlite`).
  No `npm install` step — OCP has zero external runtime dependencies
  per `package-lock.json`.
- `AGENTS.md`: note that `test-features.mjs` runs via `npm test` and
  is enforced by `.github/workflows/test.yml`.

### Why this is a hard check, not a soft check

`test-features.mjs` is self-contained — it imports `keys.mjs` and
exercises the SQLite-backed key/quota/cache code paths against a
throwaway test DB at `~/.ocp/ocp-test.db`. It does NOT require:

- a live claude CLI binary
- a running OCP server
- any network access

So CI can run it as a real check; no `continue-on-error` needed.

### Local verification

```
$ npm test
[...]
=== Results: 24 passed, 0 failed ===
```

24 assertions cover createKey / listKeys / quota math / cache hash
determinism / cache TTL / clearCache. Exit code is 1 on any failure
(`process.exit(failed > 0 ? 1 : 0)` at the bottom of test-features.mjs).

### Future expansion

If the suite later grows to include tests that DO require a live claude
CLI or a running OCP, mark those steps `continue-on-error: true` (or
split them into a separate job). The comment in `test.yml` documents
this contract.

Refs: audit (test-features.mjs orphan / unrunnable in CI).

Co-authored-by: dtzp555 <dtzp555@gmail.com>
2026-05-05 09:44:25 +10:00
cff06439fa chore(privacy): remediation follow-up from ocp#44 (#45)
Three-part follow-up from the 2026-04-22 privacy postmortem:

1. OAUTH_CLIENT_ID verified as public Claude Code constant (not a secret).
   Added gitleaks allowlist entry. The value 9d1c250a-e61b-44d9-88ed-5944d1962f5e
   is the public PKCE client ID used by the Claude Code CLI — public clients in
   PKCE flows have no client secret, so the ID itself carries no secret value.
   Introduced in commit b87992f (the 2026-04-11 drift incident); the constant
   mirrors what cli.js embeds for its OAuth token-refresh flow.

2. README.md API key example made clearly fake (ocp_example12345abcde...) to
   avoid gitleaks false-positives and clarify to readers it is not real.
   The ocp_ prefix IS the real prefix (keys.mjs line 80: randomBytes(24).base64url),
   so the prior example could be mistaken for a real truncated key.

3. PR template: added Privacy self-check section for PUBLIC repos below the
   existing 5.3 user-visible change self-check section.

4. .gitleaks.toml: new file with allowlist for the three confirmed non-issues
   (OAUTH_CLIENT_ID constant, README placeholder regex, old plan doc path).

No code or behavior change beyond the docs, README, and new config file.

Closes part of ocp#44.

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 11:40:50 +10:00
497ff1dcd2 chore(governance): add release-kit overlay + PR self-check + auto-release workflow (#35)
Implements cc-rules v1.4's 第五律 5.5 project overlay requirement.

- CLAUDE.md: declare release_kit: YAML block (version_source,
  changelog, release_channel, docs_source, resource_lists,
  new_feature_doc_expectations, bootstrap_quirk_policy)
- .github/PULL_REQUEST_TEMPLATE.md: add 5.3 user-visible-change
  self-check section with reviewer gate instruction
- .github/workflows/release.yml (NEW): auto-create GitHub Release
  from CHANGELOG.md section on v* tag push. Idempotent (checks if
  release already exists). Closes the gap that caused v3.9.0 /
  v3.10.0 / v3.11.0 to each miss their GH Release.

Governance-only change. No code, no user-visible behavior change
(the workflow only fires on future tags). No README update needed.

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 05:16:38 +10:00
2853088261 [Constitution] Alignment principle + CI guardrails (addresses b87992f drift) (#20)
* docs(constitution): establish OCP alignment constitution + CI guardrails (PR A)

Introduces the OCP project constitution to structurally prevent the kind
of scope drift that produced commit b87992f on 2026-04-11 (the fabricated
"/api/oauth/usage" endpoint, which does not appear in cli.js and broke
the dashboard usage bar for nine days).

This PR is governance-only. It does not modify server.mjs, package.json,
or any runtime code. It is intentionally shipped as one reviewable unit
per Iron Rule 11 (governance is one layer).

Files added:
  - ALIGNMENT.md
      Supreme scope document. Core principle: OCP is a proxy layer for
      Claude Code, not an extension layer. Five binding Rules: grep
      cli.js first; no invention; match the implementation; unalignable
      features are deleted; commits cite cli.js line numbers. Includes
      the 2026-04-11 drift postmortem, the Unalignable Policy, and an
      Annual Alignment Audit fixed to 11 April each year.

  - CLAUDE.md
      Project session instructions. Flags ALIGNMENT.md as required
      reading before any code. Codifies three hard requirements for
      server.mjs changes: cli.js citation, CI blacklist pass, and an
      independent reviewer per Iron Rule 10. References CC 开发铁律
      Rules 10, 11, and 12.

  - .github/PULL_REQUEST_TEMPLATE.md
      Mandatory "Claude Code Alignment Evidence" section. Three author
      checkboxes (cli.js citation, scope justification if cli.js does
      not perform the op, commit-message citations). Reviewer checklist
      requires opening cli.js at the cited lines before approval. A PR
      with this section blank receives request-changes.

  - .github/workflows/alignment.yml
      Hard-fail blacklist on server.mjs for tokens "api/oauth/usage"
      and "api/usage" (scan restricted to server.mjs; ALIGNMENT.md and
      CLAUDE.md may quote them as historical references). Soft check
      over all PR commit messages for "Claude Code uses X" / "cli.js
      uses X" assertions lacking a cli.js:NNNN or cli.js vE4 <fn>
      citation.

Historical reference: b87992f ("fix: use dedicated /api/oauth/usage
endpoint for reliable plan data") asserted the endpoint was used by
Claude Code CLI. The string does not occur in cli.js. Root cause was
LLM hallucination accepted without grep verification. See ALIGNMENT.md
-> Historical Lesson for the full record.

Merge precondition: this PR must be approved by an independent reviewer
(Iron Rule 10). The drafter of this commit may not self-approve.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs(alignment): pin first audit to 2026-04-20 (cli.js 2.1.89, SHA-256 a9950ef6)

First annual alignment audit pin. Records the cli.js version and content
hash that the current ALIGNMENT.md codified implementations mirror.

- Claude Code version: 2.1.89
- cli.js SHA-256: a9950ef6407fdc750bddb673852485500387e524a99d42385cb81e7d17128e01
- Audit date: 2026-04-20
- Auditor: Tao Deng

Next audit: 2027-04-11 (drift anniversary).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci(alignment): narrow blacklist to full host+path + strip comments before grep

Two false positives discovered during PR #20 bootstrap CI:
1. /api/usage is a legitimate OCP dashboard route (per-key quota, added
   in v3.8, server.mjs:1472). The bare token "api/usage" was too broad.
2. The ANCHOR warning comment in server.mjs (added by PR #21) references
   /api/oauth/usage as a DO-NOT-USE example, triggering the scanner.

Fix: require full host "api.anthropic.com/api/oauth/usage" to ensure
only real outbound fetch calls trip the guard, and strip line comments
with sed before grep so historical ANCHOR warnings pass.

Amendment procedure (ALIGNMENT.md) still governs future blacklist
changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Oracle Public Cloud User <opc@instance-20230820-1333.subnet07301351.vcn07301351.oraclevcn.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 13:15:34 +10:00