Skip to content

feat(obs,loom): Phase 2 — obs.MeterFor() helper + loom OTel wiring#168

Merged
thebtf merged 3 commits into
mainfrom
feat/loom-meter
Apr 15, 2026
Merged

feat(obs,loom): Phase 2 — obs.MeterFor() helper + loom OTel wiring#168
thebtf merged 3 commits into
mainfrom
feat/loom-meter

Conversation

@thebtf
Copy link
Copy Markdown
Owner

@thebtf thebtf commented Apr 15, 2026

Summary

Wires loom's internal OTel instruments through the engram main meter pipeline. Implements spec FR-11 with the spec C1 Option B resolution: extend internal/module/obs/ with a MeterFor(moduleName) metric.Meter helper rather than adding a ModuleDeps.Meter framework field.

What ships

  • internal/module/obs/meter.go (21 LOC) — new MeterFor(moduleName string) metric.Meter helper wrapping otel.GetMeterProvider().Meter("github.com/thebtf/engram/" + moduleName). Single-point accessor; respects the framework rule "no code outside internal/module/obs/ may call otel.GetMeterProvider() directly".
  • internal/module/obs/meter_test.go (55 LOC) — 3 parallel unit tests:
    • TestMeterFor_NonNil — non-nil meter for any valid name
    • TestMeterFor_SameNameSameScope — two calls with same name return usable factories
    • TestMeterFor_EmptyName — empty name does not panic
  • internal/handlers/loom/module.go (+6/-1)Init now passes loom.WithMeter(obs.MeterFor("loom")) between WithLogger and WithMaxRetries to loom.NewEngine(db, opts...). Removes the earlier explanatory comment about NoopMeter fallback (no longer applicable).

Why this approach

Spec C1 clarification chose Option B (obs.MeterFor() helper) over Option A (new ModuleDeps.Meter field):

  • Smaller blast radius — one new file in the obs package, one-line wiring update in loom, no Phase A framework spec amendment
  • No test mock churn in internal/moduletest/mocks.go
  • Honours the existing framework rule (single obs gateway) — MeterFor is the new seam
  • Zero runtime overhead — otel.GetMeterProvider() is cached by the OTel library

Effect on loom metrics

With this PR merged, loom's 8 internal OTel instruments emit through the same meter provider that serves engram_handletool_duration_ms and friends:

  • loom.tasks.submitted (counter)
  • loom.tasks.completed (counter)
  • loom.tasks.failed (counter)
  • loom.tasks.cancelled (counter)
  • loom.gate.pass (counter)
  • loom.gate.fail (counter)
  • loom.submit.duration_ms (histogram)
  • loom.task.duration_ms (histogram)

All 8 carry worker_type and project_id attributes from loom v0.1.0. Operators with OTEL_EXPORTER_OTLP_ENDPOINT configured will see them alongside the framework metrics; operators without an exporter pay zero cost (noop provider).

Verification

  • go build ./... — clean
  • go vet ./... — clean
  • go test ./internal/module/obs/... -count=1 — 3/3 new tests PASS
  • go test ./internal/handlers/loom/... -count=1 — 8/8 existing loom tests PASS (no regression)

Anti-stub check: replacing MeterFor body with return nil would fail TestMeterFor_NonNil. Verified by reading the test and tracing the assertion.

What does NOT change

  • engramcore tenant — untouched
  • Existing obs package API (RecordHandleTool etc.) — unchanged
  • ModuleDeps struct — no new field (that was Option A, not taken)
  • Phase A spec — no amendment needed (additive helper, not framework change)
  • Plugin wire format — N/A (daemon-internal change)

Related

  • Spec: .agent/specs/loom-integration/spec.md FR-11 + C1 clarification (gitignored)
  • Plan: .agent/specs/loom-integration/plan.md §Phase 2 (gitignored)
  • Tasks: .agent/specs/loom-integration/tasks.md T010 + T011 + T012 (gitignored)
  • Previous PR: feat(loom): Phase B-1 plumbing tenant — loom v0.1.0 integration #167 (plumbing landing merged as f011e22)
  • Next in train: PR loom-tools (4 MCP tools), PR engram-server-proto-b0, PR loom-serverevents, v4.4.0 tag

Summary by CodeRabbit

Примечания к выпуску

  • Улучшения

    • Расширена конфигурация механизма Loom: добавлена настройка метрик и ограничение на количество повторных попыток для повышения надёжности и наблюдаемости
    • Добавлена общая точка доступа для создания счётчиков метрик по модулю
  • Тесты

    • Добавлены автоматизированные тесты для новой логики метрик, включая проверку поведения для пустого имени модуля

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5abbc534-2e08-4fd0-9cd0-8365aca337c2

📥 Commits

Reviewing files that changed from the base of the PR and between 536845a and 098a13c.

📒 Files selected for processing (1)
  • internal/handlers/loom/module.go

Walkthrough

Добавлен пакет internal/module/obs с функцией MeterFor(moduleName) для получения OpenTelemetry-метров; инициализация loom-движка обновлена, чтобы передавать метр и устанавливать WithMaxRetries(2). Добавлены тесты для проверки поведения MeterFor.

