29 KiB
Redis Activity Pool Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Build the production Redis activity pool that atomically owns short-lived Proxy inventory, health availability, exclusive extraction, Worker ownership, bounded idempotency, and expiry cleanup without putting Redis on the Gateway request path.
Architecture: One deep redisactivity.Adapter implements the existing narrow domain ports plus health, inventory, and maintenance ports. The first release targets Redis standalone/Sentinel; all keys share {activity} for future single-slot Cluster compatibility, and bounded Lua scripts serialize every state transition that can race.
Tech Stack: Go 1.26, github.com/redis/go-redis/v9, embedded Redis Lua scripts, Redis 8.2 integration tests, Docker Compose, PowerShell verification.
Public Test Seams
The approved public seams are:
activitypool.Upserter.UpsertFetchedactivitypool.HealthStore.ApplyHealthactivitypool.InventoryReader.Inventoryextraction.Store.Extractownership.Repositorycontext-aware methodsactivitypool.Maintainer.SweepExpired
Tests assert behavior only through these seams. Lua source, Redis key contents, codec fields, script SHA values, and private runner calls are not test seams.
File Map
- Modify
internal/domain/activitypool/pool.go: public activity contracts and memory reference implementation. - Modify
internal/domain/activitypool/pool_test.go: shared behavioral expectations for max size, health, inventory, and maintenance. - Modify
internal/domain/ownership/ownership.go: context-aware production repository contract. - Modify
internal/controller/pool/ownership.go: pass contexts and return storage failures. - Modify
internal/controller/pool/ownership_test.go: public manager contract after the signature migration. - Modify
internal/controller/provider/reconciler.go: passpool.maxSizeinto authoritative Upsert. - Modify
internal/controller/provider/reconciler_test.go: verify max-size propagation. - Modify
internal/controller/extraction/service.go: classify Redis availability failures as 503-safe service errors. - Modify
internal/controller/extraction/service_test.go: verify error classification. - Create
internal/adapters/redisactivity/adapter.go: constructor, dependencies, options, interface assertions. - Create
internal/adapters/redisactivity/keys.go: normalized key construction and hash tag enforcement. - Create
internal/adapters/redisactivity/codec.go: Proxy, ownership, result, and script reply encoding. - Create
internal/adapters/redisactivity/scripts.go: embedded script declarations. - Create
internal/adapters/redisactivity/upsert.go: credential materialization and bounded Upsert calls. - Create
internal/adapters/redisactivity/health.go: health transition adapter. - Create
internal/adapters/redisactivity/extract.go: extraction command digest, reply mapping, and URL construction. - Create
internal/adapters/redisactivity/ownership.go: context-aware ownership methods. - Create
internal/adapters/redisactivity/maintenance.go: bounded expiry cleanup and inventory reads. - Create
internal/adapters/redisactivity/scripts/*.lua: atomic Redis state transitions. - Create
internal/adapters/redisactivity/contract_test.go: real Redis public-seam contract suite. - Create
internal/adapters/redisactivity/testredis_test.go: isolated integration client and namespace helpers. - Create
deploy/docker-compose.test.yml: non-persistent Redis 8.2 test fixture. - Create
deploy/compose_test.go: structured local Redis persistence-policy assertion. - Create
scripts/test-redis.ps1: bounded integration-test runner. - Modify
deploy/docker-compose.yml: make local runtime Redis explicitly non-persistent. - Modify
docs/development/implementation-plan.md: record the delivered Adapter boundary. - Modify
docs/testing/test-strategy.md: record Redis contract and failure tests.
Task 1: Make Ownership Storage Context-Aware
Files:
-
Modify:
internal/domain/ownership/ownership.go -
Modify:
internal/controller/pool/ownership.go -
Modify:
internal/controller/pool/ownership_test.go -
Modify:
internal/domain/activitypool/pool.go -
Modify:
internal/domain/activitypool/pool_test.go -
Step 1: Write failing manager tests for cancellation, storage errors, and bounded expiry
Add a recording repository implementing the new intended contract and tests with these calls:
ctx, cancel := context.WithCancel(context.Background())
cancel()
if _, err := manager.Assign(ctx, now, "proxy-1", "worker-1", time.Minute); !errors.Is(err, context.Canceled) {
t.Fatalf("Assign() error = %v, want context.Canceled", err)
}
expired, err := manager.Expire(context.Background(), now, 32)
if err != nil {
t.Fatalf("Expire(): %v", err)
}
if repository.expireLimit != 32 || len(expired) != 1 {
t.Fatalf("Expire() = %+v, limit=%d", expired, repository.expireLimit)
}
- Step 2: Run the focused tests and verify RED
Run:
go test -timeout 60s ./internal/controller/pool ./internal/domain/activitypool
Expected: compilation fails because the current manager methods do not accept a context and
Expire has neither a limit nor an error result.
- Step 3: Replace the ownership port and manager signatures
Use this exact repository contract:
type Repository interface {
Assign(context.Context, time.Time, string, string, time.Duration) (Assignment, error)
Renew(context.Context, time.Time, string, string, uint64, time.Duration) (Assignment, error)
BeginDrain(context.Context, string, string, uint64) (Assignment, error)
AcknowledgeDrain(context.Context, string, string, uint64, int64, int64) error
Get(context.Context, string) (Assignment, bool, error)
Expire(context.Context, time.Time, int) ([]Assignment, error)
}
Update OwnershipManager to validate ctx != nil, pass the context unchanged, require
limit > 0, and return repository errors without swallowing them. Update MemoryPool to
check ctx.Err() before and after locking, return (Assignment, bool, error) from Get,
and stop Expire after limit assignments.
- Step 4: Update all ownership call sites and run GREEN
Pass context.Background() from existing tests that do not exercise cancellation. Run:
go test -timeout 60s ./internal/controller/pool ./internal/domain/activitypool
Expected: PASS.
- Step 5: Commit the interface migration
git add internal/domain/ownership internal/domain/activitypool internal/controller/pool
git commit -m "refactor: make ownership repository context aware"
Task 2: Add Health, Inventory, MaxSize, and Maintenance Contracts
Files:
-
Modify:
internal/domain/activitypool/pool.go -
Modify:
internal/domain/activitypool/pool_test.go -
Modify:
internal/controller/provider/reconciler.go -
Modify:
internal/controller/provider/reconciler_test.go -
Step 1: Write failing public-seam tests
Add tests covering a Fetched -> Checking -> Available transition, stale health rejection, per-upstream MaxSize, current inventory, and bounded expiry:
updated, err := pool.ApplyHealth(context.Background(), HealthUpdate{
ProxyID: proxyID, CheckedAt: now.Add(time.Second), NextState: proxyDomain.StateChecking,
})
if err != nil || updated.State != proxyDomain.StateChecking {
t.Fatalf("ApplyHealth(checking) = %+v, %v", updated, err)
}
updated, err = pool.ApplyHealth(context.Background(), HealthUpdate{
ProxyID: proxyID, CheckedAt: now.Add(2 * time.Second),
NextState: proxyDomain.StateAvailable, Latency: 25 * time.Millisecond,
})
if err != nil || updated.State != proxyDomain.StateAvailable {
t.Fatalf("ApplyHealth(available) = %+v, %v", updated, err)
}
inventory, err := pool.Inventory(context.Background(), "provider-a", now.Add(2*time.Second))
if err != nil || inventory.Managed != 1 {
t.Fatalf("Inventory() = %+v, %v", inventory, err)
}
Insert two distinct candidates with FetchedBatch.MaxSize = 1 and assert one insert plus one
capacity drop. Call SweepExpired(ctx, afterExpiry, 1) twice and assert each call removes at
most one record.
- Step 2: Run the tests and verify RED
go test -timeout 60s ./internal/domain/activitypool ./internal/controller/provider
Expected: compilation fails because the new ports and MaxSize do not exist.
- Step 3: Add the approved activity contracts
Add these public types:
type HealthUpdate struct {
ProxyID string
CheckedAt time.Time
NextState proxyDomain.State
Latency time.Duration
}
type Inventory struct {
UpstreamID string
Managed int
}
type HealthStore interface {
ApplyHealth(context.Context, HealthUpdate) (Entry, error)
}
type InventoryReader interface {
Inventory(context.Context, string, time.Time) (Inventory, error)
}
type Maintainer interface {
SweepExpired(context.Context, time.Time, int) (int, error)
}
Add MaxSize int to FetchedBatch. Reject non-positive MaxSize as an invalid batch. Count
only managed states belonging to the incumbent upstream. Capacity-rejected candidates count
as Dropped and never create unique-key mappings.
ApplyHealth must reject missing IDs, zero time, negative latency, missing entries, invalid
state transitions, and observations older than LastCheckedAt. Replaying the same state and
timestamp is idempotent. A transition to AVAILABLE updates LastSuccessAt.
- Step 4: Pass MaxSize from Provider Reconciler
Add MaxSize int to provider.Config, require it to be positive, and populate:
activitypool.FetchedBatch{
ObservedAt: r.runtime.Clock.Now().UTC(),
ConfiguredTTL: r.config.TTL,
AllocationSafetyMargin: r.config.AllocationSafetyMargin,
MaxSize: r.config.MaxSize,
Proxies: retained,
}
Update constructor tests to use a positive MaxSize and assert the activity sink receives it.
- Step 5: Run GREEN and commit
go test -timeout 60s ./internal/domain/activitypool ./internal/controller/provider ./internal/controller/pool
git add internal/domain/activitypool internal/controller/provider
git commit -m "feat: add activity health and inventory contracts"
Expected: PASS, then a commit containing only this slice.
Task 3: Classify Activity Store Availability Failures
Files:
-
Modify:
internal/domain/extraction/extraction.go -
Modify:
internal/controller/extraction/service.go -
Modify:
internal/controller/extraction/service_test.go -
Modify:
internal/controller/distribution/handler_test.go -
Step 1: Write failing service and HTTP error tests
Configure a recording Store to return domain.ErrStoreUnavailable. Assert:
_, err := service.Extract(context.Background(), validRequest)
if !errors.Is(err, ErrUnavailable) || !errors.Is(err, domain.ErrStoreUnavailable) {
t.Fatalf("Extract() error = %v, want unavailable classification", err)
}
At the Handler seam, assert the same error becomes HTTP 503 with code
SERVICE_UNAVAILABLE and no underlying Redis text in the response body.
- Step 2: Run RED
go test -timeout 60s ./internal/controller/extraction ./internal/controller/distribution
Expected: compilation fails because ErrStoreUnavailable is not defined.
- Step 3: Add the stable storage error and mapping
Add:
var ErrStoreUnavailable = errors.New("extraction store unavailable")
In Service.Extract, preserve ErrInsufficientProxies, ErrIdempotencyConflict, and
ErrInvalidCommand; wrap any error matching ErrStoreUnavailable with ErrUnavailable:
if err != nil {
if errors.Is(err, domain.ErrStoreUnavailable) {
return response, errors.Join(ErrUnavailable, err)
}
return response, err
}
- Step 4: Run GREEN and commit
go test -timeout 60s ./internal/controller/extraction ./internal/controller/distribution
git add internal/domain/extraction internal/controller/extraction internal/controller/distribution
git commit -m "feat: classify extraction store failures"
Expected: PASS.
Task 4: Add Redis Adapter Foundation
Files:
-
Modify:
go.mod -
Modify:
go.sum -
Create:
internal/adapters/redisactivity/adapter.go -
Create:
internal/adapters/redisactivity/keys.go -
Create:
internal/adapters/redisactivity/codec.go -
Create:
internal/adapters/redisactivity/scripts.go -
Create:
internal/adapters/redisactivity/adapter_test.go -
Create:
internal/adapters/redisactivity/testredis_test.go -
Create:
deploy/docker-compose.test.yml -
Create:
scripts/test-redis.ps1 -
Step 1: Add failing constructor and key-safety tests
Test that nil clients, nil credential stores, empty namespaces, braces in namespaces, zero operation TTL, and non-positive scan/cleanup limits fail. Test that valid options build keys with exactly one fixed hash tag:
adapter, err := New(client, Options{
Namespace: "test-a", Credentials: credentialStore,
OperationTTL: time.Minute, MaxCandidateScan: 2048, CleanupLimit: 128,
})
if err != nil {
t.Fatalf("New(): %v", err)
}
if got := adapter.keys.records; got != "pp:{activity}:test-a:records" {
t.Fatalf("records key = %q", got)
}
- Step 2: Run RED
go test -timeout 60s ./internal/adapters/redisactivity
Expected: package does not exist.
- Step 3: Pin go-redis and implement the narrow constructor
Run:
go get github.com/redis/go-redis/v9@v9.19.0
Use redis.Scripter rather than exposing redis.UniversalClient throughout the package:
type Options struct {
Namespace string
Credentials credentials.Store
OperationTTL time.Duration
MaxCandidateScan int
CleanupLimit int
}
type Adapter struct {
client redis.Scripter
credentials credentials.Store
keys keyspace
options Options
}
func New(client redis.Scripter, options Options) (*Adapter, error)
Embed immutable scripts once and reuse redis.Script, which provides EVALSHA with EVAL
fallback when the server script cache is empty:
//go:embed scripts/upsert.lua
var upsertSource string
var upsertScript = redis.NewScript(upsertSource)
Normalize the namespace with a strict [A-Za-z0-9._-]+ policy. Hash user-controlled
Client IDs, idempotency keys, proxy unique keys, and operation IDs with SHA-256 before using
them in Redis key names or fields.
- Step 4: Implement deterministic codecs with redacted formatting
Use JSON only at the Redis boundary. Define private proxyRecord, ownershipRecord,
idempotencyRecord, and typed script reply structures. Store times as Unix milliseconds and
durations as integer nanoseconds. Decode with strict state and integer validation. Any
formatting method for structures containing passwords writes <redacted>.
- Step 5: Run GREEN and commit
Create the Redis 8.2 test fixture before the first integration slice. It binds only
127.0.0.1:16379, has no data volume, runs --appendonly no --save "", and exposes a PING
health check. Add testredis_test.go that skips integration-tagged tests when
PROXY_POOL_TEST_REDIS_URL is absent and otherwise creates a unique namespace without
flushing shared databases. Add scripts/test-redis.ps1 with try/finally fixture shutdown.
gofmt -w internal/adapters/redisactivity
go test -timeout 60s ./internal/adapters/redisactivity
git add go.mod go.sum internal/adapters/redisactivity deploy/docker-compose.test.yml scripts/test-redis.ps1
git commit -m "feat: add redis activity adapter foundation"
Expected: PASS.
Task 5: Implement Atomic Upsert and Health Transitions
Files:
-
Create:
internal/adapters/redisactivity/upsert.go -
Create:
internal/adapters/redisactivity/health.go -
Create:
internal/adapters/redisactivity/scripts/upsert.lua -
Create:
internal/adapters/redisactivity/scripts/health.lua -
Create:
internal/adapters/redisactivity/upsert_integration_test.go -
Step 1: Write failing real-Redis tests at the approved seams
Put //go:build integration at the top of every real-Redis test file and use a unique
namespace per test. Cover:
result, err := store.UpsertFetched(ctx, "provider-a", activitypool.FetchedBatch{
ObservedAt: now, ConfiguredTTL: 30 * time.Second,
AllocationSafetyMargin: 3 * time.Second, MaxSize: 1,
Proxies: []proxyDomain.Proxy{first, second},
})
if err != nil || result.Inserted != 1 || result.Dropped != 1 {
t.Fatalf("UpsertFetched() = %+v, %v", result, err)
}
Also verify cross-provider incumbent preservation, refresh preserving runtime health, expired incumbent replacement, credential resolution before commit, Fetched -> Checking -> Available, and stale health observations not replacing newer state.
- Step 2: Start Redis and verify RED
docker compose -f deploy/docker-compose.test.yml up -d --wait
$env:PROXY_POOL_TEST_REDIS_URL='redis://127.0.0.1:16379/15'
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*(Upsert|Health)'
Expected: tests fail because Upsert and Health return no production behavior.
- Step 3: Implement bounded Upsert
Resolve every non-empty credential reference before running Lua:
value, err := a.credentials.Resolve(ctx, credentials.Reference{
SecretRef: candidate.SecretRef, CredentialVersion: candidate.CredentialVersion,
})
if err != nil {
return activitypool.UpsertResult{}, fmt.Errorf("resolve proxy credential: %w", err)
}
The script receives validated records and atomically maintains records, unique, idkeys,
expiry, available, facet indexes, and inventory. It performs a cleanup batch first,
preserves incumbent upstream and runtime state, refuses EXTRACTED refresh, enforces MaxSize,
and stores an operation result keyed by one generated operation ID so EVALSHA retry returns
the original counters.
- Step 4: Implement Health state changes
The health script loads one record, rejects missing/expired entries, rejects invalid or stale
transitions, updates health fields, and adds or removes every availability index in the same
atomic call. Replaying the same CheckedAt and state returns the stored result without
changing indexes.
- Step 5: Run GREEN and commit
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*(Upsert|Health)'
git add internal/adapters/redisactivity
git commit -m "feat: add redis activity upsert and health"
Expected: PASS.
Task 6: Implement Atomic Exclusive Extraction
Files:
-
Create:
internal/adapters/redisactivity/extract.go -
Create:
internal/adapters/redisactivity/scripts/extract.lua -
Create:
internal/adapters/redisactivity/extract_integration_test.go -
Step 1: Write failing extraction contract tests
Seed AVAILABLE records through Upsert/ApplyHealth and test partial, allOrNothing, every filter, MinRemainingTTL, MaxHealthCheckAge, ReserveForGateway, same-key replay, conflicting digest, and bounded idempotency expiry. Add a 100-round concurrent race:
for iteration := 0; iteration < 100; iteration++ {
// Two goroutines call Extract for the same one-item namespace.
// Exactly one returned result must contain the Proxy ID.
}
Test that a selective query exhausting MaxCandidateScan returns
extraction.ErrStoreUnavailable and leaves every Proxy AVAILABLE.
- Step 2: Run RED against Redis 8.2
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*Extract'
Expected: FAIL because Adapter.Extract is absent.
- Step 3: Implement request digest and typed reply mapping
Canonicalize each filter as a sorted, duplicate-free list and hash this payload:
type digestInput struct {
Requested int `json:"requested"`
Fulfillment extraction.Fulfillment `json:"fulfillment"`
Protocols []string `json:"protocols"`
Regions []string `json:"regions"`
Carriers []string `json:"carriers"`
Upstreams []string `json:"upstreams"`
}
Exclude RequestID, SourceIP, Now, and operational TTLs from the business digest. Use Client ID plus Idempotency-Key for business replay; use RequestID only for an internal operation key.
- Step 4: Implement one bounded extraction script
The script must:
- Return a committed result when the idempotency digest matches.
- Return conflict without mutation when it differs.
- Choose the smallest
ZCARDamong filter dimensions containing exactly one requested value; otherwise use global available. This keeps the driver a complete superset when a dimension contains OR values. - Scan from longest
usableUntil, validate complete records, and stop afterrequested + reserveForGatewaymatches orMaxCandidateScanrecords. - Return scan-budget exhaustion without mutation when the result cannot be decided.
- Return insufficient without mutation for allOrNothing.
- Mark selected records EXTRACTED, remove every availability index, decrement inventory, and store the response in the same script.
- Set idempotency TTL to the earlier of configured TTL and earliest selected hard expiry.
Build URL values in Go with net/url.URL and net.JoinHostPort; never concatenate user or
password fields manually.
- Step 5: Run GREEN and commit
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*Extract'
git add internal/adapters/redisactivity
git commit -m "feat: add atomic redis proxy extraction"
Expected: PASS.
Task 7: Implement Ownership, Inventory, and Expiry Maintenance
Files:
-
Create:
internal/adapters/redisactivity/ownership.go -
Create:
internal/adapters/redisactivity/maintenance.go -
Create:
internal/adapters/redisactivity/scripts/ownership.lua -
Create:
internal/adapters/redisactivity/scripts/sweep.lua -
Create:
internal/adapters/redisactivity/ownership_integration_test.go -
Step 1: Write failing public-seam tests
Cover Assign, expired-lease takeover, Renew version increments, stale epoch rejection, BeginDrain idempotency, ACK active/reserved rejection, successful ACK, Get, limited Expire, Inventory decrement, and limited SweepExpired. Add 100 Assign-vs-Extract races and assert exactly one winner each time.
- Step 2: Run RED
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*(Ownership|Inventory|Sweep)'
Expected: FAIL because these Adapter methods are absent.
- Step 3: Implement constant-work ownership scripts
Use one operation selector with separate validated argument shapes:
assign | renew | begin_drain | acknowledge_drain | get | expire
Assign removes the Proxy from AVAILABLE indexes. Renew caps lease expiry at usableUntil.
ACK restores indexes only when the Proxy remains AVAILABLE and usable. Each mutating call
uses an internal operation ID so a transport retry does not increment epoch/version twice.
Expire processes no more than the caller-provided limit.
- Step 4: Implement inventory and bounded sweep
Inventory first runs the same small cleanup budget, then returns the upstream counter.
SweepExpired pops no more than limit hard-expired Proxy IDs, removes record/unique/id/facet/
ownership indexes, and decrements only the incumbent upstream counter. Repeated sweep calls
are idempotent and counters never go below zero.
- Step 5: Run GREEN and commit
go test -tags=integration -timeout 60s ./internal/adapters/redisactivity -run 'TestRedis.*(Ownership|Inventory|Sweep)'
git add internal/adapters/redisactivity
git commit -m "feat: add redis ownership and expiry maintenance"
Expected: PASS.
Task 8: Reuse One Contract Suite for Memory and Redis
Files:
-
Create:
internal/domain/activitypool/contracttest/contract.go -
Create:
internal/domain/activitypool/contract_external_test.go -
Create:
internal/adapters/redisactivity/contract_integration_test.go -
Modify:
internal/adapters/redisactivity/testredis_test.go -
Modify:
scripts/test-redis.ps1 -
Step 1: Extract behavior-only contract cases
Define a factory that returns the approved narrow seams and cleanup:
type Store interface {
activitypool.Upserter
activitypool.HealthStore
activitypool.InventoryReader
activitypool.Maintainer
extraction.Store
ownership.Repository
}
type Factory func(*testing.T) (Store, func())
func Run(t *testing.T, factory Factory)
Create contract_external_test.go with package activitypool_test and run the shared suite
against activitypool.NewMemoryPool. Retain the existing package-internal Snapshot tests in
pool_test.go; do not create an import cycle and do not inspect Redis keys from the shared
contract.
- Step 2: Run the contract against MemoryPool
go test -timeout 60s ./internal/domain/activitypool/...
Expected: PASS.
- Step 3: Complete the isolated Redis fixture runner
Extend scripts/test-redis.ps1 to run the complete integration contract. It must use
try/finally, wait for health, set PROXY_POOL_TEST_REDIS_URL, run only integration-tagged
tests with a 60-second Go timeout, and stop the fixture in finally.
- Step 4: Run the same contract against Redis
.\scripts\test-redis.ps1
Expected: Redis 8.2 becomes healthy and all integration tests PASS in at most 60 seconds per Go test invocation.
- Step 5: Commit the shared contract and fixture
git add internal/domain/activitypool internal/adapters/redisactivity scripts/test-redis.ps1
git commit -m "test: add redis activity pool contract suite"
Task 9: Finalize Local Runtime Policy and Documentation
Files:
-
Modify:
deploy/docker-compose.yml -
Create:
deploy/compose_test.go -
Modify:
docs/development/implementation-plan.md -
Modify:
docs/testing/test-strategy.md -
Modify:
docs/operations/runbook.md -
Modify:
docs/requirements/traceability.md -
Modify:
progress.md -
Step 1: Write a failing Compose policy check
Add deploy/compose_test.go using go.yaml.in/yaml/v4 to parse docker-compose.yml and assert
that the local Redis service uses --appendonly no, --save "", and has no persistent data
volume. Run go test -timeout 60s ./deploy before changing Compose and confirm it fails.
- Step 2: Make local Redis explicitly ephemeral
Change only the Redis service command and volume mount. Keep health checks and the backend network unchanged. Do not remove or manipulate any existing Docker volume on the machine.
- Step 3: Update authoritative documents
Record:
-
Redis Adapter and shared contract as complete.
-
Gateway hot path still has no Redis access.
-
PostgreSQL still contains no Proxy details or extraction records.
-
Redis Activity Pool is runtime truth and local Redis is intentionally non-persistent.
-
docs/testing/strategy.mdlegacy PostgreSQL extraction wording is superseded. -
docs/requirements/traceability.mdno longer claims per-proxy extraction audit storage. -
100,000 QPS remains an unverified end-to-end target.
-
Step 4: Run full verification
.\scripts\verify.ps1
.\scripts\test-redis.ps1
git diff --check
Expected: gofmt, go vet, all unit tests, build, and Redis integration tests PASS. Race tests
run only when CGO_ENABLED=1; otherwise the script prints the existing explicit skip reason.
- Step 5: Perform independent review
Review public contracts, Lua atomicity, retry behavior, TTL bounds, credential redaction,
inventory counters, and the absence of Redis calls in internal/gateway. Fix every blocking
finding and rerun both verification scripts.
- Step 6: Commit the runtime policy and documentation
git add deploy/docker-compose.yml deploy/compose_test.go docs/development/implementation-plan.md docs/testing/test-strategy.md docs/operations/runbook.md docs/requirements/traceability.md progress.md
git commit -m "docs: finalize redis activity pool delivery"
- Step 7: Push the completed branch
Verify the diff contains no user-owned deletion, then push:
git status --short
git push origin build/proxy-pool-architecture
Expected: the remote branch advances through all Redis Activity Pool commits while
proxy-pool-docs-v1.0.zip remains untracked by the commits.
Self-Review Result
- ADR-005 deployment, module, keyspace, Upsert, Health, Extract, Ownership, cleanup, credentials, inventory, failure, test, and persistence sections each map to a task above.
- Every public method used by later tasks is defined in Tasks 1-4.
- Memory and Redis implementations share behavior tests only through approved public seams.
- No PostgreSQL Proxy storage, Gateway Redis hot-path access, Cluster sharding, or production throughput claim is introduced.