8.1 KiB
AGENTS.md
FastSync is a high-performance file synchronization system written in C11. It supports TCP and SSH transports, TLS encryption (OpenSSL), streaming zstd compression, multithreaded transfers, and incremental sync. The build uses CMake; CI runs on Gitea Actions (.gitea/workflows/ci.yaml).
Dependency installation
CI rule: never add apt-get install / pip install steps to CI workflows — use the custom Docker image instead. The image is built from the repo-root Dockerfile and is the same image CI uses: gitea.tap-tap.win/taptap/fastsync-ci:v7. It contains the full toolchain: gcc/g++, CMake, libzstd-dev, libssl-dev, make, git, cppcheck, clang-format, python3 + pytest, openssh-client, and Node.js.
Host rule: for local development, use nix-shell (see README.md) which provides zstd, OpenSSL, CMake, and gcc. The Docker image can also be used locally for CI parity.
# Use the prebuilt CI image directly (faster, guaranteed CI parity)
docker pull gitea.tap-tap.win/taptap/fastsync-ci:v7
docker tag gitea.tap-tap.win/taptap/fastsync-ci:v7 fastsync-ci:local
# Or build the image from the repo-root Dockerfile
# (Note: the prebuilt :v7 image reflects the previous Dockerfile state;
# rebuild from source to pick up any newly added packages like lcov/valgrind.)
docker build -t fastsync-ci:local .
# Build, run unit tests, and run integration tests inside the container
docker run --rm -v "$PWD:/workspace" -w /workspace fastsync-ci:local \
sh -c 'cmake -B build -S . && cmake --build build -j$(nproc) && ./build/tests && python3 -m pytest tests/'
# Avoid root-owned build/ artifacts by matching your host UID/GID
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/workspace" \
-w /workspace fastsync-ci:local \
sh -c 'cmake -B build -S . && cmake --build build -j$(nproc) && ./build/tests && python3 -m pytest tests/'
Note: The first
cmake configure(cmake -B build -S .) fetches xxHash from GitHub viaFetchContent— network access is required. Subsequent reconfigures reuse the cached source.
If a dependency is missing from the CI image, add it to the Dockerfile (and rebuild) rather than adding an install step to the CI workflow.
CI Conventions
When configuring for CI parity, use:
cmake -B build -S . -DSTRICT_WARNINGS=ON # -Wextra -Wpedantic -Werror
cmake -B build -S . -DSANITIZER=address # AddressSanitizer (ASan)
cmake -B build -S . -DSANITIZER=thread # ThreadSanitizer (TSan)
The CI workflow (.gitea/workflows/ci.yaml) runs lint (clang-format, cppcheck), build + test (unit + integration), and sanitizer (currently only address) jobs sequentially.
Build
cmake -B build -S . && cmake --build build -j$(nproc)
Test
./build/tests # unit tests
python3 -m pytest tests/ # integration tests
CI Workflow — Waiting for Results
When running the CI workflow via tea (the task execution agent), always set a sufficient timeout (e.g., 600000ms) to allow CI to finish. After CI completes, check the results yourself — do not assume success. Use gh run watch or similar to monitor CI status, then inspect logs on failure.
CI Troubleshooting
If lint (clang-format) fails
Run clang-format in the CI Docker image to match the exact CI version:
docker run --rm -v "$PWD:/workspace" -w /workspace gitea.tap-tap.win/taptap/fastsync-ci:v9 \
sh -c 'find src/ tests/ -name "*.c" -o -name "*.h" | xargs clang-format -i'
If cppcheck fails
Fix reported issues locally, then verify with:
docker run --rm -v "$PWD:/workspace" -w /workspace gitea.tap-tap.win/taptap/fastsync-ci:v9 \
sh -c 'cppcheck --enable=warning,style,performance,portability --suppress=missingIncludeSystem --error-exitcode=1 --inline-suppr src/ tests/'
If integration tests fail
Run locally before pushing:
python3 -m pytest tests/ -v --tb=short
Branch Strategy
Two main branches: dev (integration) and main (stable releases).
Rules
- All PRs target
dev— never targetmaindirectly devis the default branch in Gitea repo settingsmainis protected — only merged fromdevvia PR with 2 approvals + full CI pass- Feature/bug branches branch from
dev, PR back todev dev→mainmerges happen on-demand or weekly, requiring full CI + review
# Start a new feature
git checkout dev && git pull
git checkout -b feat/my-feature
# ... work, commit, push
git push -u origin feat/my-feature
# Create PR targeting dev
Creating the dev branch (one-time setup)
git checkout main && git pull
git checkout -b dev
git push origin dev
# Then in Gitea: Settings → Repository → Default Branch → dev
Branch protection (Gitea repo settings)
For dev:
- ✅ Require PR for merging
- ✅ Require 1 approval
- ✅ Require status checks (all CI jobs must pass)
- ✅ Delete branch after merge
For main:
- ✅ Require PR from
devonly - ✅ Require CI
- ✅ Require 2 approvals
- ✅ No direct pushes
Automated Agent Workflows
When a PR targeting dev is opened or synchronized, Gitea Actions workflows automatically run agents to review the code and post results as PR comments. This replaces the manual "invoke 3 reviewers" pattern.
Automated PR Review
Triggered on pull_request: [opened, synchronize, ready_for_review]. Runs security-auditor and code-quality-guardian, posts combined review to PR.
Automated Issue Fix
Comment /opencode fix on any issue — agents will create a fix branch, implement the fix, and open a PR targeting dev.
Scheduled Maintenance
Runs weekly (Monday 06:00 UTC) — security audit of the full codebase, creates issues for findings.
Batch Merge Orchestration
For grouping multiple fixes into one integration PR (like the integration/all-fixes pattern):
- Push each fix branch independently (targetting
dev) - Use
workflow_dispatchonagent-batch-merge.ymlwith comma-separated branch names - The workflow merges all branches into
dev, resolves conflicts, runs build + tests, and pushes
Manual PR Invocation
When automated agents are unavailable or you need a targeted review, invoke agents directly:
# Run all 3 reviewers on a PR diff
opencode run --agent reviewer "Review this PR"
opencode run --agent security-auditor "Audit this PR for vulnerabilities"
opencode run --agent code-quality-guardian "Check this PR for code quality"
# Post results to Gitea
curl -s -X POST -H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"body":"MARKDOWN_REVIEW_BODY"}' \
"https://gitea.tap-tap.win/api/v1/repos/TapTap/FastSync/issues/<PR_NUMBER>/comments"
Gitea API & tea CLI
Check CI status via API
TOKEN="<token>"
curl -s -H "Authorization: token $TOKEN" \
"https://gitea.tap-tap.win/api/v1/repos/TapTap/FastSync/actions/runs?limit=5" \
| python3 -c "
import json,sys; d=json.load(sys.stdin)
for r in d.get('workflow_runs',[]):
path = r.get('path','')
prn = path.split('@')[1].replace('refs/pull/','').replace('/head','') if '@' in path else ''
print(f'PR #{prn}: sha={r[\"head_sha\"][:8]} {r[\"status\"]} {r.get(\"conclusion\",\"\")}')
"
Post review comments
curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
-d '{"body":"MARKDOWN_REVIEW_BODY"}' \
"https://gitea.tap-tap.win/api/v1/repos/TapTap/FastSync/issues/<PR_NUMBER>/comments"
Use tea for PR operations
tea pr list --repo TapTap/FastSync
tea pr close <number> --repo TapTap/FastSync
Common pitfalls
__threadon shared SSL context: io_ssl must NOT be thread-local — worker threads inherit the SSL context from the main thread. Use regularstatic SSL* io_ssl.- SSL WANT_READ/WANT_WRITE retry: Always retry on
SSL_ERROR_WANT_READandSSL_ERROR_WANT_WRITEinsend_n_data/receive_n_data. Removing these breaks TLS multithreaded transfers. - clang-format version: The CI image uses clang-format 18. Always format inside the CI Docker container for exact match.
- Merge order matters: Merge the most comprehensive branch first, then smaller ones, to minimize conflicts when creating a combined branch.