From 0b834214cdf894e1f96da289088ac161f7634b21 Mon Sep 17 00:00:00 2001 From: AI Bot Date: Wed, 15 Apr 2026 21:10:33 +0200 Subject: [PATCH] Add OpenSpec change for operation logging --- .../add-operation-logging/.openspec.yaml | 2 + .../changes/add-operation-logging/design.md | 66 +++++++++++++++++++ .../changes/add-operation-logging/proposal.md | 26 ++++++++ .../changes/add-operation-logging/tasks.md | 45 +++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 openspec/changes/add-operation-logging/.openspec.yaml create mode 100644 openspec/changes/add-operation-logging/design.md create mode 100644 openspec/changes/add-operation-logging/proposal.md create mode 100644 openspec/changes/add-operation-logging/tasks.md diff --git a/openspec/changes/add-operation-logging/.openspec.yaml b/openspec/changes/add-operation-logging/.openspec.yaml new file mode 100644 index 0000000..859e7bb --- /dev/null +++ b/openspec/changes/add-operation-logging/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-15 diff --git a/openspec/changes/add-operation-logging/design.md b/openspec/changes/add-operation-logging/design.md new file mode 100644 index 0000000..98801a8 --- /dev/null +++ b/openspec/changes/add-operation-logging/design.md @@ -0,0 +1,66 @@ +## Implementation + +### Log File Location +- Path: `/var/log/declpac.log` +- Single merged log file (stdout + stderr intermingled in order of arrival) + +### State-Modifying Functions (need logging) +1. `SyncPackages()` - `pacman -S --needed ` +2. `InstallAUR()` - `git clone` + `makepkg -si --noconfirm` +3. `MarkAllAsDeps()` - `pacman -D --asdeps` +4. `MarkAsExplicit()` - `pacman -D --asexplicit ` +5. `CleanupOrphans()` - `pacman -Rns` + +### Functions to Skip (read-only) +- `DryRun()` - queries only +- `getInstalledCount()` - pacman -Qq + +### Execution Patterns + +| Pattern | Functions | How | +|---------|------------|-----| +| Streaming | MarkAllAsDeps, MarkAsExplicit, InstallAUR | `io.MultiWriter` to tee to both terminal and log | +| Captured | SyncPackages, CleanupOrphans | capture with `CombinedOutput()`, write to log, write to terminal | + +### Error Handling +- Write error to log BEFORE returning from function +- Print error to stderr so user sees it + +### Dependencies +- Add to imports: `io`, `os`, `path/filepath` + +### Structure + +```go +// pkg/state/state.go +var logFile *os.File + +func OpenLog() error { + logPath := filepath.Join("/var/log", "declpac.log") + f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + logFile = f + return nil +} + +func GetLogWriter() io.Writer { + return logFile +} + +func Close() error { + if logFile == nil { + return nil + } + return logFile.Close() +} +``` + +### Flow in Sync() or main entrypoint +1. Call `OpenLog()` at program start, defer close +2. Each state-modifying function uses `state.GetLogWriter()` via MultiWriter + +### Wire into main.go +- Open log at start of `run()` +- Pass log writer to pacman package (via exported function or global) diff --git a/openspec/changes/add-operation-logging/proposal.md b/openspec/changes/add-operation-logging/proposal.md new file mode 100644 index 0000000..c1cd23b --- /dev/null +++ b/openspec/changes/add-operation-logging/proposal.md @@ -0,0 +1,26 @@ +## Why + +The tool exits without saving the full pacman output, making debugging difficult +when operations fail. Users need a persistent log of all pacman operations for +debugging and audit. + +## What Changes + +- Add state directory initialization creating `~/.local/state/declpac` if not exists +- Open/manage a single log file at `$XDG_STATE_HOME/declpac` (e.g., `~/.local/state/declpac/declpac`) +- Instrument all state-modifying exec calls in `pkg/pacman/pacman.go` to tee or append output to this file +- Skip debug messages (internal timing logs) +- Capture and write errors before returning + +## Capabilities + +### New Capabilities +- Operation logging: Persist stdout/stderr from all pacman operations + +### Modified Capabilities +- None + +## Impact + +- `pkg/pacman/pacman.go`: Instrument all state-modifying functions to write to log file +- New module: May create `pkg/state/state.go` or similar for log file management diff --git a/openspec/changes/add-operation-logging/tasks.md b/openspec/changes/add-operation-logging/tasks.md new file mode 100644 index 0000000..ed98aad --- /dev/null +++ b/openspec/changes/add-operation-logging/tasks.md @@ -0,0 +1,45 @@ +## Tasks + +- [x] 1. Create state module + +Create `pkg/state/state.go`: +- `OpenLog()` - opens `/var/log/declpac.log` in append mode +- `GetLogWriter()` - returns the raw log file writer (for MultiWriter) +- `Write(msg []byte)` - writes message with timestamp + dashes separator +- `Close()` - closes the file + +- [x] 2. Wire into main.go + +In `cmd/declpac/main.go` `run()`: +- Call `OpenLog()` at start +- `defer Close()` log + +- [x] 3. Instrument pkg/pacman + +Modify `pkg/pacman/pacman.go`: + +All state-modifying functions use `state.Write()` instead of `state.GetLogWriter().Write()`: + +``` +// OLD +state.GetLogWriter().Write(output) + +// NEW +state.Write(output) // auto-prepends timestamp +``` + +**Functions updated:** +- `SyncPackages()` - write output with timestamp +- `CleanupOrphans()` - write output with timestamp +- `MarkAllAsDeps()` - write operation name with timestamp before running +- `MarkAsExplicit()` - write operation name with timestamp before running +- `InstallAUR()` - write "Cloning ..." and "Building package..." with timestamps +- Error handling - `state.Write([]byte("error: ..."))` for all error paths + +- [x] 4. Add io import + +Add `io` to imports in pacman.go + +- [x] 5. Test + +Run a sync operation and verify log file created at `/var/log/declpac.log`