Skip to content

Conversation

@thomas-mangin
Copy link
Member

🤖 Installing Claude Code GitHub App

This PR adds a GitHub Actions workflow that enables Claude Code integration in our repository.

What is Claude Code?

Claude Code is an AI coding agent that can help with:

  • Bug fixes and improvements
  • Documentation updates
  • Implementing new features
  • Code reviews and suggestions
  • Writing tests
  • And more!

How it works

Once this PR is merged, we'll be able to interact with Claude by mentioning @claude in a pull request or issue comment.
Once the workflow is triggered, Claude will analyze the comment and surrounding context, and execute on the request in a GitHub action.

Important Notes

  • This workflow won't take effect until this PR is merged
  • @claude mentions won't work until after the merge is complete
  • The workflow runs automatically whenever Claude is mentioned in PR or issue comments
  • Claude gets access to the entire PR or issue context including files, diffs, and previous comments

Security

  • Our Anthropic API key is securely stored as a GitHub Actions secret
  • Only users with write access to the repository can trigger the workflow
  • All Claude runs are stored in the GitHub Actions run history
  • Claude's default tools are limited to reading/writing files and interacting with our repo by creating comments, branches, and commits.
  • We can add more allowed tools by adding them to the workflow file like:
allowed_tools: Bash(npm install),Bash(npm run build),Bash(npm run lint),Bash(npm run test)

There's more information in the Claude Code action repo.

After merging this PR, let's try mentioning @claude in a comment on any PR to get started!

thomas-mangin and others added 30 commits November 9, 2025 16:49
The sys module was used in the __main__ block but not imported,
causing undefined name errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- E711: Change `!= None` to `is not None` in test_route_refresh.py
- E712: Change `== True` to direct boolean check in test_communities.py
- E721: Change `== dict` to `is dict` for type comparison in test_util.py

These changes follow Python best practices for comparisons.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Moved all imports to the top of files, after encoding/docstring but before
code. For files with environment setup, added noqa: E402 comments to imports
that must come after env configuration.

Changes:
- Reorganized imports in 13 test files
- Followed PEP 8 import order: stdlib, third-party, local
- Added noqa comments where env setup requires import deferral

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Used ruff --fix --unsafe-fixes to automatically remove assignments to
unused variables. This removes the assignment while keeping any side effects
(like advancing file reader positions).

Fixed 115 F841 errors across test files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added Python and ruff version output before running ruff check to help
debug version-specific issues in CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Rewrote nested f-string in code.py that used quote reuse (only valid in
Python 3.12+) to be compatible with Python 3.8.1+ as required by the
project.

Changed from:
  f'{' '.join(f'{k}={v}' for k, v in kargs.items())}\n'

To:
  ' '.join(f'{k}={v}' for k, v in kargs.items())
  (with separate newline write)

This was detected by ruff 0.14.4 which checks Python version compatibility.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Modified container workflow to wait for all critical CI checks (Unit Testing,
Linting, and Functional Testing) to pass before building and pushing the
container image.

Changes:
- Added multiple workflows to workflow_run trigger
- Added wait-on-check-action step to verify all required checks pass
- Prevents container builds when any CI check fails

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add named constants to replace magic numbers throughout the BGP
implementation, improving code readability and maintainability.

Phase 1 - Use existing constants (2 fixes):
- Use Parameter.CAPABILITIES and Parameter.AUTHENTIFICATION_INFORMATION
  instead of 0x02 and 0x01 in capabilities.py

Phase 2 - Add core protocol constants (23 fixes):
- AFI.address_length(): Add method to get address byte length (4 for
  IPv4, 16 for IPv6) with proper error handling
- HoldTime.MIN and HoldTime.KEEPALIVE_DIVISOR: Add RFC 4271 constants
  for minimum hold time (3 seconds) and keepalive calculation
- Capabilities.EXTENDED_PARAMS_MARKER: Add RFC 9072 constant for
  extended optional parameters (0xFF/255)
- Notification.SHUTDOWN_COMM_MAX_LEGACY/EXTENDED: Add RFC 8203/9003
  constants for shutdown communication length limits (128/255 bytes)

