Commit Graph
5 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 d78f632cc3 fix(logging): open daily log with O_CLOEXEC + O_APPEND at creation
Replace `FileHandle(forWritingTo:)` with `Darwin.open(O_WRONLY | O_APPEND
| O_CREAT | O_CLOEXEC, 0o644)` wrapped in `FileHandle(fileDescriptor:
closeOnDealloc:)`. The previous code path opened the FD without
O_CLOEXEC, so JarvisMCP/ChildSpawnGate fatal-errored in Debug whenever
the runtime tried to spawn an MCP helper — the long-lived log FD leaks
across fork+exec by default on macOS.

`O_APPEND` makes every write atomically positioned at end-of-file, which
is more crash-resilient than the prior "seekToEnd then write" pair (a
concurrent rotator/write race could not truncate the file but could
interleave bytes). `O_CREAT` collapses the prior fileExists+createFile
roundtrip into a single syscall.

Together with the path-aware ChildSpawnGate change, this clears the
last hard-block on the native-launch path: AppDelegate now reaches
`MCPRuntime built — 3 tools` and the helper bundles spawn cleanly.

17/17 JarvisLoggingTests still pass (rotation, retention, redaction).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 15:45:20 -05:00
james.maes 5bd90762a4 fix(01-WR-08): detect clock-skew rotation + on-demand retention GC
Phase 1 REVIEW.md WR-08: `FileRotatingWriter` relied solely on wall-
clock `Date()` for day-boundary rotation and ran retention GC only as
a side effect of rotation. Two failure modes:
  1. Clock jumping ±N days (NTP correction, manual change) would
     rotate without signal — operators would see log files that
     interleave pre/post-adjustment timestamps with no warning.
  2. An idle app (no log write crossing midnight for >7 days) would
     never GC stale files.

packages/Logging/Sources/JarvisLogging/FileRotatingWriter.swift
- `rotateIfNeeded()` computes the day-delta between the previous
  window and the new day. When `|delta| > 2` emit an `.notice`-level
  fault via os.Logger naming the skew magnitude. Rotation still
  happens normally — the log just flags the unusual trigger.
- New `runRetentionGC()` dispatches `gcOldFiles` on the serial
  queue, independent of rotation. Callers can invoke at shutdown or
  on a periodic heartbeat.

packages/Logging/Sources/JarvisLogging/FileLogHandler.swift
- Public `runRetentionGC()` forwards to the writer so App-layer
  callers can trigger GC without reaching into internals.

All 14 logging tests still pass.
2026-04-23 16:26:26 -05:00
james.maes 813c690555 fix(01-WR-01): count + report dropped log lines on FileRotatingWriter failures
Phase 1 REVIEW.md WR-01 (OBS-06): `FileRotatingWriter` used `try?` on
both `FileHandle(forWritingTo:)` and `handle.write(contentsOf:)`, so a
disk-full, permission-revoked, or FD-evicted write silently dropped the
line with no signal. OBS-06 reads as "rotation must not lose data".

packages/Logging/Sources/JarvisLogging/FileRotatingWriter.swift
- Add `droppedLinesThisWindow` counter (serial-queue-local, no lock).
- Add `warnedAboutOpenFailureThisWindow` flag to emit a one-time
  os.Logger fault per rotation window (prevents spam if the failure
  persists for hours).
- `append()` now explicitly increments the counter on both the
  nil-handle path and the `handle.write` throw path, then logs a
  fault via `os.Logger` (subsystem `com.koftwentytwo.jarvis`,
  category `logging.file`) so failures are visible in Console.app
  even when the file log itself is the thing that's broken.
- `rotateIfNeeded()` emits an `.error`-level summary when the
  just-closed window lost lines, then resets both flags for the new
  window.
- Add `droppedLinesSnapshot()` test-only accessor that reads the
  counter through the serial queue (no TSan race).

packages/Logging/Tests/JarvisLoggingTests/FileLogHandlerTests.swift
- New `test_dropsLinesWhenHandleUnavailable`: points the writer at a
  path that is a regular file (so directory creation and file-open
  both fail), appends three lines, asserts `droppedLinesSnapshot() == 3`.
2026-04-23 16:14:56 -05:00
james.maes 206b4aa13f refactor: rename bundle ID com.kingsrook.jarvis → com.koftwentytwo.jarvis
Owner change: project will be owned by KofTwentyTwo. Bundle ID updates
propagate to three ${PRODUCT_BUNDLE_IDENTIFIER} targets, the Keychain
service name (SEC-01 / D-10), the OSLog subsystem identifier, and the
FileRotatingWriter DispatchQueue label. pbxproj regenerated via
xcodegen generate.

Forward-looking planning docs (STATE, REQUIREMENTS, PATTERNS, VALIDATION)
updated to match. Historical records (completed PLAN.md / SUMMARY.md /
RESEARCH.md / audits / CONTEXT / DISCUSSION-LOG) deliberately left as-is
to preserve the audit trail of the original naming decision.

Verified: 49 SPM tests pass (4 Keychain + 15 Config + 13 Logging + 17
Shell), xcodebuild Debug build succeeds with full codesign pipeline.

Decision logged in STATE.md Key Decisions (Locked 2026-04-22).
2026-04-22 17:56:37 -05:00
james.maesandClaude Opus 4.7 23917e58f2 feat(01-02): build Logging package (OBS-06, D-17, D-18, D-20)
- JarvisLogChannel: four cases (agent, tools, ui, system)
- JarvisLogHandlerFactory.bootstrap wires MultiplexLogHandler that
  fans out to FileLogHandler + OSLogHandler (D-17)
- FileLogHandler applies Redact.apply once (S-4 single call site)
- OSLogHandler does NOT redact (os.Logger handles %{private}@ at
  system layer; callers redact untrusted input before logging)
- Redact covers exactly 5 patterns: sk-ant-, Authorization:Bearer,
  sk- (OpenAI), AKIA, ghp_ (D-20, OBS-06); Anthropic branch ordered
  before OpenAI so sk-ant- consumes the full prefix
- FileRotatingWriter: lazy midnight rotation via TestDateProvider-
  injectable DateProvider; serial DispatchQueue; retain-7 GC on
  rotate (D-18)
- LogPaths.channelDirectory = ~/Library/Logs/Jarvis/
- Swift 6 strict: ISO8601DateFormatter.jarvisShared uses
  nonisolated(unsafe) since the formatter is documented thread-safe
  after configuration but not Sendable-annotated by Apple
- OSLogHandler uses fully-qualified Logging.Logger to disambiguate
  from os.Logger (both imported)
- Tests: 13/13 pass (7 Redact + 2 FileLogHandler rotate/retain +
  4 bootstrap/factory/channels)
- Deletes Plan 01 placeholders

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:27:34 -05:00