Skip to content

Commit 7b890ee

Browse files
committed
Merge remote-tracking branch 'upstream/main' into maintain/pr-623
# Conflicts: # headroom/proxy/server.py
2 parents a5ee953 + 37faf2f commit 7b890ee

296 files changed

Lines changed: 22630 additions & 2099 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/audit.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# cargo-audit configuration for the Rust workspace.
2+
#
3+
# Path matters: cargo-audit reads `.cargo/audit.toml`, not a root-level
4+
# `audit.toml`. A file at the repo root is silently ignored.
5+
#
6+
# The `audit` job in .github/workflows/rust.yml is a BLOCKING gate. It runs on
7+
# every PR touching Rust and nightly on the schedule (the `rust-changes` job
8+
# reports `rust=true` for `schedule`/`workflow_dispatch`, so a newly-disclosed
9+
# advisory surfaces without anyone touching Rust code).
10+
#
11+
# It was `continue-on-error: true` until the change that added this file, which meant it reported findings
12+
# nobody saw: RUSTSEC-2026-0258 (h2, unbounded empty DATA frames) sat in a green
13+
# run. Anything ignored here has to be listed explicitly, with a reason.
14+
15+
[advisories]
16+
ignore = [
17+
# `paste` is unmaintained — an advisory of project status, not a
18+
# vulnerability; there is no patched version to move to. It is transitive
19+
# and unavoidable at our layer: tokenizers -> paste and rav1e -> paste,
20+
# both reached via fastembed. Re-evaluate when tokenizers moves to
21+
# `pastey` (the maintained drop-in fork).
22+
"RUSTSEC-2024-0436",
23+
]

.claude-plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
},
66
"metadata": {
77
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
8-
"version": "0.34.0"
8+
"version": "0.36.1"
99
},
1010
"plugins": [
1111
{
1212
"name": "headroom",
1313
"source": "./plugins/headroom-agent-hooks",
1414
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
15-
"version": "0.34.0",
15+
"version": "0.36.1",
1616
"author": {
1717
"name": "Headroom Contributors",
1818
"url": "https://github.com/chopratejas/headroom"

.commitlintrc.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"extends": ["@commitlint/config-conventional"],
33
"rules": {
4-
"body-max-line-length": [2, "always", 200],
4+
"body-max-line-length": [0],
55
"footer-leading-blank": [0],
66
"subject-case": [0],
77
"type-enum": [
@@ -12,6 +12,7 @@
1212
"chore",
1313
"ci",
1414
"docs",
15+
"deps",
1516
"feat",
1617
"fix",
1718
"parity",
@@ -23,4 +24,4 @@
2324
]
2425
]
2526
}
26-
}
27+
}

.env.example

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1-
# Copy this file to .env and fill in real values before running in production.
2-
# IMPORTANT: Change NEO4J_AUTH before deploying — default credentials are insecure.
1+
# Copy this file to .env and fill in real values before running.
2+
# docker-compose.yml requires these — it will refuse to start with defaults.
3+
4+
# Neo4j credentials for the graph memory backend (format: user/password).
35
NEO4J_AUTH=neo4j/CHANGEME
6+
# Password only, for library / non-Docker use of the Neo4j memory backend.
7+
NEO4J_PASSWORD=CHANGEME
8+
9+
# Proxy token — gates the data plane whenever the proxy is not loopback-only.
10+
# Generate: openssl rand -hex 32
11+
HEADROOM_PROXY_TOKEN=CHANGEME
12+
13+
# Optional: set to 0.0.0.0 to expose the proxy on the network (requires a token).
14+
# HEADROOM_BIND_ADDR=127.0.0.1

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ Closes #
4040
- Observed result:
4141
- Not tested:
4242

43+
## Runtime Rollout Safety
44+
45+
- Rollout-managed feature(s):
46+
- Minimum rollout channel:
47+
- Stable/default behavior changed:
48+
- Kill switch / disable path:
49+
- Unsafe override required:
50+
- Qualification impact:
51+
- Rollback path:
52+
4353
## Review Readiness
4454

