Skip to content

feat(branch-trail): Layer 1 emit primitive for §9.4 CHIT trail - #1437

Merged
POWERFULMOVES merged 5 commits into
mainfrom
feat/branch-trail-emit-9.4-layer1
May 11, 2026
Merged

POWERFULMOVES merged 5 commits into
mainfrom
feat/branch-trail-emit-9.4-layer1

Conversation

@POWERFULMOVES

@POWERFULMOVES POWERFULMOVES commented May 8, 2026

Copy link
Copy Markdown
Owner

Summary

First of four PRs implementing AGNOTE4482 §9.4 — the last unchecked §9 hardening signoff item: "CHIT trail is recorded for branch lifecycle events (creation, PR link, merge, deletion) on NATS subject branch.<path-segments>.trail.v1."

Design was locked 2026-05-08 (per memory project_branch_trail_94_design). This PR ships Layer 1 — the canonical Python module that all higher layers (CLI, HTTP gateway, GH Actions workflow) wrap.

Why this lane is unblocked now

§9.4 was blocked since 2026-04-25 per AGNOTE4482PHI.t1.md:420: "§9.4 (blocked — CHIT trail wiring requires NATS bus)." The W6-bpm-NATS lane (PR #1425, merged 2026-05-08) hoisted publish_cgp() into pmoves/services/common/nats_client.py and proved the publish pattern. Layer 1 reuses that helper directly.

What's in this PR

pmoves/services/common/branch_trail.py     +250 lines (new module)
pmoves/tests/services/common/test_branch_trail.py  +326 lines (36 tests)

Public API

# Async one-shot publish (most common caller path)
await branch_trail.emit(
    branch="feat/w6-bpm-nats",   # original separators preserved
    event="merge",                # one of {create, link_pr, merge, delete}
    agent_id="claude-opus",       # signing identity
    pr_url="https://github.com/.../pull/1425",
    committer="DARKXSIDE",
    sha="1884e737ad",
)

# Build a signed payload without publishing (for HTTP gateway / dry-run)
payload = branch_trail.build_payload(branch, event, agent_id, ...)

# Public utility for consumers building wildcard subscriptions
subject = branch_trail.encode_subject("feat/foo")
# → "branch.feat.foo.trail.v1"

Subject encoding

Slashes in branch names map to NATS dot-tokens to enable wildcard subscriptions:

Branch Subject
main branch.main.trail.v1
feat/w6-bpm-nats branch.feat.w6-bpm-nats.trail.v1
chore/codex/parity branch.chore.codex.parity.trail.v1

Subscribers can filter by prefix: branch.feat.> matches all feat/* branches. Subject is not lossless when a branch name contains . — consumers MUST use payload branch_event.branch_name for the canonical name (one test documents this explicitly).

Reuses (zero changes to upstream code)

Reused Source
publish_cgp() async helper pmoves/services/common/nats_client.py:292-320 (PR #1425)
build_payload() identity skeleton pmoves/tools/sign_trail.py:154-211
sign_cgp() HMAC-SHA256 signer pmoves/tools/chit_security.py
agent_signatures.yaml lookup 16 cards already seeded

Auth

Same convention as existing CHIT signing: CHIT_SIGNING_KEY preferred, CHIT_PASSPHRASE legacy fallback. Function-level passphrase=... overrides env. Unsigned emit logs a warning but still publishes — best-effort semantics so a missing trail entry never blocks the operation that triggered it.

Tests

pytest pmoves/tests/services/common/test_branch_trail.py -v
36 passed in 0.60s

Coverage:

  • encode_subject: 13 cases — slash mapping, dashes/underscores, max length, NATS-special-char rejection, empty rejection
  • build_branch_event: 9 cases — event enum, ecosystem enum, fail-fast on invalid branch, all 4 event types
  • build_payload: 6 cases — unsigned warns, env signing, env legacy passphrase, param-overrides-env, summary format
  • emit: 5 cases — subject + payload via mocked publish_cgp, failure path, nats_url override, fail-fast on invalid branch/event
  • Subject roundtrip: 3 cases — wildcard ergonomics, segment count, documented non-lossless behavior

Roadmap

Layer PR Status
1. Python module + tests (this PR) this OPEN
2. CLI wrapper (python -m pmoves.tools.branch_trail) follow-up pending
3. HTTP gateway (/branch-trail/emit via Tailscale Funnel) follow-up pending
4. GH Actions workflow + spec amendments + §9.4 checkbox flip close-out pending

Lane

§9.4 design plan: C:/Users/russe/.claude/plans/yes-and-status-of-silly-lemon.md (5090-CLAUDE local; will be committed to repo with the close-out PR).
Memory pointer: project_branch_trail_94_design.md.
Spec amendments to AGNOTE4482_SIGNOFF_CHECKLIST.md:89 and AGNOTE4482_ROADMAP_W1-W5.md:523 ship in the final close-out PR after E2E validates.

Co-authored-by: DARKXSIDE cataclysmstudios@gmail.com

Summary by CodeRabbit

  • New Features

    • Added branch lifecycle event tracking with signed events and publish capability.
  • Improvements

    • Redacted sensitive info from connection logs.
    • Made signing metadata selection configurable via environment fallback.
    • CI dependency filtering deduplicates and preserves guarded specs for consistent installs.
    • CHIT contract checks now run conditionally based on detected changes.
  • Tests

    • Expanded tests for event emission, signing, URL redaction, and publish behavior.
  • Chores

    • CI workflow logic refactor for dependency installation.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8f473fd2-e99c-47ff-bf99-646374cbac55

📥 Commits

Reviewing files that changed from the base of the PR and between 3d11ee4 and 62661a4.

📒 Files selected for processing (1)
  • .github/workflows/chit-contract.yml

📝 Walkthrough

Walkthrough

Introduces branch lifecycle trail events for NATS publication. New branch_trail module builds and emits signed events (create, link_pr, merge, delete) with optional branch metadata. Supporting changes improve NATS URL logging safety, enable explicit endpoint configuration, update signature key ID defaults, and refactor CI test dependency filtering logic with normalized skip matching and preservation allowlists.

Changes

Branch Trail Feature

Layer / File(s) Summary
Branch Trail Constants and Schema
pmoves/services/common/branch_trail.py
Defined EVENT_TYPES, ECOSYSTEMS, SUBJECT_VERSION, and branch-name validation regex for safe NATS subject encoding.
Subject Encoding and Validation
pmoves/services/common/branch_trail.py
Added encode_subject(branch) to validate branch strings, replace path separators with dots, and return canonical NATS subject branch.<encoded>.trail.v1.
Event and Payload Builders
pmoves/services/common/branch_trail.py
Added build_branch_event(...) to construct events with validation and default timestamps, and build_payload(...) to assemble signed payloads with optional passphrase-based signing and environment variable fallbacks.
Event Emission and Publishing
pmoves/services/common/branch_trail.py
Added async emit(...) to orchestrate payload construction, subject encoding, and NATS publication via publish_cgp(...), plus module __all__ export.
NATS Infrastructure and URL Redaction
pmoves/services/common/nats_client.py
Added _redact_url() helper to strip credentials from NATS URLs for safe logging. Refactored publish_cgp() to construct NatsConnectionConfig with explicit nats_url and fixed name "cgp-publisher".
Signing Metadata Update
pmoves/tools/chit_security.py
Changed sign_cgp() default sig.kid to use CHIT_SIGNING_KEY_ID environment variable (fallback "chit-signing-v01") instead of hash-derived value.
Branch Trail Tests
pmoves/tests/services/common/test_branch_trail.py
Added comprehensive test coverage: encode_subject validation and rejection logic, build_branch_event field population and constraints, build_payload signed/unsigned behavior and passphrase precedence, async emit correctness and error propagation, and subject round-trip token structure.
NATS Client Tests
pmoves/tests/test_common/test_nats_client.py
Extended test coverage: _redact_url credential stripping, publish_cgp explicit nats_url configuration and cgp-publisher naming.
CI Dependency Skip and Preservation Logic
.github/workflows/python-tests.yml, .github/workflows/chit-contract.yml
Refactored test-job dependency installation to normalize skipped package identifiers (lowercase, dashes-to-underscores), introduce preserve_specs allowlist for full requirement specs, and deduplicate remaining packages by bare name; added CHIT contract workflow detection step and conditional execution for CHIT checks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • POWERFULMOVES/PMOVES.AI#1423: Proposes hoisting shared NATS publish helper (_nats_publish_cgp) that this PR refactors and extends via publish_cgp() integration with branch_trail.
  • POWERFULMOVES/PMOVES.AI#739: Implements sign_trail.py for Graphiti-signed payload publication; this PR's branch_trail module uses the same signing and publishing pattern with updated signature key ID defaults.
  • POWERFULMOVES/PMOVES.AI#302: Both modify CI workflow requirement parsing and skip logic; this PR adds normalized matching and preservation allowlists to the existing skip-filtering approach.

Poem

🐰 A branch trail, so fine and bright,
Events hop through NATS all day and night,
Signed payloads bound for glory's keep,
From create to merge, secrets run deep,
Skip lists normalized with care! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: implementing the Layer 1 emit primitive for §9.4 CHIT trail as specified in AGNOTE4482.
Description check ✅ Passed The PR description provides comprehensive context including summary, testing results (36 tests passed), roadmap, and required checks, but the optional follow-up tasks section is empty and reviewer notes are minimal.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/branch-trail-emit-9.4-layer1

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fabc92342

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pmoves/services/common/branch_trail.py
Comment thread pmoves/services/common/branch_trail.py
hunnibear and others added 2 commits May 9, 2026 12:05
Canonical Python module + 36 unit tests for AGNOTE4482 §9.4 — the last
unchecked §9 hardening signoff item. Implements the emit primitive that
all other layers (CLI wrapper, HTTP gateway, GH Actions workflow) will
wrap.

## Module: pmoves/services/common/branch_trail.py

Public API:
- `emit(branch, event, agent_id, ...)` — async one-shot publish to
  `branch.<path-segments>.trail.v1` via the W6-hoisted publish_cgp().
- `build_payload(...)` — produce the signed payload without publishing
  (used by HTTP gateway, tests, dry-run inspection).
- `build_branch_event(...)` — produce just the branch_event sub-object.
- `encode_subject(branch)` — public utility for consumers building
  wildcard subscriptions like `branch.feat.>.trail.v1`.

Module composes existing infrastructure with zero changes:
- pmoves.tools.sign_trail.build_payload  →  identity skeleton
- pmoves.tools.chit_security.sign_cgp    →  HMAC-SHA256 signing
- pmoves.services.common.nats_client.publish_cgp  →  NATS publish

## Subject pattern

Slashes in branch names map to NATS dot-tokens:
  feat/w6-bpm-nats  →  branch.feat.w6-bpm-nats.trail.v1

This enables wildcard subscriptions (`branch.feat.>.trail.v1`) that are
otherwise impossible with literal slashes. Subject is not lossless when
branches contain `.` — consumers MUST use payload `branch_event.branch_name`
for the canonical name (test documents this).

## Auth

Per existing PMOVES convention:
- `CHIT_SIGNING_KEY` (preferred) or `CHIT_PASSPHRASE` (legacy fallback)
  for HMAC keying. Function-level `passphrase=...` overrides env.
- `NATS_URL` for the bus address; function-level override available.
- Unsigned emit logs a warning but still publishes (best-effort
  semantics — a missing trail entry must not block the operation that
  triggered it).

## Tests: 36 passing

- encode_subject: 13 cases (slash mapping, dashes/underscores, max
  length, NATS-special-char rejection, empty-string rejection).
- build_branch_event: 9 cases (event enum, ecosystem enum, fail-fast
  on invalid branch, all 4 event types).
- build_payload: 6 cases (unsigned warns, env signing, env legacy
  passphrase, param-overrides-env, summary format, sig structure).
- emit: 5 cases (subject + payload via mocked publish_cgp, failure path,
  nats_url override, fail-fast on invalid branch/event).
- subject roundtrip: 3 cases (wildcard ergonomics, segment count,
  documented non-lossless behavior with dotted branch names).

```
pytest pmoves/tests/services/common/test_branch_trail.py -v
36 passed in 0.60s
```

## Lane

§9.4 design-locked 2026-05-08 (plan + memory `project_branch_trail_94_design`).
Layer 1 of 4: this PR. Layers 2 (CLI), 3 (HTTP gateway), and the GH
Actions workflow consumer ship in follow-up PRs once Layer 1 is on main.

Spec amendments to `AGNOTE4482_SIGNOFF_CHECKLIST.md:89` and
`AGNOTE4482_ROADMAP_W1-W5.md:523` (subject-pattern from
`branch.{branch_name}` → `branch.<path-segments>`) ship with the
final close-out PR after E2E validates.

Co-authored-by: DARKXSIDE <cataclysmstudios@gmail.com>
@POWERFULMOVES
POWERFULMOVES force-pushed the feat/branch-trail-emit-9.4-layer1 branch from 9fabc92 to ab5992b Compare May 9, 2026 16:09
@POWERFULMOVES

Copy link
Copy Markdown
Owner Author

Refresh after #1433/#1435 landed:

  • rebased this PR onto current main (8049ca071c)
  • added a small shared NATS helper fix found during Codex review: publish_cgp(nats_url=...) now actually builds the connection config from the explicit URL instead of falling back to module/env defaults
  • redacts NATS userinfo in the shared connection log path while touching that helper
  • tightened branch subject encoding to reject branch names that would produce empty NATS subject tokens

Local validation:

python -m pytest -q pmoves/tests/services/common/test_branch_trail.py pmoves/tests/test_common/test_nats_client.py pmoves/tests/tools/test_bpm_encoder_nats.py
55 passed

git diff --check
pass

@POWERFULMOVES
POWERFULMOVES requested a review from hunnibear May 9, 2026 16:10
@POWERFULMOVES

Copy link
Copy Markdown
Owner Author

5090 Codex refresh after CI follow-up:

  • rebased branch-trail lane onto origin/main (8049ca071c)
  • fixed publish_cgp(..., nats_url=...) so branch-trail explicit NATS URL overrides are honored
  • redacted NATS credentials in connection logs
  • tightened branch subject encoding to reject empty NATS tokens from consecutive/trailing separators
  • preserved pyreqwest-impersonate!=0.5.5 in .github/workflows/python-tests.yml so the Python Tests workflow no longer rebuilds the known-bad latest sdist
  • stopped deriving default CHIT kid by hashing signing key material; default now uses CHIT_SIGNING_KEY_ID or a stable fallback

Local validation:

  • git diff --check
  • python -m pytest -q pmoves/tests/services/common/test_branch_trail.py pmoves/tests/test_common/test_nats_client.py pmoves/tests/tools/test_bpm_encoder_nats.py pmoves/tests/test_chit_security.py::TestSignCgp pmoves/tests/test_chit_security.py::TestVerifyCgp pmoves/tests/test_chit_security.py::TestKeySeparation -> 82 passed

Remote validation on head e70af72361:

  • verify pass
  • merge-gate pass
  • python-tests pass
  • tests (3.11) pass
  • CodeQL pass
  • CodeRabbit pass
  • claude-review pass

@POWERFULMOVES
POWERFULMOVES enabled auto-merge (squash) May 9, 2026 16:30
hunnibear
hunnibear previously approved these changes May 9, 2026

@POWERFULMOVES POWERFULMOVES left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch refreshed after #1439 so the required CHIT verify check exists on this PR head. Local verification from this node: .venv\Scripts\python.exe -m pytest pmoves/tests/services/common/test_branch_trail.py -v → 46 passed in 0.64s. Current remaining gate is fresh write-access approval because the refresh commit made the prior approval stale.

@POWERFULMOVES
POWERFULMOVES merged commit 856252f into main May 11, 2026
19 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the feat/branch-trail-emit-9.4-layer1 branch May 11, 2026 10:23
POWERFULMOVES added a commit that referenced this pull request May 12, 2026
* feat(branch-trail): §9.4 Layer 4 workflow + spec amendment

Closes the last unchecked AGNOTE4482 §9 hardening item by wiring
branch lifecycle events through to the NATS CHIT trail.

Adds:
- .github/workflows/branch-trail-emit.yml — push/pull_request/delete
  triggers on a [self-hosted, ai-lab] runner, best-effort emit.
- .github/scripts/branch_trail_ci.py — pure event-parser shim that
  maps GitHub event payloads onto the Layer 1 emit() primitive.
- pmoves-ci-bot signing card (0035) for HMAC-signed CI emissions.
- 15 unit tests on the shim's parse_github_event() — green locally.

Amends the spec subject pattern from `branch.{branch_name}.trail.v1`
to `branch.<path-segments>.trail.v1` (slash→dot encoding) in:
- AGNOTE4482_SIGNOFF_CHECKLIST.md:89
- AGNOTE4482_ROADMAP_W1-W5.md:523
- stale-branch-sweep.yml:67 (comment only)

Builds directly on PR #1437 (Layer 1 emit primitive, merged 2026-05-11).
Defers Layer 2 (CLI wrapper) and Layer 3 (HTTP gateway) until a
non-tailnet emitter actually needs them — keeps the close-out PR small.

§9.4 checkbox flip deferred to a follow-up after live E2E proves all
four events (create / link_pr / merge / delete) arrive on the bus.

Co-Authored-By: DARKXSIDE <cataclysmstudios@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(branch-trail): wire existing CHIT pass-secret into emit workflow

The existing CHIT pass-secret has been in the repo since 2026-02-27.
L1's emit() already falls back through the env-var chain at line 173
of pmoves/services/common/branch_trail.py. Wiring this existing repo
secret directly into the workflow env means the operator only has to
set the new NATS_URL_TAILNET secret — signing works out of the gate
without generating fresh key material.

The CHIT_SIGNING_KEY env stays in the block for the future split-key
rollout (advisory mode per Owner-Decision D).

Co-Authored-By: DARKXSIDE <cataclysmstudios@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(agnote): add W0 follow-on REVIEW-REQUEST entry (issue #1463)

Captures the operator-observed drift root cause surfaced during the
§9.4 close-out: NATS host-bind + GH Actions runner registration +
mesh-bind override autogen all share the same hand-rolled-per-node
provenance. Three atomic PRs proposed in #1463 for review by
4090-CLAUDE / Z890-CLAUDE / CODEX-GPT5 / operator DARKXSIDE.

Not a claim — review/discussion only.

Co-Authored-By: DARKXSIDE <cataclysmstudios@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(agnote): add network-hardening REVIEW-REQUEST entry (issue #1465)

Sibling lane to #1463 — repo has 11 service-tier hardening anchors
but zero network-tier anchors for the 6 docker networks. Four atomic
PRs proposed in #1465. Sequences before #1463 PR-A (mesh-bind
auto-write) — the audit feeds the doctrine which feeds bootstrap.

Reviewers: Z890-CLAUDE, 4090-CLAUDE, CODEX-GPT5, operator DARKXSIDE.

Co-Authored-By: DARKXSIDE <cataclysmstudios@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* debug(branch-trail): structural fingerprint of NATS URL secret for E2E debug

* chore(branch-trail): remove diagnostic step after E2E verification

E2E proof landed live on NATS — HMAC-signed payload received from
GH Actions emit on pmoves-ai-lab-runner. Diagnostic served its
purpose finding the gh secret set syntax bug (--body - was treating
- as literal value, not stdin indicator).

* docs(agnote): §9.4 RELEASE — acceptance criterion satisfied in production

Workflow emit ran end-to-end on revived pmoves-ai-lab-runner.
HMAC-signed payload landed on NATS with signing_card_id 0035 and
real GH event metadata (committer, sha, runner). Follow-up: §9.4
checkbox flip after delete event proves on main.

Co-Authored-By: DARKXSIDE <cataclysmstudios@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(branch-trail): harden CI workflow — fork RCE guard + create event + best-effort wraps

Addresses 6 CodeRabbit threads on PR #1462:

- [T7 Critical] Add fork guard to emit job: prevents fork PRs from running
  on the tailnet-connected self-hosted runner (RCE via pull_request event)
- [T1 P1] Add `create` trigger + parse_github_event handler: branches created
  via GitHub UI/API now emit trail entries without a first push
- [T2 Major] Wrap _load_event_payload() and emit() in try/except: keeps the
  stated best-effort/exit-0 contract even on unexpected raises
- [T4 Critical] Add pmoves-ci-bot to agent_registry.yaml: signing card 0035
  was present but the agent was missing from the canonical registry
- [T5/T6 Major] Add branch.<path-segments>.trail.v1 to nats-subjects.md:
  documents the §9.4 subject pattern used by Layer 1 + Layer 4
- [T8] Diagnostic fingerprint step already removed (verified absent)
- [T3 False-positive] actionlint ai-lab label — not enforced in this repo

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

---------

Co-authored-by: Shaela Bello <slbello@uncg.edu>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants