Repository navigation
ADR-0001: Re-enable LLM-Dev-Ops ecosystem dependencies - #1
nicholas-ruest merged 6 commits into
Conversation
ADR-0001 decided to re-enable the five LLM-Dev-Ops ecosystem dependencies as tag-pinned git dependencies behind a working `ecosystem` feature. Its Implementation Plan step 1 makes upstream tags a blocking precondition. Verified against the real remotes: step 1 is unmet for all five, so no dependency is re-enabled here. - observatory, governance-dashboard, analytics-hub, policy-engine: 0 tags, 0 releases. Nothing immutable to pin. - sentinel: has tag v0.1.0, but it does not build. Pinned and compiled to confirm rather than assumed: `sentinel-core` fails with E0599 (`validate_length` not found for `ServiceId`) because events.rs applies `#[validate(length(...))]` to newtypes that don't implement validator 0.18's `ValidateLength`; separately the workspace won't resolve standalone because `sentinel-api` requires `axum-test = "^16.5"`, which no longer exists on crates.io. The crate is also named `sentinel-core` at that tag, predating the rename to `llm-sentinel-core`. Replaces the vague "temporarily disabled due to upstream dependency issues" note with per-dependency status and evidence, and corrects the false claim that stub implementations are used when the feature is off — no `cfg(feature = "ecosystem")` gate has ever existed in src/. `ecosystem = []` remains a deliberate no-op; it cannot name a dependency until one is pinnable. ADR-0001 steps 3-8 stay blocked on upstream. The CI feature matrix and tag-guard job that ADR-0001 step 7 asks for are NOT included: pushing .github/workflows/ requires a token with `workflow` scope, which this credential lacks. The workflow content is in the PR description for someone with that scope to add. Implements incident-manager/docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md
ADR-0001 Implementation Plan step 7 / Verification check 9. The `ecosystem` feature was never compiled in CI, which is how it decayed into a no-op without anyone noticing. Two jobs: - `build`: a default and an `ecosystem` configuration on Rust 1.84, the version `async-lock = "=3.3.0"` and `base64ct = "=1.6.0"` exist to hold. While `ecosystem = []` these compile an identical graph, so this proves only that the feature name survives; it becomes a real second configuration the moment a `dep:` entry lands. - `pinning`: fails the build if any git dependency lacks a `tag`, or if one is pinned to a branch. This is the load-bearing job today — it makes the floating-ref regression of ADR-0001 Finding 3 unmergeable rather than merely discouraged. The guard strips comment lines before matching. The manifest documents the forbidden `branch` and bare-git forms in prose, and without stripping, the guard trips over its own documentation — verified it does, then verified the fixed version still fails on a real branch ref and a real untagged git dep. `--locked` is omitted throughout because Cargo.lock is gitignored (.gitignore:5). Add it once a lockfile is committed; a tag pin is only reproducible if the rest of the graph is locked too. Follow-up to c232dcd, which could not include this file: the credential then in use lacked `workflow` scope. Implements incident-manager/docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md
The first CI run failed both build jobs in ~30s, before compiling anything. Cause is a real defect this workflow just exposed, not a workflow bug. Cargo.lock is gitignored (.gitignore:5), so a clean build resolves every transitive dependency to its newest release. Several now exceed the declared `rust-version = "1.84"`: chacha20 0.10.1 requires edition2024, unsupported by cargo 1.84.1 uuid 1.24.0 requires Rust 1.85.0 zeroize 1.9.0 requires Rust 1.85 wasip2/wit-bindgen require Rust 1.85 / 1.87 So `async-lock = "=3.3.0"` and `base64ct = "=1.6.0"` — pinned, per their comments, solely to hold the 1.84 line — do not hold it. Two exact pins cannot constrain a graph that is otherwise free to float. ADR-0001's "Toolchain constraint" treats 1.84 as an acceptance criterion for admitting upstream crates; it is already violated without any upstream crate admitted. The build matrix therefore runs on `stable`, which is what actually works today, and a separate non-blocking `msrv` job keeps the 1.84 failure visible with the evidence inline. Silently switching everything to stable would have made the symptom disappear while leaving `rust-version` a false claim. Fix is to commit a lockfile — then add `--locked` and make `msrv` blocking — or to raise `rust-version` to the truth. Both are decisions for the maintainers. Implements incident-manager/docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md
Two fixes from the first real CI run. 1. rdkafka-sys failed both build jobs with `fatal error: curl/curl.h: No such file or directory`. librdkafka is built from source and needs libcurl headers; I had installed libcurl4-openssl-dev locally when hitting this, but left it out of the workflow's apt list. 2. `cargo test --workspace` is now advisory (continue-on-error) and capped at 20 minutes. It compiles, prints `running 478 tests`, then hangs indefinitely — a test opens sockets and blocks forever waiting on Redis/NATS/Kafka, which no runner provides. Observed locally at ~30 min, 0% CPU, blocked on futex. Uncapped, this burns the full 6h job timeout on every push. This hang is pre-existing and unrelated to ADR-0001; the diff that introduced this workflow is comments only. Making it blocking now would gate every PR on a defect this branch did not cause. It should become blocking once those tests are gated behind a feature or given service containers. `cargo build` stays blocking in both configurations, which is the signal ADR-0001 step 7 actually asks for. Implements incident-manager/docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md
The first successful run of this workflow produced hard numbers, so replace the
guess with them. `cargo test --workspace` is broken on main in two independent
ways, both pre-existing — the commit that introduced this workflow changes only
comments:
- 29 tests FAIL, concentrated in metrics::collectors, metrics::decorators and
metrics::interceptors. The clustering points at a shared global metrics
registry mutated concurrently: they pass alone, fail in parallel.
- The suite then HANGS on a test blocking on Redis/NATS/Kafka sockets. The
step hit its 20-minute cap, confirming in CI what was observed locally at
~30 min and 0% CPU.
I had documented only the hang. "Pre-existing hang" reads as one flaky test;
29 failures plus a hang is a different problem, and understating it would let
someone flip this step to blocking expecting a quick fix.
Build stays blocking and passes in both configurations (23m50s / 23m36s).
Implements incident-manager/docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md
CI is now live on this PR — first real run resultsThe
Getting there surfaced three real defects that were invisible before, none introduced by this branch (its code diff is comments only). 1. The declared
|
Update sentinel dependency status: v0.1.1 fixes the naming mismatch
(llm-sentinel-core now exists at a tag) but build now succeeds too, so the
dependency is pinned for real rather than left disabled.
v0.1.0 was rejected in this PR for two independent defects. Both are fixed at
v0.1.1, and both fixes were re-verified by building the tag standalone rather
than trusting the tag message:
1. Naming — at v0.1.0 the crate was `sentinel-core`, which would have needed a
`package = "sentinel-core"` alias. At v0.1.1 crates/sentinel-core/Cargo.toml
declares `name = "llm-sentinel-core"`, so no alias is required.
2. Build — `sentinel-api` moved `axum-test` from the yanked `^16.5` to `18.2`,
so the workspace resolves standalone; and events.rs no longer applies
`#[validate(length(...))]` to the `ServiceId` newtype, so the E0599
"no method named `validate_length`" failure against validator 0.18 is gone.
Real command output:
clean clone of tag v0.1.1 (remote tag -> 504b0ea), cargo build --workspace
Finished `dev` profile [unoptimized + debuginfo] target(s) in 14m 52s
EXIT=0 (warnings only; zero errors)
incident-manager, cargo build --workspace --features ecosystem
Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 32s
EXIT=0
incident-manager, cargo build --workspace
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 03s
EXIT=0
cargo tree | grep -c 'llm-sentinel-core|llm-observatory|llm-governance|llm-analytics|policy-engine'
-> 0 (ADR check 3: default graph unchanged)
cargo tree --features ecosystem | grep llm-sentinel-core
-> llm-sentinel-core v0.1.0 (https://github.com/LLM-Dev-Ops/sentinel?tag=v0.1.1#504b0ea4)
This makes `ecosystem` a real second configuration for the first time: ADR-0001
Finding 1 (the feature named nothing) is now resolved for one of the five
members. The other four still have 0 tags and stay disabled.
Caveats recorded in the manifest rather than glossed over:
- The standalone build ran on stable 1.97.1, the only toolchain installed
here. ADR-0001 step 1 asks for Rust 1.84, which is unavailable in this
environment, so sentinel's MSRV compliance is asserted-but-unverified.
- sentinel did not bump workspace.package.version when cutting v0.1.1, so the
crate still reports 0.1.0. The tag is what pins us.
- `from_sentinel` is still commented out. Restoring it needs the conversion
written against v0.1.1's real AnomalyEvent shape and gated on `ecosystem`;
it was left commented rather than guessed at.
Update — sentinel is unblocked at
|
| Defect at v0.1.0 | Status at v0.1.1 |
|---|---|
Crate named sentinel-core, needing a package = "sentinel-core" alias |
Fixed — crates/sentinel-core/Cargo.toml declares name = "llm-sentinel-core", no alias needed |
sentinel-api required axum-test = "^16.5" (yanked; workspace wouldn't even resolve) |
Fixed — now axum-test = "18.2"; workspace resolves |
sentinel-core E0599 no method named validate_length found for struct ServiceId against validator 0.18 |
Fixed — events.rs no longer applies #[validate(length(...))] to the ServiceId newtype |
Note v0.1.1 is not merely a re-tag: git diff --stat v0.1.0 v0.1.1 is 211 files / +49416 lines.
Real build output
Clean clone of tag v0.1.1 (remote tag → commit 504b0ea), built standalone:
cargo build --workspace
Finished `dev` profile [unoptimized + debuginfo] target(s) in 14m 52s
EXIT=0 (warnings only; zero errors)
In this repo, after pinning:
cargo build --workspace --features ecosystem
Finished `dev` profile [unoptimized + debuginfo] target(s) in 7m 32s EXIT=0
cargo build --workspace
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 03s EXIT=0
cargo tree | grep -c 'llm-sentinel-core|llm-observatory|llm-governance|llm-analytics|policy-engine'
-> 0 (ADR check 3: default graph still unchanged)
cargo tree --features ecosystem | grep llm-sentinel-core
-> llm-sentinel-core v0.1.0 (https://github.com/LLM-Dev-Ops/sentinel?tag=v0.1.1#504b0ea4)
What this PR now does differently
llm-sentinel-core = { git = "https://github.com/LLM-Dev-Ops/sentinel", tag = "v0.1.1", optional = true }
ecosystem = ["dep:llm-sentinel-core"]ecosystem is no longer a no-op. ADR-0001 Finding 1 is resolved for one of the five members — --features ecosystem finally compiles a different crate graph than the default build. The pinning CI guard still passes (the new line carries an immutable tag, no branch =).
Revised scorecard deltas
| # | Check | Was | Now |
|---|---|---|---|
| 2 | every git = carries tag = |
PASS (vacuously) | PASS (for real — one git dep, tag-pinned) |
| 4 | --features ecosystem pulls upstream crates |
FAIL | PARTIAL — 1 of 4 runtime crates (sentinel) now pulled |
| 6 | cfg(feature = "ecosystem") gates in all three adapters |
FAIL | still FAIL — see caveat 3 |
Caveats — stated, not glossed over
- MSRV unverified for sentinel. The standalone build ran on stable 1.97.1, the only toolchain installed in my environment. ADR-0001 step 1 specifies Rust 1.84, which I could not install here. So sentinel's 1.84 compliance is asserted-but-unverified — the same gap the
msrvjob already tracks for this repo's own floor. CI on 1.84 is the real check. - Upstream version not bumped. sentinel left
workspace.package.versionat0.1.0when cuttingv0.1.1, socargo treeprintsllm-sentinel-core v0.1.0 (...?tag=v0.1.1#...). Cosmetic; the tag is what pins us. from_sentinelis still commented out. Restoring it needs the conversion written against v0.1.1's actualAnomalyEventshape and gated onecosystem. I left it commented rather than guessing at a shape I hadn't compiled — enabling the feature today pulls the crate in but changes no behaviour.
Still needing a human decision
cargo publishnow genuinely breaks on the next release. This was flagged as hypothetical in the original description; it is live as of this commit..github/workflows/publish-crate.ymlrunscargo publishon everyv*.*.*tag, andcargo publishrejects git dependencies even optional ones. Someone must either drop that workflow, or movellm-sentinel-coreto a registry, before the next tag on this repo.- The other four upstreams (observatory, governance-dashboard, analytics-hub, policy-engine) still have 0 tags and remain disabled. Unchanged.
Implements
docs/adr/ADR-0001-re-enabling-llm-dev-ops-ecosystem-dependencies.md(added in this PR — it was untracked).TL;DR — the ADR cannot be executed yet, and this PR says so precisely
ADR-0001 decided to re-enable the five ecosystem dependencies as tag-pinned git dependencies behind a working
ecosystemfeature. Its Implementation Plan step 1 (upstream tags) is a blocking precondition, and the ADR is explicit: "incident-manager does not merge step 1 until each upstream repo has cut a tag whosecargo buildsucceeds standalone on Rust 1.84."I checked all five against the real remotes. Step 1 is unmet for all five. So this PR re-enables nothing, and instead replaces the misleading manifest comment with per-dependency status and evidence.
Per-dependency status (verified 2026-07-27)
llm-observatory-corellm-governance-commonllm-analytics-hubpolicy-engine-benchmarksllm-sentinel-corev0.1.0, but it does not buildChecked with
gh api repos/LLM-Dev-Ops/<repo>/tagsandgit ls-remote --tags.sentinel v0.1.0 — pinned and compiled, not assumed
I actually added the pin and ran the build rather than taking the tag's existence as sufficient. Two independent defects:
sentinel-coredoes not compile against validator 0.18.events.rsapplies#[validate(length(min = 1, max = 255))]toservice_name: ServiceId, butServiceId/ModelIdare newtypes that don't implement validator 0.18'sValidateLength.The workspace doesn't even resolve standalone, so it fails ADR step 1's acceptance criterion outright:
16.x is gone from crates.io, and there is no
Cargo.lockat the tag to hold it.Bonus trap: at
v0.1.0the crate is namedsentinel-core, notllm-sentinel-core— the rename happened after the tag. ADR-0001's target-state linellm-sentinel-core = { git = ..., tag = "vX.Y.Z" }would fail to resolve as written; it needspackage = "sentinel-core"unless a new tag is cut post-rename.ADR-0001 step 2 forbids papering over any of this with
[patch], so it must be fixed upstream.What this PR actually changes
Cargo.toml— replaces "temporarily disabled due to upstream dependency issues" with per-dependency status, the concrete sentinel failures, and the rule that any re-enabled line must carry an immutabletag.[features]— deletes the false claim that "When disabled (default), stub implementations are used." There is no such switch;grep -rn 'feature = "ecosystem"' src/returns nothing (ADR Finding 2).ecosystem = []is documented as a known, deliberate no-op (Finding 1) that cannot name a dependency until one is pinnable.sentinel_adapter.rs,observatory_adapter.rs,analytics_adapter.rsblocker comments now name the specific missing tag / defect instead of "Uncomment when ecosystem dependencies are available".Deliberately no functional change: the default build is byte-for-byte the same dependency graph as before.
Build / test output (real)
Caveat, stated plainly: the full
cargo test --workspacedoes not complete in my environment. It compiles, startsrunning 478 tests, then hangs indefinitely on a test holding open sockets (~30 min, 0% CPU, blocked on futex) — evidently a test expecting Redis/NATS/Kafka to be reachable. This is pre-existing and unrelated to this PR (the diff is comments only), but I could not run the full suite to completion and am not claiming otherwise. I scoped the run tointegrations::upstream, the modules this PR touches.Also note:
cargo build --workspacefails on a clean checkout ofmainin a bare container until you installcmake,protobuf-compiler,libcurl4-openssl-dev,clang,libsasl2-dev,zlib1g-dev,libssl-dev— rdkafka builds librdkafka from source. Worth knowing for CI.ADR-0001 verification checklist — honest scorecard
grep -c '^# llm-' Cargo.toml== 0git =carriestag =, nobranch =--features ecosystempulls the four runtime cratesllm-*versionscfg(feature = "ecosystem")+cfg(not(...))in all three adapterstest_outlier_type_conversion--features benchmarksbuildsmainmoving doesn't change our graphI deliberately avoided writing
#[cfg(feature = "ecosystem")]verbatim inside the new comments, because check 6 is agrepand prose in a comment would have satisfied it spuriously.Things needing a human decision
Cargo.lockis gitignored (.gitignore:5). Every ADR-0001 verification step specifiescargo build --locked, which cannot work here. A tag pin only gives a reproducible build if the rest of the graph is locked too. The ADR didn't account for this..github/workflows/publish-crate.ymlrunscargo publishon everyv*.*.*tag.cargo publishrejects git dependencies even optional ones, so the moment any ecosystem dep is re-enabled, releases break. Someone must decide: drop publishing, or move these to a registry first.CI workflow — written but NOT pushed
ADR step 7 asks for a second CI configuration. I wrote
.github/workflows/build-features.yml(feature matrix on Rust 1.84 + a job failing the build if any git dep lacks atag, which would prevent Finding 3 recurring), but the push was rejected: this credential is a PAT withoutworkflowscope. I removed it from the commit rather than work around the restriction. Someone withworkflowscope should add it — I can supply the file, or reproduce it from this description.Note the tag-guard job is the load-bearing part today; while
ecosystem = [], both matrix entries compile an identical graph.