4555
- [ ] I have performed a self-review
Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
{
2-
"action": "ready_for_review",
3-
"number": 42,
4-
"pull_request": {
5-
"number": 42,
6-
"draft": false,
7-
"title": "feat: add PR governance",
8-
"body": "## Description\n\nAdd a required PR governance check and commit-msg enforcement.\n\nCloses #123\n\n## Type of Change\n\n- [x] New feature (non-breaking change that adds functionality)\n\n## Changes Made\n\n- Added workflow validation for PR template completeness.\n- Added a commit-msg hook that runs commitlint locally.\n\n## Testing\n\n- [x] Unit tests pass (`pytest`)\n- [x] Manual testing performed\n\n### Test Output\n\n```text\npytest scripts/tests/test_pr_governance.py -q\n```\n\n## Real Behavior Proof\n\n- Environment: Ubuntu runner, Python 3.12\n- Exact command / steps: Opened a PR with an incomplete template, then fixed the body.\n- Observed result: The governance check failed until the template and readiness boxes were complete.\n- Not tested: Repository-level automatic Copilot rulesets.\n\n## Review Readiness\n\n- [x] I have performed a self-review\n- [x] This PR is ready for human review\n",
9-
"user": {
10-
"login": "octocat"
11-
},
12-
"base": {
13-
"sha": "dff6a199"
14-
}
15-
},
16-
"repository": {
17-
"full_name": "JerrettDavis/headroom"
18-
}
19-
}
1+
{
2+
"action": "ready_for_review",
3+
"number": 42,
4+
"pull_request": {
5+
"number": 42,
6+
"draft": false,
7+
"title": "feat: add PR governance",
8+
"body": "## Description\n\nAdd a required PR governance check and commit-msg enforcement.\n\n## Type of Change\n\n- [x] New feature (non-breaking change that adds functionality)\n\n## Changes Made\n\n- Added workflow validation for PR template completeness.\n\n## Testing\n\n- [x] Unit tests pass (`pytest`)\n\n### Test Output\n\n```text\npytest scripts/tests/test_pr_governance.py -q\n```\n\n## Real Behavior Proof\n\n- Environment: Ubuntu runner, Python 3.12\n- Exact command / steps: Opened a PR and ran governance.\n- Observed result: The check passed with complete facts.\n- Not tested: Repository settings.\n\n## Runtime Rollout Safety\n\n- Rollout-managed feature(s): None.\n- Minimum rollout channel: Stable.\n- Stable/default behavior changed: No.\n- Kill switch / disable path: Not applicable.\n- Unsafe override required: No.\n- Qualification impact: None.\n- Rollback path: Revert the workflow and script changes.\n\n## Review Readiness\n\n- [x] I have performed a self-review\n- [x] This PR is ready for human review\n",
9+
"user": {"login": "octocat"},
10+
"base": {"sha": "dff6a199"}
11+
},
12+
"repository": {"full_name": "JerrettDavis/headroom"}
13+
}