Phase 3 - Add data structure constants (11 fixes):
- RouteDistinguisher.TYPE_AS2_ADMIN/IPV4_ADMIN/AS4_ADMIN: Add RFC 4364
  type constants (0, 1, 2) and LENGTH constant (8 bytes)
- ESI.LENGTH: Add RFC 7432 constant for Ethernet Segment Identifier
  length (10 bytes), use in DEFAULT, MAX, __len__, and unpack
- Version.BGP_4: Add RFC 4271 constant for BGP protocol version (4)

All constants include RFC references and are used immediately to
eliminate corresponding PLR2004 violations.

Make ESI.DEFAULT explicit (use 0x00 instead of bytes())

Change ESI.DEFAULT from bytes(LENGTH) to bytes([0x00] * LENGTH)
for clarity - makes it obvious that it's zero-filled.

Rename EXTENDED_PARAMS_MARKER to EXTENDED_LENGTH per RFC 9072

RFC 9072 officially designates value 255 as the "Extended Length"
type code in the IANA BGP OPEN Optional Parameter Types registry.
The previous name EXTENDED_PARAMS_MARKER was semantically unclear
when used in comparisons like option_len == EXTENDED_PARAMS_MARKER.

Updated constant name to match RFC terminology:
- EXTENDED_PARAMS_MARKER → EXTENDED_LENGTH
- Reflects IANA designation as Extended Length type code
- Indicates extended format encoding (two-octet lengths)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
This commit addresses ruff PLR2004 violations by replacing magic numbers
with named constants throughout the codebase. The main changes include:

- Moved IP-related constants from module-level globals into IPv4/IPv6
classes
- Added uppercase constants: BYTES, BITS, DOT_COUNT, HOST_MASK,
COLON_MIN
- Kept lowercase bits/bytes attributes for backward compatibility
- Replaced scattered constants (IPV4_ADDRESS_BYTES, IPV4_LEN, etc.) with
  consistent class-based approach (IPv4.BYTES, IPv6.BYTES)

- src/exabgp/protocol/ip/__init__.py: Added constants to IPv4/IPv6
classes
- src/exabgp/data/check.py: Use IPv4.DOT_COUNT, IPv4.BITS, IPv6.BITS
- src/exabgp/configuration/flow/parser.py: Use IPv4.DOT_COUNT,
IPv6.COLON_MIN
  with >= operator (fixed IPv6 flow route parsing)
- src/exabgp/configuration/static/mpls.py: Added SRGB_TUPLE_SIZE,
ASN_MAX_VALUE,
  TEID_MAX_BITS constants
- src/exabgp/bgp/message/update/nlri/flow.py: Added flow validation and
encoding
  constants (MAX_PACKET_LENGTH, MAX_DSCP_VALUE, etc.)
- src/exabgp/bgp/message/update/nlri/cidr.py: Added CIDR_IPV4_MAX_MASK,
  CIDR_IPV6_LENGTH_BYTES
- BGP-LS files: Replaced magic numbers with descriptive constants

- Fixed IPv6 flow route encoding: Changed comparison from > to >= for
  IPv6.COLON_MIN in flow parser to correctly handle addresses like ::1
  which have exactly 2 colons

All functional tests (86 parsing + encoding) and unit tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Relocate RUFF_PROGRESS.md, RUFF_STATUS.md, and RUFF_VIOLATIONS.md to .claude/ for better organization of project documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The --ip-ifname argument help string contained an unescaped % character
in the example "192.165.14.1%eth0". When argparse formats help text using
Python's % string formatting, it interpreted %eth0 as a format placeholder,
causing a TypeError when trying to substitute it with argparse's internal
parameters dict.

