Performance Tip: Use
Ctrl+Fto jump to sections using anchor links (e.g.,#building-and-running)
make unit-tests- Run unit testsmake unit-tests-race- Unit tests with race detectormake integration-tests-fabtoken-fabric-t1- Fabtoken integration testsmake integration-tests-dlog-fabric-t1 TEST_FILTER="T1"- ZK integration tests with T1 filter
make fmt- Format code using gofmtmake lint- Check code stylemake lint-auto-fix- Auto-fix linting issues (recommended pre-commit)make install-tools- Install development dependenciesmake checks- Run all pre-CI checks (license, fmt, vet, etc.)make download-fabric- Download Fabric binariesmake docker-images- Prepare Docker imagesmake testing-docker-images- Prepare test Docker images
make clean- Remove build artifactsmake clean-all-containers- Remove Docker containersmake tidy- Synchronize Go dependenciesgo generate ./...- Generate mocks
make install-tools
make download-fabric
export FAB_BINS=$PWD/../fabric/bin
make docker-images
make testing-docker-images# Code quality
make lint-auto-fix
make checks
# Testing
make unit-tests # Standard
make unit-tests-race # With race detection
make integration-tests-fabtoken-fabric-t1 # Integration tests# Performance profiling
go test -cpuprofile=cpu.out ./...
go test -memprofile=mem.out ./...
# Focused testing
make integration-tests-dlog-fabric TEST_FILTER="T1"- Chaincode packaging failed: Verify
FAB_BINSis set correctly and points to valid Fabric binaries - Docker errors: Run
make testing-docker-images - Linting errors on commit: Run
make lint-auto-fix - Test timeouts: Increase Docker resource allocation
- Permission denied:
chmod +xon Fabric binaries in$FAB_BINS - Container conflicts:
make clean-all-containers - Go module issues:
make tidy - Mock generation failures:
make install-tools(ensures counterfeiter is installed)
- Driver Pattern: Swappable token technologies via interfaces in
token/driver - Service Pattern: Encapsulated high-level logic in
token/services - TTX Service: Orchestrates token transaction lifecycle (Request → Assemble → Sign → Commit)
- Located alongside implementation code (
*_test.go) - Use testify for assertions (
assertfor values,requirefor error handling) - Prefer table-driven tests for service logic
- Use context struct pattern to minimize mock boilerplate
- Located in
integration/directory - Utilize Network Orchestrator (NWO) for ephemeral Fabric networks
- Use
TEST_FILTERenvironment variable with Ginkgo labels for focused testing - Example:
TEST_FILTER="T1"runs only tests with T1 label
- Add a
FuzzXxxtest (Go native fuzzing) wherever meaningful — any exported function that parses untrusted/attacker-controlled bytes (deserializers, wire-format decoders, signature/identity/token parsers) should get one, proactively when the entry point is added or touched, not only after a bug is found there. - Seed the corpus with
f.Add(...): valid input, empty input, truncated/ malformed input, and any known historical edge cases (e.g. a payload that previously triggered a panic). - Verify locally before committing:
go test <pkg> -run='^$' -fuzz='^FuzzXxx$' -fuzztime=20swith no panics, plus a plaingo test <pkg>run to confirm the seed corpus passes as ordinary test cases. - Wire every new
FuzzXxxtarget into.github/workflows/nightly-fuzz.yml: add a{name, pkg, func}entry to thefuzzjob'sstrategy.matrix.includelist. A fuzz test that isn't in that matrix never actually runs under extended-fuzztimein CI — it only gets exercised by its seed corpus in the regular unit-test run.
- Generate mocks with
counterfeiter(go generate ./...) - Use
disabled.Providerfor metrics to avoid nil panics - Use
noop.NewTracerProvider()for tracing - Employ Context Struct + Setup Helper pattern (see
token/services/ttxfor example)
- Error Handling: Handle errors explicitly; avoid blank identifier for errors
- Error Construction: Never use
fmt.Errorf(orfmtat all) to build or wrap errors. Always usegithub.com/hyperledger-labs/fabric-smart-client/pkg/utils/errorsinstead (errors.New,errors.Errorf,errors.Wrap,errors.Wrapf,errors.WithMessage,errors.WithMessagef,errors.Join, etc.). This applies to source code, tests, and code samples indocs/. - Interfaces: Define small, focused interfaces on consumer side; favor composition
- Concurrency: Use goroutines and channels; avoid shared state; validate with race detector
- Globals: Avoid global variables for testability
- Documentation: All exported functions MUST have Godoc comments
- DCO Sign-off: All commits MUST be signed off (
git commit -s) - Linear History: Use rebase workflow; avoid merge commits
- License: Apache License, Version 2.0
Full guidance: docs/development/general.md. Summary:
- Open an issue before non-trivial work, unless one already exists. Describe the problem/impact only — do not reference a fix that already exists or is in progress.
- Every issue and PR must be assigned: Assignee, Labels (
gh label listfor the current set), Milestone (gh api repos/LFDT-Panurus/panurus/milestones --jq '.[].title'), and Project (always"Panurus"). Onegh pr create/gh issue createcall can set all of these via--assignee,--label,--milestone,--project. - Issues only also get an Issue Type (Bug/Task/Feature) — this field does not exist
on PRs.
ghhas no CLI flag for it; set it viagh api graphqlwithupdateIssueIssueType(node IDs fromgh api orgs/LFDT-Panurus/issue-types). - Link the PR to its issue with
Fixes #N/Closes #Nin the PR body — not just a mention — so GitHub connects them and the project board updates automatically. - Never push directly or open a PR without the user's explicit go-ahead; confirm before
git pushand beforegh pr create.
Before implementing any task:
- Create
plan.mdin project root with:- Clear goal description
- Numbered implementation steps
- "Implementation Progress" section with
[ ] Pendingcheckboxes
- Update immediately when completing steps:
[x] Done+ brief change notes - Log blockers/decisions under
## Notes & Decisions - Mark plan as
✅ COMPLETEwhen finished
Before marking a task complete, update or create the relevant documentation under docs/:
- If the task changes a public API, protocol, or user-facing behaviour, update the corresponding
docs/page (or create one if it does not exist). - Keep docs consistent with code: function names, message fields, flow diagrams, and examples must match the implementation.
- New
docs/pages must follow the existing style (Markdown, same heading hierarchy as neighbouring files). - If no existing doc page covers the changed area, create
docs/<subsystem>/<topic>.mdand add a link from the nearest index or README.
Reusable, agent-agnostic step-by-step procedures live under docs/development/ and are
readable by any agent that reads this file — not just Claude Code. When Claude Code also
needs a /slash-command trigger for one, add a symlink at
.claude/skills/<name>/SKILL.md pointing back at the doc, so there is one source of truth.
- Update
fabric-smart-clientto latestmain: docs/development/update-fsc.md (Claude Code:/update-fsc). Bumps the FSC dependency across every Go module, resolves API/lint breakage untilmake checksandmake lint-auto-fixare clean, then stops and waits for the user's go-ahead before pushing a branch or opening the PR — the "never push or open a PR without explicit go-ahead" rule above still applies to this runbook. - Debugging Integration Tests: docs/development/debug-integration-tests.md
(Claude Code:
/debug-integration-tests). Log locations, Docker/network inspection, and Ginkgo focus/skip techniques for diagnosing failing integration tests.
See Debugging Integration Tests (Claude Code: /debug-integration-tests).