Repository navigation
Fable fixes - #1
Merged
Merged
Conversation
Fixes the correctness-critical items from the six-agent TODO review, one module group per fix, each with a regression test (Go-produced fixtures where the claim is cross-language). Networking: - NET-01 half-open trial failure now unconditionally re-trips the breaker (was stuck .halfOpen under production resetTimeout > window) - NET-02 cancellation no longer records a breaker failure - NET-03 injectable status classifier; 502/503/504 count as failures - NET-04 SSE/WebSocket set continuation.onTermination (abandoned-consumer leak) - NET-05 Event payload numbers preserve int64/decimal precision (Decimal, not Double) - NET-06 Retry.isTerminal recognizes URLError.cancelled - NET-07 CircuitBreaker.execute exempts URLError.cancelled Observability: - OBS-01 single-lock read-modify-write in LiveOperation (lost-update race) - OBS-02 NoopMetricsProvider stays silent after MetricsSystem.bootstrap - OBS-03 OSLogLogger renders field values .private (PII redaction) - OBS-04 lenient Codable (partial config decodes) - OBS-05 RecordingOperation records the success acknowledgement Services / util / crypto: - SVC-01 defer transaction finish() to the caller (consumable-loss window) - SVC-02 rethrow typed CapitalismError raw instead of wrapping - SVC-03 PostHogConfig lenient Codable - UTIL-01 CookieManager bounds unset lifetime to securecookie's 30-day default - UTIL-02 Version propagates write failures; .sortedKeys for stable JSON - CRY-01 TOTP clamps pre-1970 dates instead of trapping - CRY-02 XID wraps timestamps (truncatingIfNeeded); gethostname(2) for machine id - CRY-03 Base32 rejects mid-string padding and impossible lengths 591 tests pass (was 558); 33 new regression tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce a Sendable `AttributeValue` enum (string/int/double/bool + array cases, ExpressibleBy*Literal sugar) shared by Span.attach, Operation set/setValues/spanOnly/logOnly, and Logger.withValue/withValues, replacing the untyped `Any` surface. - An `AttributeRepresentable` bridge (String/Int/Int64/Double/Bool/[String]) keeps every existing scalar call site compiling with no churn; AttributeValue itself intentionally does NOT conform, so the generic sugar can never witness the concrete requirement (which would recurse forever) — a conformer that forgets the concrete method now fails to compile instead of stack-overflowing. - LiveOperation.set drops the pre-stringify dance: the Sendable value crosses into the lock closure directly, keeping the OBS-01 lost-update fix intact. - Span.recordError now emits OTel-semconv exception.type/exception.message attributes; field values render on the private os_log channel. Foundation for Wave 2 (feeds OBS-12 initial-attributes). Full suite green (592). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Hasher protocol shadowed Swift.Hasher for importers of the Cryptography module, breaking manual Hashable conformances. Renamed the protocol only (SHA256Hasher, SHA512Hasher, Adler32Hasher, CRC64Hasher, FNVHasher struct names are unchanged) and updated doc references and the test hasher array's type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit beb5f38bf40e504d5e9683dafc069d9ba414f556)
Mark JWTVerificationKey @unchecked Sendable: its .rsa case wraps a SecKey, which is immutable once created and only ever read via the synchronous SecKeyVerifySignature call, so no mutable state crosses an isolation boundary. JWTParser now conforms to Sendable directly, letting HMAC/ES256 (and RSA) parsers be stored in actors or crossed into @sendable closures/Tasks. Adds a regression suite (JWTParserSendableTests) proving a parser can be stored in an actor and captured in a Task.detached @sendable closure — code that would not have compiled before this change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit c78ff1b7def5ea5ed66f601cab720743652e973e)
…status/sampling
OBS-11: add async no-op Pillars.shutdown() mirroring DiagnosticsProvider.shutdown,
so a future OTLP exporter can flush on teardown without a breaking change.
OBS-12:
- Span.setStatus(_:) with SpanStatus { ok, error, unset }; implemented on
SignpostSpan (emits a signpost status event) and NoopSpan.
- SpanKind { client, server, internal, producer, consumer } and full
startSpan(_:kind:attributes:) on Tracer, with bare/partial overloads so
existing startSpan(_:) call sites are untouched. Kind rides the signpost
interval message (interval names must be StaticString).
- TracingConfig.sampleRatio: Double = 1.0, lenient-decoded (present in {}).
- Per-component tracer naming via Tracer.named(_:); SignpostTracer folds the
component into the signpost category ("spans.<component>") so Instruments can
group by component. Wired through LiveObserver.init.
- README: corrected the "interval named after the function" claim.
Regression tests: shutdown awaitable on .noop and live pillars; setStatus on
both backends; startSpan kind+attributes; sampleRatio decode incl. {};
per-component category naming.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ba394d68f54ce4ddb2cad3f506e0207a68f719c6)
CapitalismConfig.provideManager now takes Pillars (like LLMConfig.provideLLMProvider) instead of a prebuilt Observer, building the observer internally via makeObserver with StoreKitPurchaseManager.o11yName. Aligns the two config factories on one convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 2b5162878e2b1cc0c6036ae1bbce4ec6136fe510)
Corrects diverged literal values (filter.* -> query_filter.*, connection.url -> connection_url), adds the missing Go constants (name, url, reason, search_query, validation_error, length, index.name, use_database, email.*, remaining query_filter.* variants), and drops the Swift-only `path` key that Go doesn't define and nothing in Sources referenced. Adds a Keys parity test suite pinning every constant's literal string value to catch future drift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 35b967bd2f2c170fd285e968061f41892795b9d9)
Counter/Gauge/Histogram collided with common names (e.g. SwiftUI Gauge). Rename to MetricCounter/MetricGauge/MetricHistogram to match the existing MetricTimer convention, and update MetricsProvider plus its SwiftMetricsProvider/NoopMetricsProvider implementations and StandardCircuitBreaker's consuming fields. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 27b2ef8e0c7487b9451abe2a7f45bc8a86367929)
Add a `headers: [String: String] = [:]` parameter to the connector `connect` API so callers can carry auth (e.g. Authorization) instead of smuggling secrets through query params. - EventStreamConnector / BidirectionalEventStreamConnector gain `connect(to:headers:)`, with a source-compatible `connect(to:)` convenience via protocol extension. - AnyEventStreamConnector's closure is now `(URL, [String: String])`; EventStreamConfig.makeConnector threads headers through. - SSE applies caller headers then sets Accept last so a custom header can't clobber the SSE content-type. - WebSocket now builds a URLRequest and gains an internal connection factory seam so headers land on session.webSocketTask(with: request) and tests can observe the request via a fake connection. - Noop connectors updated to match. Shaped so a later reconnecting wrapper (NET-20) can add a Last-Event-ID header with no further API change; reconnect not implemented here. Tests: SSE stub now routes by an X-Stub-Token header (proving headers reach the wire) and records request headers; new tests assert a custom Authorization header arrives and Accept is preserved, and that WebSocket headers reach the built URLRequest. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 4e1b3d1897ff8d0f22379457ee0e29c1d1fc0dc6)
NET-12: make StandardCircuitBreaker generic over a Clock (default ContinuousClock) so tests can inject a manual clock and drive the open->half-open transition deterministically instead of sleeping. Static defaults become computed (generic types can't store statics); external references use StandardCircuitBreaker<ContinuousClock>. NET-10: delete HTTPClient's local synchronous CircuitBreaker protocol + NoopCircuitBreaker and route HTTPClient through the async actor-backed CircuitBreaking.CircuitBreaker instead. HTTPClient now stores a non-optional breaker defaulting to CircuitBreaking.NoopCircuitBreaker, awaits canProceed()/recordFailure()/recordSuccess(), and re-checks the gate per retry attempt (inside performOnce) rather than once per perform. Wave-1 policies preserved: cancellation never records a failure; 502/503/504 do; an injected status classifier still overrides. Added regression tests: manual-clock half-open transition, and a per-attempt gate re-check that short-circuits a mid-retry trip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 556b916b0e0419e6f990196c8a9e8f97851f877a)
…ilityLog target Move `SwiftLogLogger` out of the core `Observability` module into a new `ObservabilityLog` interop target (deps: Observability + swift-log). Core now carries NO swift-log dependency when the native `.osLog` backend is used: - Removed `import Logging` and `SwiftLogLogger` from core Logger.swift. - Dropped the `.swiftLog` case from `LoggingConfig.Provider` and bootstrap (a pre-0.1.0 breaking change); apps wanting swift-log now import ObservabilityLog and construct `SwiftLogLogger(label:)` for their Pillars. - Package.swift: swift-log moves off the Observability target onto the new ObservabilityLog target/product; added ObservabilityLogTests. DEFERRED (per design decision): swift-metrics stays in core because the `MetricsProvider` surface returns swift-metrics instrument types and every consumer calls them directly. Abstracting that is a wide re-architecture best done together with OBS-20 (OTel), which forces the same instrument abstraction. Full suite green (621). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… retry follow-ups Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidates the uncommitted Wave 3 work (Analytics/FeatureFlags/ Notifications real backends, EventStream reconnect/keepalive, HTTP retry semantics, W3C propagation) and stages the previously-resolved EventStream conflict files so Wave 4 worktrees fork from a clean HEAD. 736 tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…itBreaking seams Wave 4 REPO-05: every protocol seam ships a Noop and a recording Mock. - Cookies: extract CookieManaging protocol from the concrete CookieManager (now conforming); add inert NoopCookieManaging and recording MockCookieManaging (unsigned JSON round-trip + call recording). - EventStream: add recording MockEventStream, MockBidirectionalEventStream, MockEventStreamConnector, MockBidirectionalEventStreamConnector; promote the private FakeWebSocketConnection to public MockWebSocketConnection and add NoopWebSocketConnection. - Retry: add recording MockRetryPolicy (call/attempt counting, configurable attempts). - CircuitBreaking: add recording MockCircuitBreaker and MockKeyedCircuitBreaker. - Analytics/FeatureFlags: fix actor mocks whose public var handlers could not be set cross-actor under Swift 6 — handlers are now private let, injected at init (the shape the tests already use). Focused regression test per new seam (Noop inert; Mock records). 762 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ss + StoreKit testability REPO-05 crypto seams: add Noop + Mock conformers for EncryptorDecryptor (Cryptography), and introduce TOTPGenerator + JWTParsing protocol seams (Authentication) with live conformances plus Noop + Mock doubles. Verifier Noops fail safe (reject) rather than silently accepting; mock handlers use untyped `throws` to stay on the macOS 13 / iOS 16 floor. REPO-11 JWT/base64url strictness (CRY-21): - Base64URLNoPad (Authentication) and Base64URL (Cryptography) now reject the standard-alphabet +/`/`, mid-string/any padding, and whitespace that Go's URLEncoding/RawURLEncoding reject, instead of leniently translating them. - Wrong-typed exp/nbf (string where a number is required) => malformed. - Non-string aud array elements => rejected (were silently dropped). - A `crit` header => rejected (new JWTError.unsupportedCriticalHeader). - Added a `leeway` parameter for exp/nbf clock drift. - AES-GCM master key now stored as SymmetricKey with best-effort zeroization. REPO-10 test debt: - Full RFC 6238 Appendix B: all 18 vectors at 8 digits, all three algorithms (values reconfirmed against pquerna/otp v1.5.0, platform-go's TOTP dep). - Explicit alg:"none"/empty-signature JWT rejection tests. - Strict base64url rejection tests for both codecs. - Garbage + empty decompress for all four CompressionKit algorithms, plus a liveness guard in AppleCompressor.stream so a no-progress codec can't hang. - Extracted a StoreKitClient seam (SystemStoreKitClient live conformer) so StoreKitPurchaseManager's orchestration (SVC-01 deferred finish, SVC-02 typed errors, observability) is unit-testable with a fake. 787 tests pass (was 736). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Filtering (REPO-06): lenient Codable for Pagination and QueryFilteredResult
so a bare {} and missing/null fields decode to Go zero values instead of
throwing; null data -> empty slice. Enforce QueryFilter.maxLimit clamp in
queryItems() (REPO-12), drop the stale "crossed CodingKeys"/"JSON-tag swap"
doc sentences, and document the deliberately-dropped FromParams/ToPagination.
QRCodes (REPO-12/REPO-05): fix the false linkerSettings doc claim and add the
missing recording MockQRCodeBuilder (Result-returning closure to avoid Swift 6
typed-throws closures) with tests.
LLM (REPO-12/REPO-05/REPO-10): remove the dead unsupportedProvider case (and
its garbled salsa20 doc), add LLMProviderMock.setCompletionHandler async
mutator, add Noop/Mock doubles tests and a transport error-classification
matrix. Rename Duration+Wire.swift -> DurationWire.swift as the canonical
DurationWire helper (LLM call sites migrated; HTTPClient/Retry/EventStream/
FeatureFlags/Cookies copies deferred to a cross-module follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
REPO-01: add GitHub Actions on macos-15 (setup-xcode latest-stable): build.yaml runs `make build` + `make test` + `make build-ios`; lint.yaml runs `make lint` (non-blocking until the tree's pre-existing format debt is swept — flip continue-on-error once clean). REPO-02: add `make coverage` (swift test --enable-code-coverage + `llvm-cov export -format=lcov`, resolving profdata/xctest paths from .build) writing coverage.lcov; coverage.yaml uploads to Codecov on push to main, tolerating a missing token on forks. REPO-03: commit `.swift-format` pinning the current toolchain default (2-space indent, lineLength 100) verbatim, so `make format`/`make lint` don't drift across Xcode versions. Byte-identical to today's default, so it introduces zero new reformatting. REPO-12 (slice): drop the empty `ObservabilityOTel` *product* from Package.swift; the placeholder target is retained. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generalize the README beyond observability-only: reframe the intro as a 25-product toolkit, generalize the Installation snippet to adopt products à la carte, and add a per-module status table (tier + "what's real" per product). Correct now-stale claims (OTLP exporter dropped; W3C propagation in core; `.swiftLog` provider case removed by OBS-14). Add PORT_PROGRESS.md modeled on platform-rs's tiered survey (deep-port / pure-logic / native-backend / cloud-transport / placeholder), grounded in the settled source. Records deliberate deferrals: PASETO, zstd/s2 (CRY-20), Filtering FromParams/ToPagination, streaming LLM, OTLP exporter (OBS-20), Redis-backed Cache, LaunchDarkly/RevenueCat/server-push second backends, and the Wave 5 SKIP list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the REPO-05 Noop/Mock sweep — the ClientEncoder seam lives in Encoding, outside the crypto agent's ownership, so it was deferred. Adds NoopClientEncoder (fail-safe: encodes to empty, decode throws the new EncoderError.codecDisabled) and a recording MockClientEncoder (JSON-backed round-trip + call recording + throwing gate closures). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… render, id shape, concurrency Adds a mock Span/Logger/Tracer trio (MockSpan/MockLogger/MockTracer + LogSink) in the test target and exercises the production LiveObserver/LiveOperation path that the existing RecordingObserver-based suite left unobserved: - LiveOperation routing: set/setValues dual-write to BOTH span and logger; spanOnly/logOnly route to one pillar only; acknowledge(error)/error() hit both; acknowledge(nil) logs info only; logger seeded with span/trace ids; begin() manual end. - OSLogLogger render format (OBS-03): renderPublic/renderFields exact shape, keys public / values private, sorted, name & field segments. - Span end-idempotence: double end on SignpostSpan/NoopSpan is safe. - IDGen format (OBS-20): trace id 32 hex, span id 16 hex, non-all-zero, child linkage. - Concurrency: task-local parenting across withTaskGroup and async let; siblings don't cross-link; root siblings get independent traces; concurrent op.set from many child tasks drops no keys on either pillar (OBS-01 regression on the live path). - Config partial/empty decode regression (OBS-04). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ve 4) Test-only. Fills the networking test debt REPO-09 called out: - Deterministic half-open re-trip (NET-01) via the injected clock with resetTimeout > window, so the aged-out-window path is exercised without real sleeping; confirms NET-01's fix holds under production timings. - RollingWindow unit tests: sample aging/eviction at window boundaries, ring-slot reuse, reset, and single-bucket edge. - ExponentialBackoffPolicy delay schedule: geometric progression, maxDelay clamp, and jitter [delay/2, delay) bounds (prior tests only counted attempts). - Mid-stream SSE transport error surfaces to the consumer (hermetic throwing byte source). - NET-04 consumer-abandonment: a cancellation-aware WebSocketConnection double proves the underlying receive() read is reaped, not just the consumer. - NET-26: an unclassified transport fault is counted with error=transport. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture bytes produced ONCE by platform-go's runtime deps and pin them as checked-in fixtures with regression tests that decode/verify them in Swift, extending the established JWT Go-token fixture pattern. Test-only; no Sources. - AES-256-GCM: a nonce‖ct‖tag ciphertext from Go's crypto/cipher GCM (go1.26.4) decrypts back to the original plaintext via AESGCMEncryptorDecryptor. - securecookie: a signed value from gorilla/securecookie v1.1.2 (JSON serializer, no block key — the subset the Swift port reproduces) verifies and decodes via CookieManager, honoring the 30-day default decode bound (UTIL-01). - xid: three rs/xid v1.6.0 ids (two reused from the JWT token's jti/sub, one freshly minted) re-encode byte-for-byte from their Go raw bytes and validate. - RFC3339Nano: eight variable-precision timestamps from Go's time.RFC3339Nano all parse via Filtering's RFC3339; sub-millisecond digits truncate to the ms per Foundation's documented ISO8601DateFormatter limit (pinned, not silent). Each fixture documents the exact Go package + version + program that produced it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ion (REPO-03) Ran `make format` across Sources/Tests against the pinned .swift-format config (79 files), relocated 3 over-length end-of-line comments the formatter can't move, and fixed a latent flaky test: MockCookieManaging's encode assertion compared raw JSONEncoder bytes from two independent encodes, which is non-deterministic without `.sortedKeys`. Now asserts the stored payload decodes back to the value. `make lint` is clean; 895 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…low-ups Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports from platform-go, all following the settled seam + lenient-Codable Config + Noop + Mock + Observer-threading pattern; thin/native/no-SPM-dep: - Secrets (PORT-01): Keychain-backed SecretSource + env/plist debug source - Cache (PORT-02): Cache<T>/BatchCache<T>, actor memory (TTL+LRU) + disk; Redis dropped - RateLimiting (PORT-03): clock-injectable per-key token-bucket actor - Files (PORT-04): AsyncSequence line/chunk/windowed readers, Decode<T>, sandbox Dir - Fake (PORT-05): corpus-based fixture generators + seeded preview data - Embeddings (PORT-06): Embedder seam; on-device NLEmbedding + OpenAI HTTP backend - Uploads (PORT-07): FileManager + presigned-URL background-upload seam; S3 dropped - HealthCheck (PORT-09): Checker + registry actor w/ per-check timeouts - Panicking (PORT-10): injectable fatalError/assertionFailure seam Built by a parallel worktree fleet; integrated against main HEAD with drift fixes (Counter->MetricCounter, import CircuitBreaking) since worktrees forked from a pre-Wave-1 base. swift build + 1132 tests green (was 895); make lint clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…search seams Port platform-go's search/text and search/vector protocol families. Text search via native SQLite FTS5 (SQLiteTextSearcher); vector search via in-memory brute-force cosine/dot/euclidean (InMemoryVectorIndex). Ships Noop + actor Mock conformers for each seam, lenient-Codable dispatch configs, and an Embedder-backed text->vector convenience path. Drops the Elasticsearch/Algolia (text) and pgvector/Qdrant (vector) remote backends, keeping the seams for a future adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update README status table (+10 rows, 35 libraries), PORT_PROGRESS.md tiers (Fake/Panicking pure-logic; Secrets/Cache/RateLimiting/Files/Embeddings/Uploads/ Search/HealthCheck native-backend), and TODO.md (check off PORT-01..10, add completion banner + residual follow-ups). Only Database/TestSupport remain deferred. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.