Fixed by escaping the % as %% in the help string, which is the correct
way to represent a literal % character in Python string formatting.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Bumps [lewagon/wait-on-check-action](https://github.com/lewagon/wait-on-check-action) from 1.3.4 to 1.4.1.
- [Release notes](https://github.com/lewagon/wait-on-check-action/releases)
- [Changelog](https://github.com/lewagon/wait-on-check-action/blob/master/CHANGELOG.md)
- [Commits](lewagon/wait-on-check-action@v1.3.4...v1.4.1)

---
updated-dependencies:
- dependency-name: lewagon/wait-on-check-action
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
…lewagon/wait-on-check-action-1.4.1

Bump lewagon/wait-on-check-action from 1.3.4 to 1.4.1
The container workflow was failing because the wait-on-check-action was
looking for check names based on workflow names (Unit Testing, Linting,
Testing (Python 3.8 to 3.12)), but the GitHub check-runs API reports
job names, not workflow names. All three workflows had jobs named "build",
which caused the regex filter to fail to match any checks.

Changes:
- Renamed job in unit-testing.yml from "build" to "unit-tests"
- Renamed job in linting.yml from "build" to "lint"
- Renamed job in functional-testing.yml from "build" to "functional-tests"
- Updated container.yml check-regexp to match new job names:
  '^(unit-tests|lint|functional-tests) \('

This ensures the container build workflow can properly wait for all
required checks to complete before building and pushing the container.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed the linting workflow job name from "lint" to "lint-test" to be
consistent with the naming pattern of other test jobs (unit-tests,
functional-tests).

Updated the container workflow check-regexp accordingly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed from "lint-test" to "lint-tests" for consistency with other
test job names (unit-tests, functional-tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Document preference to commit changes as work progresses but not
automatically push to remote. This allows for commit review and
squashing before pushing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Reorganized the TOC into a 3-column layout to reduce vertical
screen space usage. Grouped related sections logically:
- Column 1: Getting Started & Installation
- Column 2: Usage & Documentation
- Column 3: Development

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

Restore anchor links for Installation and Development headers

Added back the clickable links for the Installation and Development
section headers in the TOC that were accidentally removed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Set TOC table to width="100%" and remove fixed column widths to
  prevent line wrapping and better utilize available screen space
- Remove incorrect migration guide link from Version Information section
  (was pointing to 3.4→4.x guide when discussing 4.x→5.0.x breaking changes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add wait_for_ack() helper function to all ExaBGP API example scripts
and update usage while preserving original timing behavior:

Function features:
- Configurable expected ACK count and timeout (default: 1 ACK, 1s timeout)
- Handles text mode: "done", "error", "shutdown"
- Handles JSON mode: {"answer": "done"} / {"answer": "error"}
- Returns True on success, False on timeout/error
- Raises SystemExit on shutdown signal
- Non-fatal failures (continues if ACKs disabled in config)

Timing preservation:
- Restore time.sleep() calls inside message loops (0.1s, 0.2s, or 1s per message)
- Call wait_for_ack() with hardcoded expected_count for known message counts
- Preserve initial delays and inter-loop delays

Special handling:
- api-add-remove.run: Skip 'sleep' token (don't send to ExaBGP), expect 4 ACKs
- api-broken-flow.run: Sleep 0.3s for 'sleep' tokens, 0.1s after real messages
- api-mvpn.run: Two loops (announce/withdraw) with 0.1s sleep, 6 messages each
- api-rib.run, api-rr-rib.run: Sequential commands with individual ACKs
- api-teardown.run: Variable timing delays per message (preserved)
- api-ipv4.run: Initial 3s sleep, then 0.2s per message (4 messages)
- api-ipv6.run: Initial 0.2s sleep, then 0.2s per message (4 messages)
- api-multiple-private.run: 1s sleep per message (not 0.2s)
- api-blocklist.run: Removed thread-based polling, replaced with wait_for_ack()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…AUDE.md

Add two new sections to guide Claude Code behavior:

1. Repository State Verification:
   - ALWAYS check git status before git operations
   - ALWAYS check git log history before commits
   - Verify user hasn't made manual changes
   - Never assume repository state
   - Prevents overwriting user's manual work

2. Testing Requirements:
   - Reference to .claude/docs/CI_TESTING_GUIDE.md
   - All CI test requirements (linting, unit, functional, legacy)
   - File descriptor limit requirement (≥64000)
   - Encoding test details:
     * Run in parallel (this is fine)
     * Before running: killall -9 python to clear leftover processes
     * Should complete in <20 seconds
     * Use --list or --short-list to see available tests
     * Run failed tests independently with --server/--client options
   - Quick test commands reference

These additions address the earlier mistake of not checking repository
state and provide clear testing guidance based on .claude/ folder content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Replace wait_for_ack() in all API example scripts with the improved
version from the wiki (API/API-Overview.md):

Improvements:
- Default timeout increased from 1s to 30s (more appropriate default)
- Unified JSON/text parsing with 'answer' variable (cleaner logic)
- Import json inside function (more self-contained)
- Better comments explaining JSON vs text formats
- Handles non-ACK messages gracefully (ignores BGP updates)
- More detailed documentation in docstring

The wiki version is more robust and better documented, making it
easier for users to understand and adapt for their use cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Add explicit, emphatic rules to CLAUDE.md to prevent automatic git operations:

CRITICAL GIT RULES:
- NEVER commit without explicit user request for that specific work
- NEVER push without explicit user request for that specific work
- Each task requires separate explicit instruction
- "commit and push" for previous work does NOT apply to new work
- Stop and wait for user instruction after completing work

This addresses the issue where commits/pushes were made automatically
after completing work, even when the user only said "commit and push"
for a previous, unrelated task.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The 30s timeout (from wiki version) was too long for these example scripts.
Reduced to 2s as a more reasonable timeout for API acknowledgment responses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements per-connection ACK state management to allow external processes
to control whether they receive acknowledgment responses from ExaBGP.

New API commands:
- disable-ack: Sends final 'done' ACK, then disables ACKs for this connection
- enable-ack: Re-enables ACKs for this connection
- silence-ack: Immediately disables ACKs (no ACK for this command itself)

ACK state is per-connection and does not affect other processes.

Implementation details:
- Renamed global ACK setting from self.ack to self._default_ack for clarity
- Per-process ACK state stored in self._ack dictionary
- Direct dictionary access (not .get()) to catch bugs for non-existent processes
- Added force parameter to answer_done() to send ACK even when disabled
- Command handlers call set_ack() before answer_done() for proper ordering
- Process configuration can override global ACK default via 'ack' setting
  (allows internal/critical processes to force ACK on regardless of global setting)

Changes:
- src/exabgp/reactor/api/processes.py: Add per-process ACK state tracking
- src/exabgp/reactor/api/command/reactor.py: Add three ACK control commands
- etc/exabgp/api-ack-control.conf: Test configuration for disable/enable-ack
- etc/exabgp/run/api-ack-control.run: Test script for disable/enable-ack
- etc/exabgp/api-silence-ack.conf: Test configuration for silence-ack
- etc/exabgp/run/api-silence-ack.run: Test script for silence-ack
- qa/encoding/api-ack-control.ci/.msg: Functional test for disable/enable-ack
- qa/encoding/api-silence-ack.ci/.msg: Functional test for silence-ack

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
thomas-mangin and others added 27 commits November 20, 2025 14:51
- Enhanced neighbor command detection using registry metadata
- Improved neighbor IP caching with concurrency protection
- Show neighbor JSON now includes all configured neighbors
- Added socket connection error handling with proper cleanup
- Ctrl+C now properly exits interactive CLI
- Added comprehensive test coverage for completion scenarios

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Update TESTING_DISCIPLINE.md to enforce mandatory regression test coverage
for all code changes including bug fixes, new features, and refactoring.

Changes:
- Add "Regression Prevention" section with requirements per change type
- Update workflow: Write tests → Make changes → Run tests
- Update checklist: Regression tests now mandatory before claiming "complete"
- Add examples showing correct vs incorrect approach
- Update forbidden phrases to require both testing AND regression tests

This ensures all code changes include appropriate tests to prevent future
regressions, addressing a gap in the testing discipline protocol.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…pretty-printing

- Auto-expansion: Unambiguous tokens auto-expand on TAB completion
- Enhanced completions: All options show one-per-line with descriptions
  - Commands: Show purpose/explanation
  - Neighbor IPs: Show AS number and BGP state
  - Options: Show what they do
  - Color-coded by type (yellow=commands, cyan=neighbors, green=options)
- JSON pretty-printing: All JSON output formatted with 2-space
indentation
- Filter '#' from completions (comment command, not useful
interactively)
- Add descriptions for all base commands (help, version, crash, etc.)

Tests: 15 new unit tests (completion + JSON formatting)
- test_auto_expand_unambiguous_token
- test_pretty_print_json_object
- test_json_with_unicode
- And 12 more

All tests passing: 1644/1644 unit, 71/72 functional (98.6%)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Single command to run all 6 required test suites:
- Ruff format
- Ruff check
- Unit tests (1645 tests)
- Functional encoding tests (72 tests)
- Functional decoding tests (18 tests)
- Configuration validation

Features:
- Exits on first failure with specific exit code (1-6)
- Clear color-coded progress output with section headers
- Shows elapsed time
- Prints full stdout/stderr on any failure
- Robust success detection using regex patterns

Usage: ./qa/bin/test_everything

Replaces need to remember and run 6 separate commands.
Updated protocols in previous commit to reference this tool.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Use regex to match "100.0% passed" on same line, preventing
false positives from separate "100%" and "passed" strings.

Previously could incorrectly pass with "100% failed 0% passed".
Now requires "100" followed by optional decimals, "%", and "passed"
in sequence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Documentation for CLI tab completion and interactive improvements:
- CLI_AUTO_EXPANSION_IMPLEMENTATION.md - Auto-expansion design
- CLI_ENHANCED_COMPLETIONS.md - Enhanced completion features
- CLI_INTERACTIVE_ENHANCEMENT_STATUS.md - Feature status
- CLI_TAB_COMPLETION_STATUS.md - Completion status
- CLI_TESTING_COMPLETE.md - Testing summary
- CLI_TESTING_GUIDE.md - Testing guide
- CLI_WORK_SUMMARY.md - Work summary
- MANUAL_TESTING_INSTRUCTIONS.md - Manual testing steps

Related to commits 5f337c5 and earlier CLI enhancement work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements two new reactor API commands for health and status monitoring:

- ping: Returns unique UUID for request tracking and acknowledgment
- status: Returns comprehensive session information (UUID, uptime, PID, neighbors)

Features:
- UUID generation and tracking for ping requests
- Detailed status reporting including session uptime and neighbor states
- Both commands integrated into interactive CLI with descriptions
- Full test coverage: unit tests + functional encoding test

Files added:
- src/exabgp/reactor/api/command/reactor.py: ping and status implementations
- tests/unit/test_reactor_health.py: comprehensive unit tests
- qa/encoding/api-health.*: functional encoding test
- etc/exabgp/api-health.conf: test configuration
- etc/exabgp/run/api-health.run: test script
- qa/bin/test_api_commands: manual API command testing utility

All 73 encoding tests pass, 1653 unit tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Adds BGP UPDATE announcements after each successful health check step
to verify correct execution order:

- After ping succeeds: announces 192.168.0.1/32 next-hop 10.0.0.1
- After status succeeds: announces 192.168.0.2/32 next-hop 10.0.0.2

Benefits:
- Verifies commands execute in correct sequence
- Test fails if any command fails or order changes
- Provides regression protection for health check execution flow

Test G (api-health) passes with new checkpoints.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Bind '?' key to trigger completion like TAB, displaying command descriptions.
Works on both macOS (libedit) and Linux (GNU readline).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Multiple improvements to neighbor extensive output and API logging:

1. **Python 3.12+ compatibility**: Fix timedelta formatting in neighbor output
   - Wrap timedelta objects with str() before f-string formatting
   - Python 3.12+ removed support for format specs on timedelta objects
   - Fixes: "unsupported format string passed to datetime.timedelta.__format__"

2. **Show disconnected neighbors**: Include configured neighbors in extensive output
   - Previously only showed connected peers (reactor.peers())
   - Now shows all configured neighbors with minimal info when disconnected
   - Helps troubleshoot neighbors that fail to connect

3. **API response debug logging**: Add debug logs for API command responses
   - Log all responses sent to API processes (both sync and async)
   - Helps debug API communication issues
   - Format: "API response to {process}: {string}"

4. **Fix column alignment**: Improved readability of extensive output
   - Changed indent from 3 to 4 spaces for consistent alignment
   - Right-aligned Add-Path column values to match other columns
   - All section headers and data rows now align properly

All tests pass (1654 unit tests + 73 functional encoding + 18 decoding).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
This commit addresses three bugs but work is incomplete:

1. Fix KeyError when using 'json' keyword with API commands
   - Added json_support=True to 20 commands (announce/withdraw/rib/teardown)
   - Commands were registered but not in callback['json'] dict
   - Added regression tests in test_command_registry.py

2. Fix autocomplete breaking after connection loss
   - Wrapped complete() in try/except to handle exceptions gracefully
   - Prevents readline from permanently breaking on errors
   - Added regression tests in test_completer.py

3. Fix terminal state corruption on connection loss
   - Replaced os._exit(1) with graceful shutdown (self.running = False)
   - Allows Python cleanup and readline state restoration
   - Ctrl+C cleanup now matches connection loss cleanup

Tests: All 1669 unit tests pass, 12 new regression tests added

TODO: Need to verify graceful shutdown works in practice - currently
requires user to press Enter after connection loss. May need better
solution for interrupting blocked input() call.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changes:
- Show "✓ Command sent" immediately after socket write succeeds
- Show "✓ Command accepted" when daemon confirms (empty response or "done")
- Show response data for commands that return information (show, etc.)
- Handle socket/timeout errors without showing "Command sent"

Previously only showed "✓ Command sent" after receiving response.
Now provides immediate feedback that command was sent, followed by
daemon's response (accepted, data, or error).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added Rule #7 to both protocols:
- ERROR_RECOVERY_PROTOCOL.md: Never run git add -A without asking
- GIT_VERIFICATION_PROTOCOL.md: Process for "commit" with pre-existing changes

Required actions when user says "commit" with pre-existing changes:
1. STOP - don't run git add immediately
2. Categorize: my changes vs pre-existing
3. ASK which files to include
4. WAIT for explicit answer
5. Stage only specified files

Prevents protocol violation: committing pre-existing changes without
explicit user permission.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added mandatory clarification protocol:
- MUST NOT guess when user input is ambiguous
- MUST ask for clarification with numbered options
- Use thinking mode to identify ambiguity before asking
- Examples: "commit" (which files?), "fix test" (which test?)

Rationale: Guessing wastes time through wrong assumptions and rework.
Asking clarifying questions gets it right first time.

Prevents mistakes from assumptions about user intent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Test G (api-health) was failing because the ping API command now
defaults to JSON output format instead of text format.

Changes:
- Update etc/exabgp/run/api-health.run to parse JSON response
- Falls back to text format for backward compatibility
- Fix linting issues (bare except, quote consistency)

The ping command now returns:
- JSON (default): {"pong": "<uuid>", "active": true}
- Text (with 'ping text'): pong <uuid> active=true

All tests pass (73/73 encoding, 1680 unit, 18 decoding).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Problem: When ExaBGP daemon stopped, CLI attempted reconnection but
would not exit - it remained at the prompt indefinitely, even after
displaying "Exiting CLI..." message. This left the terminal in a
corrupted state with non-functional autocomplete keys.

Root cause: Background threads called os._exit() which:
1. Blocked/hung when trying to clean up readline state
2. Never actually exited because cleanup functions deadlocked
3. Main thread remained blocked in input() waiting for user input

Solution: Signal-based graceful shutdown
1. Background threads call _signal_shutdown() instead of os._exit()
2. Sends SIGUSR1 to interrupt the blocking input() call
3. Signal handler raises KeyboardInterrupt to break out of input()
4. Main loop exits gracefully allowing Python cleanup to run
5. Terminal readline state properly restored via atexit handlers

Changes:
- Add signal handler setup in PersistentSocketConnection.__init__()
- Add _signal_shutdown() to send SIGUSR1 from background threads
- Replace all os._exit() calls with _signal_shutdown() + break
- Handle KeyboardInterrupt in main loop to distinguish signal vs Ctrl+C
- Update test_everything to explicitly test both src and tests with ruff

Result: CLI exits immediately when daemon stops, terminal autocomplete
keys (tab, ?, arrows) work properly on restart.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
**Breaking change:** Async mode is now the default reactor implementation.
Users who need the legacy generator-based reactor must explicitly opt-in.

**Changes:**
- New env var: `exabgp_reactor_legacy=true` to use legacy/sync mode
- Old env var: `exabgp_reactor_asyncio` removed (never released)
- Default behavior: Async mode (modern asyncio-based event loop)
- Legacy behavior: Generator-based event loop (opt-in)

**Files modified:**
- src/exabgp/environment/setup.py: Renamed 'asyncio' key to 'legacy'
- src/exabgp/reactor/loop.py: Inverted logic (async when not legacy)
- qa/bin/test_everything: Updated to test both modes (async + legacy)
- tests/shell/test-ctrl-c-async.sh: Removed env override (now default)

**Rationale:**
- Async mode has achieved 100% test parity (72/72 functional tests)
- Phase 2 production validation ready to begin
- Clean break acceptable (feature never released in stable version)

**Testing:**
- Async mode (default): 72/72 tests pass (with 1 intermittent: test D)
- Legacy mode (opt-in): 72/72 tests pass
- test_everything now validates both modes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Fixed typo in AFI.implemented_safi() for IPv4 where 'mcast-vpn' and
'mpls-vpn' were concatenated into 'mcast-vpnmpls-vpn'.

Changed:
- 'mcast-vpnmpls-vpn' → 'mcast-vpn', 'mpls-vpn'

This corrects the IPv4 SAFI list to return 8 values instead of 7.

Impact:
- CLI auto-complete unaffected (registry already had correct values)
- AFI.implemented_safi('ipv4') now returns proper SAFI list
- Any AFI/SAFI validation code now has correct values

Tests:
- All 1680 unit tests pass
- All 28 CLI completion tests pass
- Linting clean (ruff format + ruff check)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
⚠️ WARNING: This feature is newly implemented and not yet extensively tested.
Please report any issues or bugs you encounter.

Implements dynamic shell completion generation for the exabgp command:

Features:
- Completion generated on-demand via Python code (no static files)
- Complete all subcommands (version, cli, run, server, env, validate, decode, healthcheck, shell)
- Complete all options and flags for each subcommand
- Smart file completion for .conf files in standard locations
- AFI/SAFI family completion for decode command
- Backward compatibility mode (bare config file as first arg)

Commands:
- 'exabgp shell install [bash|zsh|fish]' - Generate and install completion
- 'exabgp shell uninstall [bash|zsh|fish]' - Remove installed completion
- 'exabgp shell completion <bash|zsh|fish>' - Output completion script to stdout

Installation:
- Auto-detects current shell if not specified
- Installs to user directories (~/.local/share/, ~/.zsh/, ~/.config/fish/)
- No filesystem files required - all generated dynamically

Implementation (shell.py):
- generate_bash_completion() - 251 lines of bash completion
- generate_zsh_completion() - 434 lines of zsh completion
- generate_fish_completion() - 571 lines of fish completion
- Total: 729 lines of Python generating 1256 lines of shell completion

Documentation:
- README.md updated with Shell Completion section
- Installation, uninstallation, and manual generation instructions

Tests:
- Verified bash completion generates and loads correctly
- Verified all shell subcommands work
- Linting passes (ruff format + ruff check)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Version Changes:
- pyproject.toml: 5.0.0 → 6.0.0
- src/exabgp/version.py: 5.0.0 → 6.0.0 (release, json, text)
- Dockerfile.remote: Updated exabgp_version to 6.0.0
- README.md: Updated all version examples to 6.0.0

Documentation Updates:
- doc/CHANGELOG.rst: Added comprehensive 6.0.0 changelog
  - BREAKING: Async reactor now default (exabgp_reactor_legacy=true for old)
  - Shell completion for bash/zsh/fish
  - Enhanced interactive CLI
  - Health monitoring API
  - Python 3.12+ support
  - Various bug fixes
- README.md: Added 6.0.0 improvements section
  - Clear warning about async reactor default
  - Feature highlights (shell completion, CLI, health monitoring)
  - Clarified version status: 5.0.0 stable, 6.0.0 upcoming release

Version Status:
- 5.0.0: Current stable release (recommended for production)
- 6.0.0: Upcoming release on main branch (testing recommended)
- Historical note: silence-ack introduced in 5.0.0 (kept accurate)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Replace autocomplete for 'show neighbor' and 'show adj-rib' commands with
more intuitive CLI-first syntax while maintaining full API compatibility.

CLI syntax changes:
- Old: show neighbor <ip> [options]
  New: neighbor <ip> show [options]
- Old: show adj-rib <in|out> [options]
  New: adj-rib <in|out> show [options]
- New: neighbor <ip> adj-rib <in|out> show [options]

Implementation:
- Removed 'show', 'neighbor', 'adj-rib' from 'show' subcommand completions
- Added 'neighbor' and 'adj-rib' to base commands for discoverability
- CLI transforms new syntax to API-compatible 'show ...' commands
- Full autocomplete chain for all patterns
- Old syntax still works when typed explicitly (backward compatible)
- All transformations happen client-side; API unchanged

Updated 18 tests across 5 files to reflect new completion behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…cascade

When user presses Ctrl+C during CLI input, the KeyboardInterrupt handler
calls _quit() which sends 'bye' command and waits up to 5 seconds for
response. If user presses Ctrl+C again during this wait, a second
KeyboardInterrupt was raised but not caught (except Exception doesn't
catch KeyboardInterrupt since it inherits from BaseException).

Changed exception handler in _quit() from 'except Exception' to
'except (Exception, KeyboardInterrupt)' to gracefully handle impatient
multiple Ctrl+C presses without showing confusing nested tracebacks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changes:
- Create qa/scripts/ directory for test scripts
- Move tests/shell/ → qa/scripts/ (test-ctrl-c-async.sh, test_async_cancellation.py)
- Modernize test_api_commands: bash → Python, socket → process API
- Add test_no_neighbor.py: validates API-only mode without BGP neighbors
- Add api-no-neighbor.conf + api-simple.conf test configurations
- Add api-no-neighbor.run + api-simple.run test scripts
- Integrate test_no_neighbor into qa/bin/test_everything (test 8/8)
- Extend qa/bin/functional nickname pool to accommodate 88 config files (87→111 chars)

Tests:
- version command works without neighbors
- ping command works without neighbors
- status command works without neighbors
- shutdown command cleanly stops ExaBGP
- All 8 test suites pass (ruff, unit, encoding×2, decoding, config, no-neighbor, api-commands)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Enable container builds for 5.0 stable branch alongside main:
- Add 5.0 to test workflow triggers (unit, functional, linting, type-checking)
- Add 5.0 to container.yml workflow_run branches
- Tag 5.0 branch builds as '5.0' and 'stable'
- Tag main branch builds as 'latest' (unchanged)

Result:
- main commits → ghcr.io/exa-networks/exabgp:latest
- 5.0 commits → ghcr.io/exa-networks/exabgp:5.0 + :stable

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Update linting.yml: ruff check . → ruff check src qa tests
- Update qa/bin/test_everything: ruff check src tests → ruff check src qa tests
- Fix ruff errors in qa/scripts:
  - test_api_commands.py: bare except → except Exception
  - test_no_neighbor.py: remove unused signal import, bare except → except Exception

Now CI and local tests explicitly check same folders (src, qa, tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
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.

2 participants