Changes

Cohort / File(s) Summary
Observability Meter Package
internal/module/obs/meter.go, internal/module/obs/meter_test.go
Добавлен MeterFor(moduleName string) metric.Meter (возвращает meter от otel.GetMeterProvider().Meter("github.com/thebtf/engram/"+moduleName)). Добавлены три параллельных теста: ненулевой meter, повторный вызов с тем же именем, и вызов с пустым именем (не паникует).
Loom Engine Configuration
internal/handlers/loom/module.go
Инициализация loom.NewEngine расширена: добавлены опции loom.WithMeter(obs.MeterFor(moduleName)) и loom.WithMaxRetries(2); добавлен импорт internal/module/obs. Нет изменений в логике Init помимо передачи новых опций.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Маленький кролик рад, подпрыгнув в траве,
Метры принес я в код, тихонько в руке.
Лимит повторов вручил, наблюденье дал,
Пусть сбор метрик в проекте теперь закружит бал. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing obs.MeterFor() helper and integrating loom with OpenTelemetry.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/loom-meter

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Comment @coderabbitai help to get the list of available commands and usage tips.

@thebtf
Copy link
Copy Markdown
Owner Author

thebtf commented Apr 15, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a centralized observability utility for OpenTelemetry meters and integrates it into the loom module. Specifically, it adds the obs package with a MeterFor function to standardize instrumentation scope naming and updates the loom engine initialization to include metrics and a retry policy. Feedback suggests avoiding hardcoded strings by using module methods or shared constants and replacing magic numbers with named constants for better maintainability.

Comment thread internal/module/obs/meter.go
Comment thread internal/handlers/loom/module.go Outdated
Comment thread internal/handlers/loom/module.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/handlers/loom/module.go (1)

121-121: Используйте moduleName вместо строкового литерала на Line 121.

Сейчас "loom" дублирует уже существующую константу moduleName; лучше убрать риск расхождения при будущем переименовании модуля.

Предлагаемое изменение
-			loom.WithMeter(obs.MeterFor("loom")),
+			loom.WithMeter(obs.MeterFor(moduleName)),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/handlers/loom/module.go` at line 121, Replace the hard-coded string
"loom" passed to obs.MeterFor with the existing moduleName constant to avoid
duplication; specifically update the call used with
loom.WithMeter(obs.MeterFor("loom")) to
loom.WithMeter(obs.MeterFor(moduleName)), ensuring moduleName is in scope where
this call occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/handlers/loom/module.go`:
- Line 121: Replace the hard-coded string "loom" passed to obs.MeterFor with the
existing moduleName constant to avoid duplication; specifically update the call
used with loom.WithMeter(obs.MeterFor("loom")) to
loom.WithMeter(obs.MeterFor(moduleName)), ensuring moduleName is in scope where
this call occurs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1717f083-3799-4dff-bb64-f48f7bc6ec6a

📥 Commits

Reviewing files that changed from the base of the PR and between f011e22 and 536845a.

📒 Files selected for processing (3)
  • internal/handlers/loom/module.go
  • internal/module/obs/meter.go
  • internal/module/obs/meter_test.go

@thebtf
Copy link
Copy Markdown
Owner Author

thebtf commented Apr 15, 2026

🤖 PR Review MCP State (auto-managed, do not edit)
{
  "version": 2,
  "parentChildren": {},
  "resolvedNitpicks": {
    "coderabbit-nitpick-1803fc13-121": {
      "resolvedAt": "2026-04-15T15:47:15.943Z",
      "resolvedBy": "agent"
    }
  },
  "updatedAt": "2026-04-15T15:47:16.489Z"
}

Addresses CodeRabbit nitpick on PR #168 — replace hardcoded string literal with the existing moduleName package constant to avoid divergence if the module is ever renamed.
@thebtf thebtf merged commit b3dc577 into main Apr 15, 2026
8 checks passed
@thebtf thebtf deleted the feat/loom-meter branch April 15, 2026 19:28
thebtf added a commit that referenced this pull request Apr 15, 2026
Phase 6 release ceremony for the v4.4.0 train. Bumps the unified
engram-server + plugin version after the 5-PR train completed:

  #167 — Phase B-1 plumbing tenant (loom integration)
  #168 — Phase 2 obs.MeterFor helper + loom OTel wiring
  #169 — Phase 3 4 loom_* tools + CLI worker with allowlist
  #170 — Phase 4 server-side gRPC proto extensions + soft-delete reaper
  #171 — Phase 5 daemon serverevents bridge for real OnProjectRemoved e2e

Per Constitution §15 the daemon version and plugin version must move
together, so this commit bumps:

  - cmd/engram/main.go daemonVersion "v4.3.0" → "v4.4.0"
    Reported to gRPC Initialize + logged in structured output.
  - plugin/engram/.claude-plugin/plugin.json version "4.3.0" → "4.4.0"
    Drives Claude Code plugin cache invalidation on /reload-plugins.
    Without this bump the marketplace update would not be detected.

No behaviour change, no test change. Release notes + git tag + gh
release + marketplace sync land in subsequent steps.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant