Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
12e8207
feat(3f): multi-adapter bridge facilitator with signed-payload auth
oxsteins Jun 26, 2026
6d774f2
feat(solver): log a solver's fatal error so a stopped solver is visible
oxsteins Jun 26, 2026
e065bda
docs(3f): multi-adapter design, plan, and README
oxsteins Jun 26, 2026
cc45657
Merge branch 'stage' into feat/3f-multi-adapter
oxsteins Jun 26, 2026
804f762
refactor(3f): select offers in one ranked pass over adapter capacity
oxsteins Jun 26, 2026
031da90
fix(3f): authenticate offer listing with the operating-chain grunt-ap…
oxsteins Jun 26, 2026
5011113
chore: vendor RedStone OEV generated surfaces
alrxy Jun 26, 2026
c615d0c
refactor: add shared parsing and Morpho helpers
alrxy Jun 26, 2026
e09c4a0
feat: add RedStone OEV solver
alrxy Jun 26, 2026
7489d44
test: add RedStone OEV coverage and Sepolia tooling
alrxy Jun 26, 2026
a86399e
docs: document RedStone OEV solver
alrxy Jun 26, 2026
948beed
fix(3f): ignore non-live offers when rebuilding the offer cache
oxsteins Jun 26, 2026
3918cce
fix: finalize RedStone OEV settlement guards
alrxy Jun 26, 2026
9781d81
Merge branch 'stage' into feat/3f-multi-adapter
oxsteins Jun 28, 2026
83dc1f4
merge stage into oev plan
alrxy Jun 29, 2026
3d4ca1b
Merge pull request #31 from symbioticfi/feat/3f-multi-adapter
oxsteins Jun 29, 2026
0941ea1
Merge remote-tracking branch 'origin/stage' into feat/oev-plan
alrxy Jun 29, 2026
6de55a4
track callback auction keys
alrxy Jun 29, 2026
8908f16
fix oev bundle gas depth boundary
alrxy Jun 29, 2026
6387528
feat(3f): bind core-mirror ThreeFAdapter; drop sleeve cap, ppm yield …
oxsteins Jun 29, 2026
8e1a066
feat(3f): re-target solver at refactored ThreeFAdapter (getMaxAssets,…
oxsteins Jun 30, 2026
8126652
chore(deps): bump actions/setup-go from 6.4.0 to 6.5.0
dependabot[bot] Jun 30, 2026
50c3005
chore(deps): bump golangci/golangci-lint-action from 9.2.1 to 9.3.0
dependabot[bot] Jun 30, 2026
ac2f4ab
feat(3f): use requestsLength() for the active-request count
oxsteins Jul 1, 2026
c912511
Merge pull request #44 from symbioticfi/feat/3f-threefadapter-migration
oxsteins Jul 1, 2026
bca7eb3
Merge branch 'stage' into feat/oev-plan (post-#44)
oxsteins Jul 1, 2026
7159610
fix(3f): treat maxAssetsPerRequest as an always-active cap (0 = rejec…
oxsteins Jul 1, 2026
82bf825
docs(3f): tighten the maxAssets cap comments
oxsteins Jul 1, 2026
1229da5
Merge pull request #49 from symbioticfi/fix/3f-maxassets-cap
oxsteins Jul 1, 2026
4deed6c
feat(oev): stale-state gate on background caches + non-zero address c…
oxsteins Jul 2, 2026
7a9f65f
Merge branch 'stage' into feat/oev-plan
oxsteins Jul 2, 2026
7600636
fix(oev): drop auctions without ids
alrxy Jul 2, 2026
30dae51
fix(oev): reject zero-seize legs at encode; sync plan to PR #10 callb…
oxsteins Jul 2, 2026
77b0636
fix(oev): require strictly positive per-leg minProfit at encode
oxsteins Jul 2, 2026
f201709
Merge pull request #25 from symbioticfi/feat/oev-plan
oxsteins Jul 2, 2026
53d3d5d
drop oev leg maxassets
alrxy Jul 2, 2026
12669ea
drop oev leg max assets
alrxy Jul 3, 2026
daa308e
Merge pull request #51 from symbioticfi/feat/oev-drop-leg-maxassets
alrxy Jul 3, 2026
266eea2
feat(strategy): add shared webhook and liquidlane math helpers
alrxy Jul 6, 2026
983c62b
feat(rfq): add strategy contract and webhook adapter
alrxy Jul 6, 2026
f406d88
feat(rfq): add default quote strategy
alrxy Jul 6, 2026
5c82f4e
refactor(rfq): route quotes through pluggable strategies
alrxy Jul 6, 2026
82236e9
docs(rfq): document strategy configuration
alrxy Jul 6, 2026
32ee1c6
fix(deps): update golang.org/x/crypto
alrxy Jul 6, 2026
430f04e
refactor(rfq): delegate fill planning to strategies
alrxy Jul 6, 2026
385da16
refactor(rfq): clarify fill plan naming
alrxy Jul 6, 2026
7b8a112
feat(3f): add offer strategy contract
alrxy Jul 6, 2026
1705ecf
feat(3f): delegate offers to strategies
alrxy Jul 6, 2026
cb79f38
docs(3f): document strategy configuration
alrxy Jul 6, 2026
5f8264f
Merge pull request #45 from symbioticfi/feat/rfq-strategy
oxsteins Jul 6, 2026
e08b5f5
chore(deps): bump the go group across 1 directory with 5 updates
dependabot[bot] Jul 6, 2026
84718a4
Merge branch 'stage' into 3f-strategy
alrxy Jul 6, 2026
ec8e2d6
feat: add uniswapx plan
oxsteins Jul 6, 2026
457e658
Merge pull request #55 from symbioticfi/feat/uniswapx-plan
oxsteins Jul 6, 2026
7740e6e
Merge branch 'stage' into 3f-strategy
alrxy Jul 6, 2026
7af075b
refactor(rfq): updated strategy files in rfq
oxsteins Jul 6, 2026
6f74f82
Merge pull request #56 from symbioticfi/refactor/strategy-types
oxsteins Jul 6, 2026
1656530
Merge remote-tracking branch 'origin/stage' into 3f-strategy
alrxy Jul 6, 2026
2105acb
refactor(3f): align strategy package layout
alrxy Jul 6, 2026
3c9fdb7
NO-TASK Add the mainnet RFQ filler config profile
1kresh Jul 6, 2026
c982b28
NO-TASK Add a write-only RPC for transaction broadcasts
1kresh Jul 6, 2026
fb86402
Merge pull request #57 from symbioticfi/NO-TASK-mainnet-enable-liquid…
1kresh Jul 6, 2026
4c81399
Merge pull request #54 from symbioticfi/dependabot/go_modules/stage/g…
1kresh Jul 6, 2026
5214d6b
Merge pull request #47 from symbioticfi/dependabot/github_actions/sta…
1kresh Jul 6, 2026
c2a3134
Merge pull request #46 from symbioticfi/dependabot/github_actions/sta…
1kresh Jul 6, 2026
041ec51
Merge branch 'stage' into 3f-strategy
oxsteins Jul 6, 2026
70fecee
Merge pull request #52 from symbioticfi/3f-strategy
oxsteins Jul 6, 2026
84ced25
feat: refactor 3f solver strategy responsibilities
oxsteins Jul 6, 2026
bb4556e
feat: refactor rfq webhook strategy caching handling
oxsteins Jul 6, 2026
e91c52c
chore: update docs
oxsteins Jul 6, 2026
f27af02
NO-TASK Un-gate mainnet deploys (gprptest + symbiotic.fi)
1kresh Jul 6, 2026
782ef2c
Merge pull request #58 from symbioticfi/NO-TASK-ungate-mainnet-deploys
1kresh Jul 6, 2026
415f51a
chore: refactor
oxsteins Jul 7, 2026
65b8ee7
NO-TASK Fix vault-solver mainnet deploy: populate empty mainnet.yaml
1kresh Jul 7, 2026
5f4b53d
Merge branch 'stage' into feat/refactors
oxsteins Jul 7, 2026
827d1a3
Merge pull request #59 from symbioticfi/feat/refactors
oxsteins Jul 7, 2026
aa1afdd
Merge pull request #60 from symbioticfi/NO-TASK-vault-solver-mainnet-…
1kresh Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/chart/hoodi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ configMap:
solvers:
- name: rfq-filler
config:
strategy:
name: default
config: {}
backendUrl: http://rfq-backend:42072
backendSharedSecretEnv: BACKEND_SHARED_SECRET # env var NAME (secret never in config)
listenAddr: ":42073" # quote HTTP server (poll-only; no /notify)
Expand Down
55 changes: 55 additions & 0 deletions .github/chart/mainnet.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
configMap:
data:
config.yaml: |
# vault-solver — RFQ filler, Ethereum mainnet (chainId 1) profile.
#
# Addresses are the mainnet deployment of the RFQ contracts. Provide secrets via env:
# SOLVER_PRIVATE_KEY — the caller EOA (must hold CALLER_ROLE on the Executor)
# BACKEND_SHARED_SECRET — shared secret authenticating the backend peer on /quote
# ${VAR} fields are expanded from the environment at load time.

chain:
rpcUrl: ${ETH_RPC_URL} # primary READ RPC; expanded from env; do not commit a real URL
# writeRpcUrl carries ONLY transaction broadcasts (eth_sendRawTransaction); nonce, gas,
# receipts and every other read stay on rpcUrl. Point it at a private/MEV-protected relay
# so fills submit privately. Optional — omit to broadcast through rpcUrl.
writeRpcUrl: ${WRITE_RPC_URL} # e.g. https://rpc.mevblocker.io/fullprivacy
chainId: 1

signer:
keyEnv: SOLVER_PRIVATE_KEY # the CALLER_ROLE EOA that submits Executor.fill (P2)

txManager:
confirmations: 2

observability:
addr: ":9090" # /metrics, /healthz, /readyz (separate from the quote server below)
# Optional: set env SENTRY_DSN (and SENTRY_ENVIRONMENT) to tee Error+ logs to Sentry.

solvers:
- name: rfq-filler
config:
strategy:
name: default
config: {}
backendUrl: http://rfq-backend:42072
backendSharedSecretEnv: BACKEND_SHARED_SECRET # env var NAME (secret never in config)
listenAddr: ":42073" # quote HTTP server (poll-only; no /notify)
# Mainnet RFQ deployment:
executor: ${EXECUTOR} # per-instance: deploy.yml sets EXECUTOR (non-secret env, not vault)
reactor: "0x5eB54c47837cC84249F697e3CD8C5D88bCc35dac" # used at execution time
pollIntervalMs: 166 # backend order poll cadence
orderLimit: 20 # max open orders fetched per poll
# internal — public discounts + every advertised adapter; `adapters` optional. (external
# would REQUIRE a concrete adapter and error on empty; we don't have the mainnet instance
# yet, so stay internal to quote permissionlessly via the discounts API.)
solverMode: internal
# Token routing: quote scope ("all"|"permissioned"|"permissionless") is set per-instance
# via env (deploy.yml). mGLOBAL is not yet launched on mainnet (tabled), so there is no
# local permissioned set to evaluate against.
tokensToQuote: ${TOKENS_TO_QUOTE}
permissionedTokens: []
# LiquidLane adapters (optional in internal mode = extra recovery inventory). Left empty
# until the concrete mainnet mF-ONE LiquidLane adapter INSTANCE is known — a deployed
# instance of factory 0x3b5Bb07d7af98EBdD5A715fa09C5c10aF1749460, NOT the factory itself.
adapters: []
3 changes: 3 additions & 0 deletions .github/chart/sepolia.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ configMap:
solvers:
- name: rfq-filler
config:
strategy:
name: default
config: {}
backendUrl: http://rfq-backend:42072
backendSharedSecretEnv: BACKEND_SHARED_SECRET # env var NAME (secret never in config)
listenAddr: ":42073" # quote HTTP server (poll-only; no /notify)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0

- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # pin@v6.4.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@v6.5.0
with:
go-version-file: go.mod
cache: true
Expand All @@ -36,12 +36,12 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # pin@v7.0.0

- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # pin@v6.4.0
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # pin@v6.5.0
with:
go-version-file: go.mod
cache: true

- name: golangci-lint
uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee # pin@v9.2.1
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # pin@v9.3.0
with:
version: v2.11.4
6 changes: 5 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ jobs:
-f .github/chart/${{ inputs.network }}.yaml
-f .github/chart/vault-solver/${{ inputs.namespace }}.yaml
--set deployment.containers.vault-solver.image.tag=${{ github.sha }}@${{ inputs.digest }}
--set deployment.containers.vault-solver.env.EXECUTOR.value=${{ inputs.network == 'hoodi' && '0x5Ef14C81eBf7941523f8195F2E55b2A22700C181' || '0xc12714738A4772Bf966b2e999037D39315589263' }}
--set deployment.containers.vault-solver.env.EXECUTOR.value=${{ inputs.network == 'hoodi' && '0x5Ef14C81eBf7941523f8195F2E55b2A22700C181' || inputs.network == 'mainnet' && '0xe60E84218BB81539cc599A1E213d6F67058C69Cf' || '0xc12714738A4772Bf966b2e999037D39315589263' }}
--set deployment.containers.vault-solver.env.TOKENS_TO_QUOTE.value=permissionless
--set deployment.containers.vault-solver.env.RFQ_ADAPTER.value='${{ inputs.network == 'hoodi' && '"0x788Ab0D58bC7E5537109064F95875013c886ACC8"\, "0x9292Ad3e9C3747cFA31885657B5A458002205281"' || '"0x8F38656B85fb440018c109A5118aFfDfD923721c"\, "0xe4eb1E756F2d78F77B9ebE304515fA61B2451A33"' }}'
--set "podAnnotations.vault\.security\.banzaicloud\.io/vault-role"="${{ inputs.namespace }}-vault-solver"
--set "podAnnotations.vault\.security\.banzaicloud\.io/vault-env-from-path"="secrets/data/application/${{ inputs.namespace }}/vault-solver\,secrets/data/application/${{ inputs.namespace }}/vault-solver-keys"

vault-solver:
name: Deploy ${{ matrix.name }}
# The first/second/third solvers are the dedicated mGLOBAL fillers. mGLOBAL is
# not launched on mainnet yet (tabled), so skip them on mainnet — only the
# permissionless mF-ONE daemon deploys there.
if: ${{ inputs.network != 'mainnet' }}
uses: symbioticfi/github-workflows-common/.github/workflows/deploy.yml@main
secrets: inherit
strategy:
Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/deploy_prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ jobs:
namespace: '${{ matrix.network }}-symbiotic-fi'
digest: ${{ needs.docker-build.outputs.digest }}

# deploy-prod:
# name: Deploy symbiotic-fi
# needs:
# - docker-build
# - deploy-public-stage
# uses: ./.github/workflows/deploy.yml
# secrets: inherit
# with:
# network: mainnet
# namespace: 'symbiotic-fi'
# digest: ${{ needs.docker-build.outputs.digest }}
deploy-prod:
name: Deploy symbiotic-fi
needs:
- docker-build
- deploy-public-stage
uses: ./.github/workflows/deploy.yml
secrets: inherit
with:
network: mainnet
namespace: 'symbiotic-fi'
digest: ${{ needs.docker-build.outputs.digest }}
1 change: 1 addition & 0 deletions .github/workflows/deploy_stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
fail-fast: false
matrix:
network:
- mainnet
- hoodi
- sepolia
uses: ./.github/workflows/deploy.yml
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ dev-account.local.txt
.vscode/
*.swp
.DS_Store

# Local debug tooling (not part of the solver)
/cmd/3f-debug/
11 changes: 0 additions & 11 deletions AGENTS.md

This file was deleted.

1 change: 1 addition & 0 deletions AGENTS.md
72 changes: 58 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# CLAUDE.md — working agreement for this repo

This file is the source of truth for how code is written here. Read it before making changes.
It applies to AI agents and humans alike. `AGENTS.md` points here.
It applies to AI agents and humans alike. `AGENTS.md` is a symlink to this file.

## Purpose

Expand All @@ -27,6 +27,13 @@ Two layers, and code lives in exactly one:
(today `bridgefacilitator/`). All protocol-specific logic, types, ABIs usage, pricing, and config
live here.

**Shared protocol code** used by ≥2 solvers lives in its own shared package or generated binding — e.g.
Morpho's math in `internal/morpho/`, generated Morpho GraphQL bindings in `api/morphographql`, or neutral
contract bindings like `api/bindings/liquidlane/adapter` / `api/bindings/erc4626` (shared by redstone-oev +
rfq). Hand-written domain adapters stay inside the solver that owns the workflow unless a second solver
actually reuses them. Neutral, protocol-agnostic helpers (config parsing, etc.) live in their own small
helper package — `internal/parse`.

To add a new integration (e.g. `rfq`):
1. Create `internal/solvers/rfq/` implementing `solver.Solver` (`Name()`, `Run(ctx)`), with a
`Factory(raw yaml.Node, deps solver.Deps) (Solver, error)`.
Expand Down Expand Up @@ -104,7 +111,7 @@ bot's view of an external surface honest (it comes from the source of truth, not
that silently drifts), keeps the build hermetic (generated code is committed, so a clean checkout
builds with no network/toolchain surprises), and turns an upstream change into a reviewable diff.

Two instances of the same pattern — **vendor → generate → commit, regenerated only via `make`:**
Three instances of the same pattern — **vendor → generate → commit, regenerated only via `make`:**

- **Contract bindings (ABI → abigen).** Vendor the ABI JSON under `api/abi/` (from a `forge build`
out-dir; `make refresh-abi` extracts `.abi` from the build artifacts of `ABIS`/`CORE_MIRROR_ABIS`),
Expand All @@ -115,8 +122,9 @@ Two instances of the same pattern — **vendor → generate → commit, regenera
renamed method or changed signature in a refreshed ABI breaks the build at the call site instead of
panicking at runtime. **Never reintroduce stringly-typed `abi.Pack("method", …)`/`abi.Unpack` in
solver code** — use the generated `Pack`/`Unpack` (or `TryPack` for the error-returning variant).
Multicall3 stays on v1 (`BINDINGS_V1`): it's the transport (`chain.Multicall` binds its `Aggregate3`
caller), where v2's pure helpers buy nothing. An ABI that can't be sourced from a build (e.g.
Multicall3 is v2 like every other binding: `chain.Multicall` builds its sub-calls with the generated
`PackAggregate3`/`UnpackAggregate3` pure helpers and does its own `eth_call`. An ABI that can't be
sourced from a build (e.g.
Multicall3, or a minimal hand-pruned `UniversalDelegator` whose full ABI has an abigen-hostile
overload) is hand-vendored into `api/abi/` with a comment saying why — still generated from, never
hand-bound.
Expand All @@ -127,8 +135,14 @@ Two instances of the same pattern — **vendor → generate → commit, regenera
(e.g. 7.12.0 for an OpenAPI 3.1 spec with numeric `exclusiveMinimum` / `type:[…,null]` unions, which
`oapi-codegen`/kin-openapi and `ogen` reject). The recipe strips the generator's non-package cruft
(its `go.mod`/docs/test/etc.), keeping only the Go client so it joins the main module.

Rules for both: the vendored artifact (ABI/spec) is the **contract of record** — when upstream changes,
- **GraphQL clients (schema SDL + operations → genqlient).** Vendor the upstream schema SDL under
`api/graphql/<name>/` (`make refresh-morpho-graphql-schema` pulls Morpho's live schema), keep named
operation documents under `operations/`, then `make refresh-morpho-graphql-client` runs pinned
`genqlient` into `api/<client>/` and emits `operations.json` for review/safelisting. The generated package
is the shared binding; hand-written adapters that parse generated response types into domain types live in
the owning integration until reuse proves they belong elsewhere.

Rules for every generated surface: the vendored artifact (ABI/spec/schema) is the **contract of record** — when upstream changes,
re-vendor + regenerate in the same change rather than patching generated Go. The integration code wraps
the generated client/binding behind a thin adapter so generated types (nullable pointers, response
wrappers) stay contained at the boundary and don't leak into solver logic. Reach for this pattern
Expand All @@ -155,25 +169,55 @@ Write defensively; this bot holds a signing key and moves funds.
- Prefer the standard library and already-vendored deps; adding a dependency is a deliberate decision
(supply-chain surface). Run `make tidy` and keep `go.sum` honest.

## Keep the plan in sync — required
## Commits — semantic titles

Commit titles follow [Conventional Commits](https://www.conventionalcommits.org):
`type(scope): summary`.

- **type** — one of `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `build`, `ci`, `perf`.
- **scope** — the area touched, lowercase: the solver or subsystem (`rfq`, `3f`, `oev`,
`strategy`, `config`, `deps`, `bindings`, …). Optional but preferred.
- **summary** — imperative mood, lowercase, no trailing period
(`feat(rfq): route quotes through pluggable strategies`, `fix(3f): bound 3F API calls with an HTTP timeout`).
- Breaking changes: append `!` after the scope (`refactor(rfq)!: …`) and explain the break in the body.
- Keep the title under ~72 chars; put detail, rationale, and any plan-sync note in the body.

## Keep the docs in sync — required

The per-solver plans under `docs/` (e.g. `docs/3F-PLAN.md`, `docs/RFQ-PLAN.md`) are the source of truth
for the high-level architecture, design decisions, and the live TODO list. They are not write-once docs.
Two audiences, two docs, kept current **in the same change** as the code:

**Plans** (`docs/*-PLAN.md`, plus the cross-cutting `docs/strategy-plan.md`) are the source of truth
for **internal architecture, design decisions, and the live TODO list** — write for a future
maintainer.

- **Whenever you change the high-level architecture or a design decision** — a new layer or boundary,
a changed data flow, a new/removed integration, an interface or external-contract change, a
deliberate deviation from an upstream reference — **update the relevant plan in the same change.**
- **Whenever the TODO work changes** — an item is started, finished, dropped, or added — **update the
TODO list (§10 of the relevant solver plan)** so it always reflects reality.
- A code change that alters architecture/design but leaves the plan stale is **incomplete**. If a
change is purely local (a bug fix, a refactor with no design impact), no plan update is needed —
use judgement, but err toward recording anything a future reader would be surprised to discover.
- A code change that alters architecture/design but leaves a plan stale is **incomplete**.

**README** (`README.md`) is the **external, user-facing** entry point — write for an operator or
integrator running the bot, not a maintainer of it. Keep internal design out of it; keep runtime and
integration surface in it.

- **Whenever you change something a user observes or configures** — a new or renamed CLI flag or
subcommand, a config knob or its default, a new/removed solver or a change to what a solver does, a
new strategy or integration surface, quickstart/build/run steps, or requirements — **update the
README in the same change.**
- A user-facing change (flag, config field, solver capability) that lands without a README update is
**incomplete**.

If a change is purely internal (a bug fix or refactor with no design impact and nothing a user
observes), neither doc needs an update — use judgement, but err toward recording anything a future
reader or operator would be surprised to discover.

## Quick reference

- Run gate: `make format && make test && make lint && go build ./...`
- Add an integration: new `internal/solvers/<name>/` + `solver.Register` in `init()` + bindings under
`api/bindings/<name>/` + a `solvers[]` entry. No framework changes.
- Config is king: if it varies by deployment, it belongs in the YAML, not in code.
- Keep the plan current: architecture/design or TODO changes must update `docs/*-PLAN.md`
in the same change.
- Keep the docs current in the same change: architecture/design or TODO changes update `docs/*-PLAN.md`;
user-facing changes (CLI flags, config knobs, solver/strategy capabilities) update `README.md`.
- Commit titles are Conventional Commits: `type(scope): imperative summary` (e.g. `feat(rfq): …`).
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Symbiotic

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading