Imported from xiaohei16h/interview-limi (
AGENTS.md). Install upstream withnpx skills add xiaohei16h/interview-limi. Copyright stays with the author.
AGENTS — interview-remote
Single source of truth for AI agents working on this repo.
What this repo is
Minimal Spring Boot logistics service. Four RPC endpoints
(allocateTrackNumber / allocateLabel / calcShipFee / checkAuth),
H2 file DB, one Maven module. See README.md for runtime details.
The only extension point
Adding a logistics carrier? Read this and stop:
→ src/main/java/com/example/logistics/provider/AGENTS.md
Fastest path (~10s, zero manual edits) — Pattern B / Pattern C carriers:
scripts/new-provider.sh --name <slug> --pattern <B|C> --verify \
[--code <CODE>] [--country US,GB,AE] [--tracking-prefix XY] \
[--api-key-header X-<Brand>-Api-Key]
--code is optional — it defaults to upper-cased --name (yto → YTO,
china_post → CHINA_POST). Pass it explicitly only if the providerCode
must differ from the package slug. --verify chains scripts/verify.sh after
scaffolding so you get "scaffolded + tests green" in one command.
--api-key-header (Pattern C only) defaults to X-<Camel>-Api-Key (matches
Aramex/JT style). Pass it to use brand-faithful caps like X-FedEx-Api-Key or
DHL-API-Key. A Pattern C scaffold now lands at 0 TODOs — --ready --json
returns ready:true immediately, no de-template editing needed.
The script copies the right skeleton, renames every token, registers the channel id in
LogisticsChannelIds.java + the provider/AGENTS.md registry table, and (Pattern C only) wires
logistics.<slug> blocks into both application.yml files plus auto-substitutes the
API_KEY_HEADER literal and https://api.<slug>.example.local host. After it runs, only
carrier-specific business logic (rate ladders, real HTTP endpoints, auth shape) is left —
none of which is required for --ready to turn green.
All three patterns now scaffold via the same command. Pattern A (pre-seeded pool) lands at 0
TODOs from _template_pool/ — @EventListener seed, appKey+appSecret auth, weight-banded fee
rules, idempotent on re-boot via findByTrackNumber. ChinaPostProvider remains the legacy
reference but is no longer the only path; new pool carriers go through --pattern A.
Discovery / safety flags (use these first as an AI agent):
scripts/new-provider.sh --list # registered carriers + prefix + #ctry survey table
scripts/new-provider.sh --list --json # same, JSON. Each carrier carries trackingPrefix +
# countryCount so an agent surveys 19+ carriers in
# one call without looping --info per carrier.
scripts/new-provider.sh --info <slug> # dump one carrier's channelId/pattern/countries/auth/yml
scripts/new-provider.sh --info <slug> --json # JSON form of --info
scripts/new-provider.sh --todos <slug> # list remaining TODO markers in a scaffolded carrier
scripts/new-provider.sh --todos <slug> --json # JSON form of --todos (exit code = TODO count)
scripts/new-provider.sh --quick-test <slug> # fast single-carrier mvn test (~5s, no full reset)
scripts/new-provider.sh --quick-test <slug> --json # JSON {testClass, exit, passed}
scripts/new-provider.sh --ready <slug> # composite gate: todos==0 + validate + carrier test
scripts/new-provider.sh --ready <slug> --json # JSON {todosCount, validateOk, testOk, ready}
scripts/new-provider.sh --ready --all # repo-wide: every carrier's todos + validate + full mvn test
scripts/new-provider.sh --ready --all --json # JSON {validateOk, testAllOk, carriers[...], allReady}
scripts/new-provider.sh --validate # check LogisticsChannelIds.java ↔ AGENTS.md table
# AND emit ⚠ stderr warnings (advisory, not blocking)
# for tracking-prefix collisions across carriers.
# Exit code stays governed by registry drift only.
scripts/new-provider.sh --name … --dry-run # preview a plan without writing files
scripts/new-provider.sh --name … --dry-run --json # JSON-shaped plan (action=plan, applied=false)
scripts/new-provider.sh --name … --verify --json # JSON success (incl. verifyExit) on stdout, mvn on stderr
scripts/new-provider.sh --remove <slug> # inverse of scaffold (dir + test + const + AGENTS.md row + yml)
scripts/new-provider.sh --remove <slug> --dry-run # preview removal
--info and --todos accept any of: the SCREAMING constant (FEDEX), the underscored slug
(china_post), or the collapsed package name (chinapost). With --json they emit a single
document an agent can pipe into jq without parsing columns.
All slug-taking modes accept SCREAMING constant, underscored slug, or collapsed package
name. Unknown slugs trigger a did you mean: suggestion drawn from registered constants
(substring or subsequence match — handles fdx → FEDEX).
Canonical agent loop:
--list --json # discover registry + next free channelId
--name X --pattern B|C --verify --json # scaffold + mvn test in one shot
# Pattern C: --ready --json already true. Pattern B: 3 TODOs remain (rate logic etc.).
--todos X --json # queue of remaining edits (file:line:text) — empty for Pattern C
# ...edit files for any remaining TODOs (Pattern B mostly; Pattern C only if business logic differs)...
--quick-test X --json # ~5s carrier-only test for tight iteration
--ready X --json # exit 0 + ready:true = production-ready
Failure-state JSON shape (--quick-test / --ready / --ready --all).
Every test-running command emits the same diagnostic envelope so an agent
never has to fall back out of --json mode to learn what broke:
{
"passed": false, // or "testOk" / "allReady" for --ready / --ready --all
"failureReason": "compile", // "compile" | "test" | null
"failedTests": [], // method names (test failure) — empty on compile failure
"summary": "[ERROR] .../HttpClient.java:[37,50] cannot find symbol",
"logPath": "/tmp/new-provider-quick-test-<pkg>.log" // full mvn output
}
failureReason: "compile"⇒summaryalready names the broken file + line+col.failureReason: "test"⇒failedTestslists the failing method(s).failureReason: null⇒passed:true/testOk:true/allReady:true.
For --ready --all the same three fields apply globally; failedTests
uses ClassName.method form so an agent can grep straight to the file.
mvn compile/mvn testalready runs--validateduring the validate phase (via exec-maven-plugin) — drift between code and AGENTS.md fails the build, not justverify.sh. You don't need to remember to run it manually.
That file is authoritative. It covers:
- The four-primitive contract (
providerCode+allocate*+calcShipFee+checkAuth+defaultChannels) - Three reference templates — pool / registry / HTTP-backed
- A copy-paste skeleton in
src/main/java/com/example/logistics/provider/_template/ - The
channelIdregistry (claim the next free id) - Fail-fast guard rails (duplicate
providerCodeorchannelId→ startup error) - An anti-pattern list (what NOT to do)
Files you do not edit when adding a carrier
src/main/java/com/example/logistics/service/DefaultLogisticsService.java— router auto-discovers@Componentproviderssrc/main/resources/schema.sql— DDL only; seed your data in your provider classsrc/main/java/com/example/logistics/controller/LogisticsController.java- Any
dto/*.java src/main/java/com/example/logistics/provider/LogisticsProvider.java— the contract itselfsrc/main/java/com/example/logistics/provider/{AuthJsons,ChannelSeed,LogisticsResultCode,LogisticsChannelInitializer}.java— shared helpers
Verify before claiming done
scripts/verify.sh
Runs in order: --validate registry consistency → rm -rf ./data && mvn clean test → second
mvn test (idempotency: every seed must be a no-op on second boot). Exits non-zero on the first
failure. This is the canonical "is the branch ready to merge?" command.
GitHub Actions (.github/workflows/ci.yml) runs verify.sh and test-scaffold.sh on every
push/PR to main, so a forgotten local run still surfaces as a red check before merge.
Self-test the scaffold tool (only if you edit scripts/new-provider.sh)
scripts/test-scaffold.sh
Exercises both Pattern B and Pattern C end-to-end (scaffold → verify → rollback),
plus --list / --validate / --pattern A scaffold / --dry-run idempotency / auto-derived
--code. ~30s. Rolls back its throwaway scaffold even on failure. Run this if you change
scripts/new-provider.sh or anything in provider/_template*/.