.github/plugin/marketplace.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
},
66
"metadata": {
77
"description": "Headroom marketplace for Claude Code and GitHub Copilot CLI plugins.",
8-
"version": "0.34.0"
8+
"version": "0.36.1"
99
},
1010
"plugins": [
1111
{
1212
"name": "headroom",
1313
"source": "./plugins/headroom-agent-hooks",
1414
"description": "Headroom startup hooks for Claude Code and GitHub Copilot CLI.",
15-
"version": "0.34.0",
15+
"version": "0.36.1",
1616
"author": {
1717
"name": "Headroom Contributors",
1818
"url": "https://github.com/chopratejas/headroom"
180 KB
Loading

.github/workflows/docker.yml

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,10 +204,10 @@ jobs:
204204
- name: Upload digest marker
205205
uses: actions/upload-artifact@v7
206206
with:
207-
# Variant + arch in the artifact name so the manifest job can
208-
# download with `pattern: digests-<variant>-*` to gather all
209-
# arches for one variant. `root` substitutes the empty-string
210-
# variant since GHA artifact names can't end in a hyphen.
207+
# Variant + arch uniquely identify the marker. The manifest job
208+
# downloads both architecture artifacts by exact name; a glob such
209+
# as `digests-code-*` would also match code-nonroot/code-slim.
210+
# `root` substitutes the empty-string variant.
211211
name: digests-${{ matrix.variant.name || 'root' }}-${{ matrix.arch.name }}
212212
path: ${{ runner.temp }}/digests/*
213213
if-no-files-found: error
@@ -273,12 +273,17 @@ jobs:
273273
username: ${{ github.actor }}
274274
password: ${{ secrets.GITHUB_TOKEN }}
275275

276-
- name: Download per-arch digests for this variant
276+
- name: Download amd64 digest for this variant
277277
uses: actions/download-artifact@v8
278278
with:
279-
pattern: digests-${{ matrix.variant.name || 'root' }}-*
279+
name: digests-${{ matrix.variant.name || 'root' }}-amd64
280+
path: ${{ runner.temp }}/digests
281+
282+
- name: Download arm64 digest for this variant
283+
uses: actions/download-artifact@v8
284+
with:
285+
name: digests-${{ matrix.variant.name || 'root' }}-arm64
280286
path: ${{ runner.temp }}/digests
281-
merge-multiple: true
282287

283288
# Same tag rules as the pre-fan-out workflow — preserve every
284289
# tag flavor (semver, ref, sha-prefixed, version-suffixed,
@@ -288,6 +293,18 @@ jobs:
288293
uses: docker/metadata-action@v6
289294
with:
290295
images: ${{ env.REGISTRY }}/${{ steps.image-name.outputs.image_name }}
296+
# `latest=false` is load-bearing (#3150). The action defaults to
297+
# `latest=auto`, which appends a bare `latest` for any semver
298+
# release — and it logs `suffixLatest=false`, so the per-tag
299+
# `suffix=` below never reaches it. Every one of the 8 variant
300+
# cells therefore pushed `ghcr.io/.../headroom:latest`, and the
301+
# last cell to finish won. At 0.36.0 that was `code-slim`, so
302+
# `:latest` resolved to the distroless build, whose
303+
# `import onnxruntime` segfaults on arm64 — `headroom deploy`
304+
# crash-looped on Apple Silicon. `:latest` has exactly one
305+
# writer: the root-cell promotion step at the end of this job.
306+
flavor: |
307+
latest=false
291308
tags: |
292309
type=ref,event=branch,enable=${{ inputs.enable_ref_tags != 'false' && github.event_name != 'release' }},suffix=${{ matrix.variant.name != '' && format('-{0}', matrix.variant.name) || '' }}
293310
type=ref,event=pr,enable=${{ inputs.enable_ref_tags != 'false' && github.event_name != 'release' }},suffix=${{ matrix.variant.name != '' && format('-{0}', matrix.variant.name) || '' }}
@@ -306,6 +323,9 @@ jobs:
306323
env:
307324
IMAGE: ${{ env.REGISTRY }}/${{ steps.image-name.outputs.image_name }}
308325
DIGEST_DIR: ${{ runner.temp }}/digests
326+
# Read by the bare-`latest` guard below. Via `env:` rather than
327+
# inline `${{ }}` so the value is never spliced into the script.
328+
VARIANT_NAME: ${{ matrix.variant.name }}
309329
run: |
310330
# Reconstruct full image references from the digest marker
311331
# filenames (each file is named after the bare hex digest
@@ -325,6 +345,16 @@ jobs:
325345
digest_refs+=("${IMAGE}@sha256:${digest}")
326346
done
327347
348+
# Belt-and-braces for #3150: only the root cell may ever carry a
349+
# bare `latest`. A suffixed variant reaching this point with one
350+
# means the tag rules regressed, and shipping it would repoint
351+
# `:latest` at a non-default image. Fail instead of publishing.
352+
if [ -n "${VARIANT_NAME}" ] && jq -e '.tags[]? | select(endswith(":latest"))' \
353+
<<< "${DOCKER_METADATA_OUTPUT_JSON}" >/dev/null 2>&1; then
354+
echo "::error::variant '${VARIANT_NAME}' would publish a bare :latest tag" >&2
355+
exit 1
356+
fi
357+
328358
# Build `--tag` args from the metadata-action JSON output.
329359
# Empty tags array is valid (PR builds without ref-tags
330360
# enabled emit nothing); skip manifest creation in that case.

.github/workflows/release-metadata-sync.yml

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,26 @@ jobs:
5050
runs-on: ubuntu-latest
5151
timeout-minutes: 10
5252
steps:
53+
# Prefer a short-lived, repo-scoped GitHub App installation token over a
54+
# personal PAT. Gated on the repo variable so an unconfigured app simply
55+
# falls through to the existing chain instead of breaking the release.
56+
- name: Mint installation token
57+
id: app-token
58+
if: ${{ vars.RELEASE_APP_ID != '' }}
59+
continue-on-error: true
60+
uses: actions/create-github-app-token@v3
61+
with:
62+
app-id: ${{ vars.RELEASE_APP_ID }}
63+
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
64+
5365
- uses: actions/checkout@v7
5466
with:
5567
ref: ${{ github.ref_name }}
56-
# PAT (not GITHUB_TOKEN) for the same reason release-please.yml uses one:
57-
# a push made with GITHUB_TOKEN does not trigger workflows, so the release
58-
# PR's checks would never re-run against the synced commit and would stay
59-
# red. Falls back to GITHUB_TOKEN, where the sync still lands and a manual
60-
# re-run of the PR's checks picks it up.
61-
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
68+
# Do NOT persist the credential into .git/config. The next step runs
69+
# scripts/version-sync.py *from the checked-out branch*, and this job
70+
# triggers on a push to the unprotected glob release-please--branches--**.
71+
# A persisted token would be readable by that script.
72+
persist-credentials: false
6273

6374
- uses: actions/setup-python@v6
6475
with:
@@ -72,6 +83,14 @@ jobs:
7283
run: python scripts/verify-versions.py
7384

7485
- name: Commit and push if anything changed
86+
env:
87+
# An app installation token if one was minted, else the existing
88+
# chain. A PAT (not GITHUB_TOKEN) is still preferred here for the same
89+
# reason release-please.yml wants one: a push made with GITHUB_TOKEN
90+
# does not trigger workflows, so the release PR's checks would never
91+
# re-run against the synced commit and would stay red. Supplied only
92+
# to this step, after the branch-supplied script has already run.
93+
SYNC_TOKEN: ${{ steps.app-token.outputs.token || secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
7594
run: |
7695
if git diff --quiet; then
7796
echo "Already in sync — nothing to commit."
@@ -81,7 +100,12 @@ jobs:
81100
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
82101
git add -A
83102
git commit -m "chore: sync generated version metadata"
103+
# Push via an explicit remote URL because the checkout no longer
104+
# persists credentials. Passed on stdin-free env expansion so the
105+
# token is not written to the command line or into .git/config.
84106
# This push re-triggers this workflow. version-sync.py is idempotent, so
85107
# the next run finds no diff and exits above without pushing — the loop
86108
# terminates after one no-op run.
87-
git push origin HEAD:"${GITHUB_REF_NAME}"
109+
git push \
110+
"https://x-access-token:${SYNC_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
111+
HEAD:"${GITHUB_REF_NAME}"

0 commit comments

Comments
 (0)