Repository navigation
feat: run jobs from the Haskell binding - #33
Conversation
WalkthroughChangesThe Haskell binding now supports durable job creation, polling, claiming, lease management, settlement, streaming, wire encoding, ABI errors, and ABI v3 tagged calls. It adds runtime and wire-format tests plus Cabal and Nix dependency wiring. Durable job binding
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
2b12033 to
b188473
Compare
7a06d40 to
6124d43
Compare
b188473 to
0277557
Compare
6124d43 to
481f8ac
Compare
0277557 to
7055567
Compare
481f8ac to
52a0d1a
Compare
7055567 to
619f6f2
Compare
52a0d1a to
5a44a19
Compare
619f6f2 to
ab37995
Compare
5a44a19 to
ecd3ce2
Compare
c9ad720 to
d41dc9b
Compare
6bcc70b to
9f2a692
Compare
d41dc9b to
5f35255
Compare
9f2a692 to
52be823
Compare
5f35255 to
f94fb39
Compare
52be823 to
99e2659
Compare
f94fb39 to
2976e92
Compare
99e2659 to
44a9204
Compare
2976e92 to
1841695
Compare
44a9204 to
cc59463
Compare
1841695 to
b4b7809
Compare
cc59463 to
087a39b
Compare
|
@coderabbitai review |
b4b7809 to
c1f1cd8
Compare
087a39b to
cb53757
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bindings/haskell/src/EventSorcery/Engine/Internal.hs`:
- Around line 388-405: Update callWithTag to inspect the value read from output
after callWithoutOutput succeeds, and convert unwrittenTag into the existing
unknown-tag EngineError before returning. Preserve successful tag values,
including any legitimate ABI-defined tags, while keeping call failures
unchanged.
- Line 143: Document in SPEC.md that the minimum ABI minor version is 3,
matching the minimumAbiMinor constant and openStore validation. State that ABI
0.2 engines, including stream-only deployments, are no longer compatible with
the bindings.
- Around line 509-522: Update SPEC.md’s JOB_REFUSAL specification to document
the six canonical reason literals used by decodeJobRefusal: job attempt counter
exhausted, invalid job claim policy, job aggregate refused the settlement,
claimed job payload is not opaque bytes, claimed job payload contains an invalid
byte, and claimed job payload exceeds the ABI payload limit. State that existing
values must not be reworded and that new refusal cases must be added as new
leaves; do not introduce a numeric subtag.
In `@bindings/haskell/src/EventSorcery/Job.hs`:
- Around line 256-262: Correct the documentation for ClaimedJob and related
claim-settlement APIs to state that linear consumption prevents reusing the
claim, while engine-side fencing rejects duplicate or superseded settlements; do
not claim the type system bounds token multiplicity. Record this ownership
decision in an ADR, covering settlementToken and JobSpec.releaseClaim, and apply
the same clarification to the corresponding documentation around the other noted
lines.
In `@bindings/haskell/test/JobSpec.hs`:
- Around line 128-137: Expand the JobSpec behavior tests to exercise the
settlement outcomes SettlementFenced, LeaseLost, JobContended, JobSkipped, and
JobRefused through the real engine, including a superseded-claim scenario that
produces SettlementFenced rather than only InvalidState. Verify the
corresponding lease and settlement results end to end; if SettlementFenced or
LeaseLost cannot be emitted by this binding, remove those constructors and their
decoding paths instead.
In `@bindings/haskell/test/WireSpec.hs`:
- Around line 82-98: Export encodeAcknowledge, encodeRetry, encodeDefer, and
encodeDeadLetter from EventSorcery.Job, then add WireSpec byte-fixture tests
covering each settlement encoder, including all dead-letter reason tags. Extend
decoding tests with JobContended and JobSkipped cases through decodeClaimWire,
preserving the existing request encoding coverage.
In `@flake.nix`:
- Line 48: Update the linear-base package override in the Nix package set to
remove dontCheck and keep both test suites enabled. If inspection assertions are
incompatible, disable only those assertions or switch
haskellPackages.linear-base to a compatible revision.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 223dbdc0-5a35-414c-b29c-2c1b2844fa54
📒 Files selected for processing (10)
bindings/haskell/event-sorcery.cabalbindings/haskell/src/EventSorcery/Engine.hsbindings/haskell/src/EventSorcery/Engine/Internal.hsbindings/haskell/src/EventSorcery/Engine/Internal/FFI.hsbindings/haskell/src/EventSorcery/Job.hsbindings/haskell/src/EventSorcery/Stream.hsbindings/haskell/test/JobSpec.hsbindings/haskell/test/WireSpec.hsflake.nixnix/event-sorcery-haskell.nix
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
- GitHub Check: clippy
- GitHub Check: test
- GitHub Check: check
- GitHub Check: hooks
- GitHub Check: fmt
- GitHub Check: examples
- GitHub Check: haskell
🧰 Additional context used
📓 Path-based instructions (2)
**/*
📄 CodeRabbit inference engine (AGENTS.md)
**/*: Before work, read SPEC.md and docs/domain.md; read relevant supplemental documentation before implementation.
New features must be documented in SPEC.md before implementation and must follow the hierarchy SPEC.md -> issue -> plan -> tests -> implementation.
Fix all known problems immediately, complete all tasks, and do not allow warnings or errors to pass through.
Keep a granular task list and clear completed tasks from the active list.
All new or modified logic must have corresponding test coverage.
Understand relevant documentation and source code before implementation, keep diffs small, and review the approach critically.
When changing direction or making an important undocumented architectural decision, obtain confirmation; record significant decisions as ADRs under adrs/.
Before handover, review the diff, revert unjustified changes, and check for scope creep.
Each aggregate in a consuming application must use exactly one SqliteCqrs instance constructed at startup; per-request construction is forbidden.
Never read secret or credential files such as .env*, credentials.json, *.key, *.pem, *.p12, *.pfx, or sensitive database files without explicit permission.
Never bypass, disable, suppress, or obscure quality-control mechanisms without explicit permission; fix lint and test issues at their root.
Use cargo check, cargo nextest, and cargo clippy for verification; never use cargo build unless build artifacts are required.
Files:
flake.nixbindings/haskell/src/EventSorcery/Engine.hsnix/event-sorcery-haskell.nixbindings/haskell/event-sorcery.cabalbindings/haskell/src/EventSorcery/Stream.hsbindings/haskell/test/WireSpec.hsbindings/haskell/test/JobSpec.hsbindings/haskell/src/EventSorcery/Engine/Internal/FFI.hsbindings/haskell/src/EventSorcery/Engine/Internal.hsbindings/haskell/src/EventSorcery/Job.hs
*
⚙️ CodeRabbit configuration file
Focus on providing constructive criticism. Whenever you see a suboptimal approach, suggest more idiomatic or robust alternative(s). Flag potential footguns. Suggest FP alternatives to mutable/imperative code. Point out architectural flaws like leaky abstractions, tight coupling, wrong level of abstraction, poor type modeling, over-abstraction, unclear domain boundaries. Code should generally be organized based on business concerns rather than technical aspects - suggest improvements if you find violations. Point out gaps in test coverage but suggest tests that are not too coupled to the implementation and actually test domain invariants and business logic
Files:
flake.nix
🧠 Learnings (3)
📓 Common learnings
Learnt from: 0xgleb
Repo: dataclique/event-sorcery PR: 0
File: :0-0
Timestamp: 2026-07-16T21:10:49.512Z
Learning: In `crates/event-sorcery/src/job_backend.rs`, `JobClaimHandle` is deliberately non-serializable. It contains private fencing identity, so foreign-language bindings must retain it in trusted process memory and expose only a binding-owned opaque token; accepting caller-provided serialized handles could permit forged claim state.
📚 Learning: 2026-07-16T15:10:47.551Z
Learnt from: 0xgleb
Repo: dataclique/event-sorcery PR: 24
File: crates/event-sorcery/src/lib.rs:88-88
Timestamp: 2026-07-16T15:10:47.551Z
Learning: In `crates/event-sorcery/src/lib.rs`, the crate-root `engine` module should remain private (`mod engine;`). Rust permits descendant modules, including `crates/event-sorcery/src/job_sqlite.rs` and `crates/event-sorcery/src/sqlite_event_repository.rs`, to access it through `crate::engine`; it does not need `pub(crate)` visibility for that internal access.
Applied to files:
bindings/haskell/src/EventSorcery/Engine.hsbindings/haskell/event-sorcery.cabalbindings/haskell/src/EventSorcery/Job.hs
📚 Learning: 2026-07-16T21:10:49.512Z
Learnt from: 0xgleb
Repo: dataclique/event-sorcery PR: 0
File: :0-0
Timestamp: 2026-07-16T21:10:49.512Z
Learning: In `crates/event-sorcery/src/job_backend.rs`, `JobClaimHandle` is deliberately non-serializable. It contains private fencing identity, so foreign-language bindings must retain it in trusted process memory and expose only a binding-owned opaque token; accepting caller-provided serialized handles could permit forged claim state.
Applied to files:
bindings/haskell/test/WireSpec.hsbindings/haskell/src/EventSorcery/Job.hs
🔇 Additional comments (21)
bindings/haskell/src/EventSorcery/Stream.hs (4)
29-30: LGTM!
220-220: LGTM!
229-240: LGTM!
243-259: LGTM!bindings/haskell/src/EventSorcery/Engine.hs (1)
14-16: LGTM!Also applies to: 40-42
bindings/haskell/src/EventSorcery/Engine/Internal.hs (2)
202-231: LGTM!Also applies to: 276-291, 482-486
5-6: LGTM!Also applies to: 15-24, 69-69, 571-575
bindings/haskell/src/EventSorcery/Engine/Internal/FFI.hs (2)
8-17: LGTM!Also applies to: 100-132
92-97: 🩺 Stability & AvailabilityNo change required
Word8is imported, and the relevant foreign import declarations are not duplicated.> Likely an incorrect or invalid review comment.bindings/haskell/src/EventSorcery/Job.hs (5)
580-657: LGTM!
304-331: LGTM!
372-409: LGTM!Also applies to: 490-499
163-254: LGTM!
418-435: 🗄️ Data Integrity & IntegrationKeep the current seed framing.
encodeCommitWithJobandencodeEnqueuematch their Rust decoders.bindings/haskell/test/JobSpec.hs (1)
74-127: LGTM!Also applies to: 138-147, 150-197
bindings/haskell/test/WireSpec.hs (1)
18-40: LGTM!Also applies to: 113-129, 170-188, 255-271, 428-678, 880-881
bindings/haskell/event-sorcery.cabal (3)
55-55: LGTM!
67-71: LGTM!
100-114: LGTM!nix/event-sorcery-haskell.nix (2)
6-13: LGTM!
23-37: LGTM!
| -- | The oldest ABI minor version whose calls this binding can make. | ||
| minimumAbiMinor :: Word32 | ||
| minimumAbiMinor = 2 | ||
| minimumAbiMinor = 3 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the engine-side ABI version constants and any ABI compatibility notes.
rg -n -C3 'ABI_(MAJOR|MINOR)|abi_minor|abi_version' --glob '!bindings/haskell/**'
fd -i 'SPEC.md|CHANGELOG.md' | xargs -r rg -n -C2 'ABI|minor'Repository: dataclique/event-sorcery
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- required documentation ---'
for f in SPEC.md docs/domain.md; do
if [ -f "$f" ]; then
printf '\n--- %s ---\n' "$f"
cat -n "$f"
else
printf '%s\n' "MISSING: $f"
fi
done
printf '%s\n' '--- ABI-related files and symbols ---'
rg -n -C4 'minimumAbiMinor|AbiMinor|abiMinor|abi_minor|ABI|abi' \
bindings/haskell \
--glob '!**/.git/**' || true
printf '%s\n' '--- repository files that may define the engine ABI ---'
git ls-files | rg -i '(^|/)(spec\.md|domain\.md|changelog|abi|version|build\.rs|cargo\.toml|.*engine.*)$' | head -200Repository: dataclique/event-sorcery
Length of output: 50382
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- required documentation ---'
for f in SPEC.md docs/domain.md; do
if [ -f "$f" ]; then
printf '\n--- %s ---\n' "$f"
cat -n "$f"
else
printf '%s\n' "MISSING: $f"
fi
done
printf '%s\n' '--- ABI-related files and symbols ---'
rg -n -C4 'minimumAbiMinor|AbiMinor|abiMinor|abi_minor|ABI|abi' \
bindings/haskell \
--glob '!**/.git/**' || true
printf '%s\n' '--- candidate engine ABI files ---'
git ls-files | rg -i '(^|/)(spec\.md|domain\.md|changelog|abi|version|build\.rs|cargo\.toml|.*engine.*)$' | head -200Repository: dataclique/event-sorcery
Length of output: 50382
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- engine-side ABI symbols ---'
rg -n -C5 'es_abi_version|abi_version|ABI_VERSION|AbiVersion|abi minor|minor version' \
--glob '*.rs' --glob '*.toml' --glob '*.h' --glob '*.h.in' . || true
printf '%s\n' '--- Haskell version gate and call paths ---'
sed -n '125,180p' bindings/haskell/src/EventSorcery/Engine/Internal.hs
rg -n -C5 'checkAbiVersion|withOpenStore|callWith(Output|Tag)|callWithoutOutput|openStore' \
bindings/haskell/src/EventSorcery/Engine/Internal.hs
printf '%s\n' '--- documentation and ADR ABI references ---'
rg -n -C3 'ABI|abi|minor|compatib' SPEC.md docs adrs \
--glob '*.md' || true
printf '%s\n' '--- Rust package layout ---'
git ls-files '*.rs' '*.toml' | sed -n '1,240p'Repository: dataclique/event-sorcery
Length of output: 37143
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Rust FFI ABI constants and export ---'
sed -n '1,80p' crates/event-sorcery-ffi/src/lib.rs
sed -n '200,245p' crates/event-sorcery-ffi/src/lib.rs
rg -n -C3 'const ABI|static ABI|ABI_MAJOR|ABI_MINOR|es_abi_version' \
crates/event-sorcery-ffi crates/event-sorcery crates/sqlite-es
printf '%s\n' '--- ABI version references outside generated/build output ---'
rg -n -C2 '0x000[0-9a-fA-F]+|abi[-_ ]minor|minimumAbiMinor|minimum minor|ABI version' \
--glob '!bindings/haskell/**' \
--glob '!target/**' \
--glob '!*.lock' . || trueRepository: dataclique/event-sorcery
Length of output: 7156
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
rust = Path("crates/event-sorcery-ffi/src/lib.rs").read_text()
haskell = Path("bindings/haskell/src/EventSorcery/Engine/Internal.hs").read_text()
spec = Path("SPEC.md").read_text()
rust_major = int(re.search(r"const ABI_MAJOR: u32 = (\d+);", rust).group(1))
rust_minor = int(re.search(r"const ABI_MINOR: u32 = (\d+);", rust).group(1))
hs_floor = int(re.search(r"minimumAbiMinor = (\d+)", haskell).group(1))
open_checks = bool(re.search(
r"openStore\b.*?checkAbiVersion version", haskell, re.S
))
spec_declares_floor = bool(re.search(
r"(?:minimumAbiMinor|minimum minor.{0,80}\b3\b|ABI minor.{0,80}\b3\b)",
spec, re.I | re.S
))
print({
"rust_abi": (rust_major, rust_minor),
"haskell_minimum_minor": hs_floor,
"open_store_checks_abi": open_checks,
"spec_explicitly_records_minor_3_floor": spec_declares_floor,
})
PYRepository: dataclique/event-sorcery
Length of output: 290
Document the ABI minor floor as 3
The Rust engine exports ABI 0.3, and openStore rejects engines below minor 3. Add the raised floor and its compatibility impact to SPEC.md; it prevents bindings from opening ABI 0.2 engines, including stream-only deployments.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/src/EventSorcery/Engine/Internal.hs` at line 143, Document
in SPEC.md that the minimum ABI minor version is 3, matching the minimumAbiMinor
constant and openStore validation. State that ABI 0.2 engines, including
stream-only deployments, are no longer compatible with the bindings.
| -- | Reads a call that answers with a single byte tag rather than a buffer. | ||
| -- | ||
| -- The tag only means anything once the call has succeeded, and the engine | ||
| -- writes it while it still owns the cell. The sentinel poked in first is | ||
| -- outside the tags the ABI defines, so a call that answered without writing | ||
| -- one is reported as an unknown tag instead of being read as the zero tag. | ||
| callWithTag | ||
| :: (Ptr Word8 -> Ptr EsBuf -> IO CInt) | ||
| -> IO (Either EngineError Word8) | ||
| callWithTag call = | ||
| alloca $ \output -> do | ||
| poke output unwrittenTag | ||
| called <- callWithoutOutput (call output) | ||
| case called of | ||
| Left engineError -> pure (Left engineError) | ||
| Right () -> Right <$> peek output | ||
|
|
||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
Detect the unwritten sentinel inside callWithTag.
The comment states that a call which wrote no tag is reported as an unknown tag. callWithTag does not do that; it returns Right 255. The guarantee currently depends on each caller in EventSorcery.Job having no case for 255. That contract is implicit and breaks if the ABI later defines tag 255, or if a caller uses a total mapping.
Reject the sentinel where it is poked, so the invariant is local.
♻️ Proposed change
callWithTag call =
alloca $ \output -> do
poke output unwrittenTag
called <- callWithoutOutput (call output)
case called of
Left engineError -> pure (Left engineError)
- Right () -> Right <$> peek output
+ Right () -> do
+ tag <- peek output
+ pure
+ if tag == unwrittenTag
+ then Left (BindingProtocolError (UnknownResultTag tag))
+ else Right tag📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| -- | Reads a call that answers with a single byte tag rather than a buffer. | |
| -- | |
| -- The tag only means anything once the call has succeeded, and the engine | |
| -- writes it while it still owns the cell. The sentinel poked in first is | |
| -- outside the tags the ABI defines, so a call that answered without writing | |
| -- one is reported as an unknown tag instead of being read as the zero tag. | |
| callWithTag | |
| :: (Ptr Word8 -> Ptr EsBuf -> IO CInt) | |
| -> IO (Either EngineError Word8) | |
| callWithTag call = | |
| alloca $ \output -> do | |
| poke output unwrittenTag | |
| called <- callWithoutOutput (call output) | |
| case called of | |
| Left engineError -> pure (Left engineError) | |
| Right () -> Right <$> peek output | |
| -- | Reads a call that answers with a single byte tag rather than a buffer. | |
| -- | |
| -- The tag only means anything once the call has succeeded, and the engine | |
| -- writes it while it still owns the cell. The sentinel poked in first is | |
| -- outside the tags the ABI defines, so a call that answered without writing | |
| -- one is reported as an unknown tag instead of being read as the zero tag. | |
| callWithTag | |
| :: (Ptr Word8 -> Ptr EsBuf -> IO CInt) | |
| -> IO (Either EngineError Word8) | |
| callWithTag call = | |
| alloca $ \output -> do | |
| poke output unwrittenTag | |
| called <- callWithoutOutput (call output) | |
| case called of | |
| Left engineError -> pure (Left engineError) | |
| Right () -> do | |
| tag <- peek output | |
| pure | |
| if tag == unwrittenTag | |
| then Left (BindingProtocolError (UnknownResultTag tag)) | |
| else Right tag |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/src/EventSorcery/Engine/Internal.hs` around lines 388 - 405,
Update callWithTag to inspect the value read from output after callWithoutOutput
succeeds, and convert unwrittenTag into the existing unknown-tag EngineError
before returning. Preserve successful tag values, including any legitimate
ABI-defined tags, while keeping call failures unchanged.
| -- | Reads the refusal reason the engine renders as fixed ABI text. | ||
| decodeJobRefusal :: Decoder s JobRefusal | ||
| decodeJobRefusal = do | ||
| reason <- decodeString | ||
| pure case reason of | ||
| "job attempt counter exhausted" -> AttemptExhausted | ||
| "invalid job claim policy" -> InvalidClaimPolicy | ||
| "job aggregate refused the settlement" -> SettlementRefused | ||
| "claimed job payload is not opaque bytes" -> PayloadNotOpaqueBytes | ||
| "claimed job payload contains an invalid byte" -> PayloadByteOutOfRange | ||
| "claimed job payload exceeds the ABI payload limit" -> PayloadAboveAbiLimit | ||
| unmodelled -> UnrecognizedRefusal unmodelled | ||
|
|
||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Compare the binding's refusal literals with the engine-side rendered text.
rg -n -C2 'job attempt counter exhausted|invalid job claim policy|job aggregate refused the settlement|claimed job payload' --glob '!bindings/haskell/**'Repository: dataclique/event-sorcery
Length of output: 162
🏁 Script executed:
printf '%s\n' '--- SPEC.md ---'
sed -n '1,220p' SPEC.md
printf '%s\n' '--- docs/domain.md ---'
sed -n '1,220p' docs/domain.md
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(SPEC\.md|docs/domain\.md|.*(Engine|WireSpec|Refusal|Error).*)$' | head -200
printf '%s\n' '--- relevant symbols and literals ---'
rg -n -C3 'decodeJobRefusal|JobRefusal|AttemptExhausted|SettlementRefused|UnrecognizedRefusal|job attempt counter exhausted|invalid job claim policy|job aggregate refused the settlement|claimed job payload' --glob '!*.env*' --glob '!credentials.json' --glob '!*.key' --glob '!*.pem' --glob '!*.p12' --glob '!*.pfx'Repository: dataclique/event-sorcery
Length of output: 21077
🏁 Script executed:
printf '%s\n' '--- Internal.hs outline and size ---'
wc -l bindings/haskell/src/EventSorcery/Engine/Internal.hs
ast-grep outline bindings/haskell/src/EventSorcery/Engine/Internal.hs | sed -n '1,220p'
printf '%s\n' '--- refusal decoder and model ---'
sed -n '180,245p' bindings/haskell/src/EventSorcery/Engine/Internal.hs
sed -n '480,535p' bindings/haskell/src/EventSorcery/Engine/Internal.hs
printf '%s\n' '--- Haskell refusal references ---'
rg -n -C4 'JobRefusal|AttemptExhausted|SettlementRefused|UnrecognizedRefusal|decodeJobRefusal|job attempt counter|invalid job claim|refused the settlement|payload is not opaque|invalid byte|ABI payload' bindings/haskell
printf '%s\n' '--- refusal references outside Haskell binding ---'
rg -n -C4 'AttemptExhausted|SettlementRefused|PayloadNotOpaqueBytes|PayloadByteOutOfRange|PayloadAboveAbiLimit|UnrecognizedRefusal|job attempt counter|invalid job claim|refused the settlement|payload is not opaque|invalid byte|ABI payload' --glob '!bindings/haskell/**' --glob '!*.env*' --glob '!credentials.json' --glob '!*.key' --glob '!*.pem' --glob '!*.p12' --glob '!*.pfx'Repository: dataclique/event-sorcery
Length of output: 15527
🏁 Script executed:
printf '%s\n' '--- SPEC ABI/error sections ---'
rg -n -C5 'refusal|JobRefused|error|ABI text|wire|numeric|claim policy|payload' SPEC.md docs adrs bindings/haskell --glob '*.md' --glob '*.hs'
printf '%s\n' '--- Rust and C ABI files ---'
git ls-files | rg '(^|/)(src|crates|bindings)/.*\.(rs|h|c|cc|cpp)$' | head -300
printf '%s\n' '--- engine error encoders and refusal strings ---'
rg -n -C5 'JobRefused|job_refused|JobRefusal|AttemptExhausted|InvalidClaimPolicy|SettlementRefused|PayloadNotOpaqueBytes|PayloadByteOutOfRange|PayloadAboveAbiLimit|attempt counter exhausted|invalid job claim policy|refused the settlement|payload exceeds|opaque bytes|invalid byte' crates src bindings --glob '*.!env*' --glob '!*.key' --glob '!*.pem' --glob '!*.p12' --glob '!*.pfx' 2>/dev/null || true
printf '%s\n' '--- WireSpec relevant regions ---'
sed -n '80,140p' bindings/haskell/test/WireSpec.hs
sed -n '620,675p' bindings/haskell/test/WireSpec.hsRepository: dataclique/event-sorcery
Length of output: 50383
🏁 Script executed:
printf '%s\n' '--- SPEC error identity ---'
sed -n '298,326p' SPEC.md
printf '%s\n' '--- exact refusal literals in tracked files ---'
git grep -n -C2 -E 'job attempt counter exhausted|invalid job claim policy|job aggregate refused the settlement|claimed job payload is not opaque bytes|claimed job payload contains an invalid byte|claimed job payload exceeds the ABI payload limit' -- ':!bindings/haskell/**' || true
printf '%s\n' '--- refusal/error identifiers in tracked files ---'
git grep -n -C3 -E 'JobRefused|JobRefusal|AttemptExhausted|InvalidClaimPolicy|SettlementRefused|PayloadNotOpaqueBytes|PayloadByteOutOfRange|PayloadAboveAbiLimit|UnknownEngineError|RESOURCE_LIMIT|MALFORMED_INPUT' -- '*.rs' '*.hs' '*.md' '*.h' | head -500
printf '%s\n' '--- WireSpec fixtures ---'
sed -n '100,135p' bindings/haskell/test/WireSpec.hs
sed -n '630,675p' bindings/haskell/test/WireSpec.hs
printf '%s\n' '--- all ABI/error-related Rust filenames ---'
git ls-files '*.rs' | rg 'abi|ffi|engine|job|error|wire|codec|cbor' | head -250Repository: dataclique/event-sorcery
Length of output: 41639
🏁 Script executed:
printf '%s\n' '--- refusal encoder and Rust tests ---'
sed -n '1245,1325p' crates/event-sorcery-ffi/src/lib.rs
sed -n '1748,1812p' crates/event-sorcery-ffi/src/lib.rs
sed -n '2160,2185p' crates/event-sorcery-ffi/src/lib.rs
sed -n '4120,4300p' crates/event-sorcery-ffi/src/lib.rs
printf '%s\n' '--- refusal tests and conformance references ---'
git grep -n -C5 -E 'PayloadNotOpaqueBytes|PayloadByteOutOfRange|PayloadAboveAbiLimit|AttemptExhausted|SettlementRefused|InvalidClaimPolicy|job-refusal|JOB_REFUSAL|fixed ABI|canonical detail|conformance corpus' -- crates bindings SPEC.md docs adrs | head -700
printf '%s\n' '--- encoded fixture declarations ---'
rg -n -C3 'jobRefusal|unmodelledRefusal|invalid job claim policy|reason' bindings/haskell/test/WireSpec.hsRepository: dataclique/event-sorcery
Length of output: 43062
🏁 Script executed:
python3 - <<'PY'
from pathlib import Path
import re
rust = Path("crates/event-sorcery-ffi/src/lib.rs").read_text()
hs = Path("bindings/haskell/src/EventSorcery/Engine/Internal.hs").read_text()
spec = Path("SPEC.md").read_text()
rust_block = re.search(
r"enum JobRefusal\s*\{(?P<body>.*?)\n\}",
rust,
re.S,
)
hs_block = re.search(
r"decodeJobRefusal\s*=\s*do.*?pure case reason of(?P<body>.*?)\n\s*unmodelled",
hs,
re.S,
)
assert rust_block and hs_block
rust_literals = re.findall(r'#\[error\("([^"]+)"\)\]', rust_block.group("body"))
hs_literals = re.findall(r'^\s*"([^"]+)"\s*->', hs_block.group("body"), re.M)
print("rust_literal_count =", len(rust_literals))
print("haskell_literal_count =", len(hs_literals))
print("literal_sets_match =", rust_literals == hs_literals)
print("rust_literals =", rust_literals)
print("spec_lists_canonical_literals =", all(literal in spec for literal in rust_literals))
print("spec_defines_reason_shape =", "[job_id, reason]" in spec)
PYRepository: dataclique/event-sorcery
Length of output: 570
Document the canonical JOB_REFUSAL reasons in SPEC.md.
The Rust engine and Haskell binding use the same six literals. SPEC.md specifies only [job_id, reason]; it does not list the canonical values or prohibit rewording existing values. Document the six values and require new refusal leaves instead of changing existing values. A numeric subtag is not required for the current ABI shape.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/src/EventSorcery/Engine/Internal.hs` around lines 509 - 522,
Update SPEC.md’s JOB_REFUSAL specification to document the six canonical reason
literals used by decodeJobRefusal: job attempt counter exhausted, invalid job
claim policy, job aggregate refused the settlement, claimed job payload is not
opaque bytes, claimed job payload contains an invalid byte, and claimed job
payload exceeds the ABI payload limit. State that existing values must not be
reworded and that new refusal cases must be added as new leaves; do not
introduce a numeric subtag.
Source: Path instructions
| -- | A won claim, which its worker consumes exactly once. | ||
| -- | ||
| -- The claim is the right to run the job and then settle it, and the engine | ||
| -- retains exactly one settleable claim per job. Consuming the value linearly | ||
| -- is what keeps a worker from settling the same claim twice. | ||
| data ClaimedJob where | ||
| ClaimedJob :: Ur ByteString %1 -> ClaimedJob |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
The linear claim does not prevent double settlement; the documentation says it does.
Lines 259-260 state that consuming the claim linearly stops a worker settling the same claim twice. settlementToken returns Ur JobSettlementToken. Ur is unrestricted, so the token escapes the claim body and can be passed to acknowledgeJob, retryJob, deferJob and deadLetterJob any number of times. JobSpec.releaseClaim does exactly that, and one test settles a stale token and then a current token.
Linearity here only guarantees that ClaimedJob is consumed once. It does not bound settlement. Double settlement is prevented by engine-side fencing, which is why SettlementFenced exists.
Two options:
- Keep the current shape and correct the comment. State that the linear claim mints one token and that fencing, not the type system, rejects a superseded settlement.
- Enforce the invariant in types. Do not return the token to the caller. Give the claim body a linear settlement continuation, so the token cannot outlive the claim.
The type-level option is the stronger design, but it changes the public API. Choose one deliberately and record the decision, because it is an architectural choice about where the ownership guarantee lives.
Based on learnings, JobClaimHandle carries private fencing identity and bindings must expose only a binding-owned opaque token; the unexported JobSettlementToken constructor satisfies that part, so this comment concerns the multiplicity guarantee only. As per coding guidelines, "When changing direction or making an important undocumented architectural decision, obtain confirmation; record significant decisions as ADRs under adrs/."
Also applies to: 354-356
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/src/EventSorcery/Job.hs` around lines 256 - 262, Correct the
documentation for ClaimedJob and related claim-settlement APIs to state that
linear consumption prevents reusing the claim, while engine-side fencing rejects
duplicate or superseded settlements; do not claim the type system bounds token
multiplicity. Record this ownership decision in an ADR, covering settlementToken
and JobSpec.releaseClaim, and apply the same clarification to the corresponding
documentation around the other noted lines.
Sources: Coding guidelines, Learnings
| , testCase "retires the claim that a later claim superseded" $ | ||
| withStore $ \store -> do | ||
| enqueueJob store seed >>= (@?= Right ()) | ||
| (stale, staleToken) <- claimFixture store now | ||
| (_, currentToken) <- claimFixture store later | ||
| renewJob store stale.reference (JobInstant 120_000) | ||
| >>= (@?= Left (InvalidState "claim handle is invalid")) | ||
| acknowledgeJob store staleToken | ||
| >>= (@?= Left (InvalidState "claim handle is invalid")) | ||
| acknowledgeJob store currentToken >>= (@?= Right SettlementApplied) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Cover the outcomes the settlement model depends on.
The suite never observes SettlementFenced, LeaseLost, JobContended or JobSkipped from the real engine. The fencing test reaches a superseded claim, but the engine answers InvalidState "claim handle is invalid" for both the renewal and the stale acknowledgement, so SettlementFenced is never produced.
This matters beyond coverage. The safety argument for the claim API is that the engine fences a superseded settlement. No test demonstrates that path, so decodeSettlement 1 and decodeLeaseResult 1 are unexercised end to end.
Add cases that produce these outcomes, or confirm the engine cannot return SettlementFenced and LeaseLost through this binding and remove the constructors. Also cover the JobRefused error, which the wire tests decode but no behaviour test triggers.
As per coding guidelines, "All new or modified logic must have corresponding test coverage."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/test/JobSpec.hs` around lines 128 - 137, Expand the JobSpec
behavior tests to exercise the settlement outcomes SettlementFenced, LeaseLost,
JobContended, JobSkipped, and JobRefused through the real engine, including a
superseded-claim scenario that produces SettlementFenced rather than only
InvalidState. Verify the corresponding lease and settlement results end to end;
if SettlementFenced or LeaseLost cannot be emitted by this binding, remove those
constructors and their decoding paths instead.
Source: Coding guidelines
| , testCase "commit carrying a job intent" $ | ||
| encodeCommitWithJob stream 0 (proposed :| []) seed | ||
| @?= expectedCommitWithJob | ||
| , testCase "job enqueue" $ | ||
| encodeEnqueue seed @?= expectedEnqueue | ||
| , testCase "job poll" $ | ||
| encodePoll kind (JobInstant 5) (PollLimit 10) @?= expectedPoll | ||
| , testCase "job claim" $ | ||
| encodeClaim | ||
| (JobId "job") | ||
| (WorkerId "worker") | ||
| (JobInstant 5) | ||
| (LeaseDuration 30_000) | ||
| (ClaimBudget 50) | ||
| @?= expectedClaim | ||
| , testCase "claim renewal" $ | ||
| encodeRenew reference (JobInstant 60_000) @?= expectedRenew |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Pin the settlement request encodings.
The suite pins five request encodings. It pins none of the four settlement encodings: encodeAcknowledge, encodeRetry, encodeDefer and encodeDeadLetter. Those four are not exported from EventSorcery.Job, while the five tested encoders are. The split is arbitrary, and it leaves the settlement half of the wire contract unverified.
encodeDeadLetter writes deadReasonTag, which maps four reasons to 0, 1, 2 and 3. If a value is wrong, the engine records the wrong reason for a job leaving the queue, and no test fails.
Export the four settlement encoders and add byte fixtures for them. Also add decoding cases for JobContended and JobSkipped, which decodeClaimWire handles but no test exercises.
As per coding guidelines, "All new or modified logic must have corresponding test coverage."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bindings/haskell/test/WireSpec.hs` around lines 82 - 98, Export
encodeAcknowledge, encodeRetry, encodeDefer, and encodeDeadLetter from
EventSorcery.Job, then add WireSpec byte-fixture tests covering each settlement
encoder, including all dead-letter reason tags. Extend decoding tests with
JobContended and JobSkipped cases through decodeClaimWire, preserving the
existing request encoding coverage.
Source: Coding guidelines
| ./nix/event-sorcery-haskell.nix | ||
| { | ||
| event_sorcery_ffi = ffiEngine; | ||
| linear-base = pkgs.haskell.lib.dontCheck haskellPackages.linear-base; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
printf '%s\n' '--- SPEC.md ---'
cat SPEC.md
printf '%s\n' '--- docs/domain.md ---'
cat docs/domain.md
printf '%s\n' '--- flake.nix ---'
cat -n flake.nix | sed -n '1,100p'
printf '%s\n' '--- relevant references ---'
rg -n --hidden --glob '!*.env*' --glob '!credentials.json' 'linear-base|dontCheck|inspection' .Repository: dataclique/event-sorcery
Length of output: 39989
🏁 Script executed:
printf '%s\n' '--- Haskell package expression ---'
cat -n nix/event-sorcery-haskell.nix
printf '%s\n' '--- flake.lock references ---'
rg -n -C 3 '"rainix"|"nixpkgs"|revision|lastModified' flake.lock | head -160
printf '%s\n' '--- linear-base references and package metadata ---'
rg -n -C 4 'linear-base|dontCheck|inspection' nix bindings flake.nix flake.lock
printf '%s\n' '--- repository files mentioning tests or package overrides ---'
git ls-files | rg '(^|/)(flake|.*linear|.*nix|.*test)' | head -200Repository: dataclique/event-sorcery
Length of output: 9737
🏁 Script executed:
set -eu
NIXPKGS_REV=535f3e6942cb1cead3929c604320d3db54b542b9
printf '%s\n' '--- rainix lock entry ---'
sed -n '300,390p' flake.lock
printf '%s\n' '--- dontCheck implementation at pinned Nixpkgs ---'
curl -fsSL "https://raw.githubusercontent.com/NixOS/nixpkgs/$NIXPKGS_REV/pkgs/development/haskell-modules/lib.nix" |
rg -n -A 8 -B 5 'dontCheck'
printf '%s\n' '--- linear-base package definition at pinned Nixpkgs ---'
curl -fsSL "https://raw.githubusercontent.com/NixOS/nixpkgs/$NIXPKGS_REV/pkgs/development/haskell-modules/hackage-packages.nix" |
rg -n -A 35 -B 5 'linear-base'
printf '%s\n' '--- linear-base Hackage metadata ---'
curl -fsSL 'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base.cabal' |
rg -n -A 20 -B 5 'test-suite|inspection-testing|build-depends'Repository: dataclique/event-sorcery
Length of output: 2793
🏁 Script executed:
set -eu
printf '%s\n' '--- rainix nixpkgs_4 lock entry ---'
sed -n '270,315p' flake.lock
NIXPKGS_REV="$(sed -n '270,315p' flake.lock | awk '/"rev":/ {gsub(/[",]/,"",$2); print $2; exit}')"
printf 'NIXPKGS_REV=%s\n' "$NIXPKGS_REV"
printf '%s\n' '--- Haskell module tree ---'
curl -fsSL "https://api.github.com/repos/NixOS/nixpkgs/contents/pkgs/development/haskell-modules?ref=$NIXPKGS_REV" |
jq -r '.[].path' | head -100
printf '%s\n' '--- candidate dontCheck definitions ---'
for path in \
pkgs/development/haskell-modules/default.nix \
pkgs/development/haskell-modules/lib.nix \
pkgs/development/haskell-modules/make-package-set.nix \
pkgs/development/haskell-modules/hackage-packages.nix
do
url="https://raw.githubusercontent.com/NixOS/nixpkgs/$NIXPKGS_REV/$path"
body="$(curl -fsSL "$url" 2>/dev/null || true)"
if [ -n "$body" ]; then
printf '%s\n' "--- $path ---"
printf '%s\n' "$body" | rg -n -A 10 -B 5 'dontCheck|linear-base' || true
fi
doneRepository: dataclique/event-sorcery
Length of output: 7030
🏁 Script executed:
set -eu
NIXPKGS_REV=4ba039de0909446943c07e2b42bd2f0f4507072e
printf '%s\n' '--- full linear-base 0.5.0 derivation ---'
curl -fsSL "https://raw.githubusercontent.com/NixOS/nixpkgs/$NIXPKGS_REV/pkgs/development/haskell-modules/hackage-packages.nix" |
sed -n '430240,430325p'
printf '%s\n' '--- upstream linear-base 0.5.0 cabal file ---'
curl -fsSL 'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base-0.5.0.cabal' |
rg -n -A 35 -B 5 'test-suite|inspection-testing|build-depends'
printf '%s\n' '--- upstream linear-base package file list ---'
curl -fsSL 'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base-0.5.0.tar.gz' |
tar -tzf - | rg '(^|/)(test|tests|inspection|.*Spec|.*Test)' | head -120Repository: dataclique/event-sorcery
Length of output: 2005
🌐 Web query:
linear-base 0.5.0 source test-suite inspection-testing tasty-inspection-testing
💡 Result:
Linear-base 0.5.0 is a standard library for Haskell applications utilizing linear types, released on April 7, 2025 [1][2]. It is designed to work with the -XLinearTypes language extension introduced in GHC 9 [1][3]. Inspection testing in Haskell is enabled by the inspection-testing package, which provides a GHC plugin to embed and verify assertions about intermediate compiler code (Core) [4][5]. The tasty-inspection-testing package acts as an integration layer, allowing these inspection-testing obligations to be seamlessly incorporated into tasty test suites [6][7]. While linear-base itself focuses on providing linear type primitives, it has historically utilized inspection-testing in its own test suite to verify code properties [6][7]. Key components involved include: 1. inspection-testing: The core GHC plugin that performs the actual verification of code properties, such as checking for the absence of allocation, verifying equality of definitions, or ensuring strictness properties [4][5][8]. 2. tasty-inspection-testing: A bridge library that converts inspection-testing obligations into tasty TestTrees, enabling them to be run alongside standard unit or property tests [6][9][10]. To use these in a project, developers typically define obligations using TemplateHaskell and the inspection-testing API (e.g., inspect or inspectTest) [11][12]. When integrated with tasty, tasty-inspection-testing automates the plugin enabling process, meaning manual configuration of the GHC plugin is often unnecessary when using the inspectTest function [13].
Citations:
- 1: https://hackage.haskell.org/package/linear-base
- 2: https://hackage.haskell.org/package/linear-base-0.5.0/changelog
- 3: https://github.com/tweag/linear-base/
- 4: https://hackage.haskell.org/package/inspection-testing
- 5: https://github.com/nomeata/inspection-testing/
- 6: https://hackage.haskell.org/package/tasty-inspection-testing
- 7: https://github.com/Bodigrim/tasty-inspection-testing
- 8: https://www.stackage.org/package/inspection-testing
- 9: https://www.stackage.org/haddock/nightly-2026-03-30/tasty-inspection-testing-0.2.1/Test-Tasty-Inspection.html
- 10: https://hackage.haskell.org/package/tasty-inspection-testing-0.2/docs/Test-Tasty-Inspection.html
- 11: https://www.stackage.org/haddock/nightly-2026-03-19/inspection-testing-0.6.3/index.html
- 12: https://www.stackage.org/haddock/nightly-2026-03-17/inspection-testing-0.6.3/Test-Inspection.html
- 13: https://www.stackage.org/haddock/nightly-2026-03-17/tasty-inspection-testing-0.2.1/Test-Tasty-Inspection-Plugin.html
🏁 Script executed:
set -eu
printf '%s\n' '--- Hackage package metadata ---'
for url in \
'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base.json' \
'https://hackage.haskell.org/package/linear-base-0.5.0/revision/0.cabal' \
'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base.cabal' \
'https://hackage.haskell.org/package/linear-base-0.5.0/linear-base-0.5.0.tar.gz'
do
printf '%s\n' "--- $url ---"
curl -fsSL "$url" -o /tmp/linear-base-response 2>/dev/null || {
printf '%s\n' 'unavailable'
continue
}
file /tmp/linear-base-response
case "$url" in
*.tar.gz) tar -tzf /tmp/linear-base-response | rg '(^|/)(test|tests|inspection|.*Spec|.*Test)' | head -120 ;;
*.json|*.cabal) rg -n -A 35 -B 5 'test-suite|inspection-testing|build-depends' /tmp/linear-base-response || true ;;
esac
doneRepository: dataclique/event-sorcery
Length of output: 408
🌐 Web query:
site:github.com/NixOS/nixpkgs "dontCheck =" haskell
💡 Result:
In the context of the Nix package manager (nixpkgs), dontCheck is a helper function used in Haskell package derivations to disable the execution of a package's test suite [1][2]. When applied to a Haskell package, dontCheck sets the doCheck attribute to false [1][3][4]. This is useful when a package has a broken, flaky, or otherwise problematic test suite that prevents the package from building successfully [1]. Key points regarding dontCheck: - Functionality: It is a wrapper that modifies a derivation by setting doCheck = false, ensuring the test phase is skipped [2][3][4]. - Conditional usage: There is also a variant called dontCheckIf, which allows you to disable tests only if a specific condition is met (e.g., based on the operating system or architecture) [1][2][5]. - Limitations: It is important to note that while dontCheck disables the execution of the tests, it does not necessarily remove the dependencies that were required to run the test suite [6]. Those dependencies may still be present in the build environment. - Nixpkgs usage: You will typically see it used in overrides within Nix expressions, such as in configuration-common.nix, to handle packages with known test issues [5].
Citations:
- 1: https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/haskell.section.md
- 2: https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/lib/default.nix
- 3: https://github.com/NixOS/nixpkgs/blob/fcb8d47fcd3398132da439f601f449fd9c7e9f05/pkgs/development/haskell-modules/lib.nix
- 4: https://github.com/NixOS/nixpkgs/blob/00afba6480eadd14d69aece0e5da7842eeee0673/pkgs/development/haskell-modules/lib/compose.nix
- 5: https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/haskell-modules/configuration-common.nix
- 6:
dontCheckfor haskell packages doesn't remove dependencies NixOS/nixpkgs#224197
🏁 Script executed:
set -eu
printf '%s\n' '--- linear-base upstream tags ---'
curl -fsSL 'https://api.github.com/repos/tweag/linear-base/git/refs/tags' |
jq -r '.[].ref' | rg '0\.5\.0|v0\.5\.0'
printf '%s\n' '--- linear-base upstream repository files ---'
for ref in v0.5.0 0.5.0 master; do
url="https://raw.githubusercontent.com/tweag/linear-base/$ref/linear-base.cabal"
body="$(curl -fsSL "$url" 2>/dev/null || true)"
if [ -n "$body" ]; then
printf '%s\n' "--- ref $ref ---"
printf '%s\n' "$body" | rg -n -A 45 -B 5 'test-suite|benchmark|inspection-testing|build-depends'
fi
done
printf '%s\n' '--- upstream test paths at v0.5.0 ---'
TAG_SHA="$(curl -fsSL 'https://api.github.com/repos/tweag/linear-base/git/ref/tags/v0.5.0' | jq -r '.object.sha')"
curl -fsSL "https://api.github.com/repos/tweag/linear-base/git/trees/$TAG_SHA?recursive=1" |
jq -r '.tree[]?.path' | rg '(^|/)(test|tests|inspection|.*Spec|.*Test)' | head -160Repository: dataclique/event-sorcery
Length of output: 245
🌐 Web query:
github tweag linear-base linear-base.cabal test-suite 0.5.0
💡 Result:
In version 0.5.0 of the tweag/linear-base Haskell package, the linear-base.cabal file defines two test suites [1]: 1. test: This is the main test suite, which uses exitcode-stdio-1.0 and runs the module Main.hs from the test directory [1]. It includes various modules such as Test.Data.Destination, Test.Data.Functor.Linear, Test.Data.List, Test.Data.Mutable.Array, Test.Data.Mutable.HashMap, Test.Data.Mutable.Set, Test.Data.Mutable.Vector, Test.Data.Polarized, Test.Data.Replicator, and Test.Data.V [1]. Its dependencies include inspection-testing, tasty-inspection-testing, base, linear-base, containers, hedgehog, tasty, tasty-hedgehog (>= 1.2), mmorph, vector, and linear-generics [1]. 2. test-examples: This test suite also uses exitcode-stdio-1.0 and runs Main.hs from the test-examples directory [1]. It includes the modules Test.Foreign and Test.Simple.Quicksort [1]. Its dependencies include base, linear-base, tasty, tasty-hedgehog, hedgehog, and examples [1]. The 0.5.0 version of linear-base was released on April 7, 2025 [2][3]. The cabal file for this version transitioned to using shared imports (build-opts and rts-opts-multithread) for these test suites [1][2].
Citations:
- 1: https://hackage.haskell.org/package/linear-base-0.5.0/linear-base.cabal
- 2: tweag/linear-base@v0.4.0...v0.5.0
- 3: https://hackage.haskell.org/package/linear-base-0.5.0/changelog
🏁 Script executed:
set -eu
printf '%s\n' '--- Hackage package index entries ---'
curl -fsSL 'https://hackage.haskell.org/01-index.tar.gz' |
tar -tzf - 2>/dev/null |
rg '^linear-base/' | tail -30 || true
printf '%s\n' '--- upstream raw cabal candidates ---'
for ref in \
linear-base-0.5.0 \
linear-base-v0.5.0 \
v0.5.0 \
0.5.0 \
main
do
for name in linear-base.cabal linear-base/linear-base.cabal; do
url="https://raw.githubusercontent.com/tweag/linear-base/$ref/$name"
body="$(curl -fsSL "$url" 2>/dev/null || true)"
if [ -n "$body" ]; then
printf '%s\n' "--- $ref/$name ---"
printf '%s\n' "$body" | rg -n -A 45 -B 5 'test-suite|inspection-testing|build-depends'
fi
done
doneRepository: dataclique/event-sorcery
Length of output: 4203
Keep the linear-base checks enabled.
dontCheck skips both linear-base test suites, including ordinary Hedgehog/Tasty tests and example tests. Disable only the incompatible inspection assertions, or use a compatible package revision.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@flake.nix` at line 48, Update the linear-base package override in the Nix
package set to remove dontCheck and keep both test suites enabled. If inspection
assertions are incompatible, disable only those assertions or switch
haskellPackages.linear-base to a compatible revision.
Source: Coding guidelines
Expose the existing Rust job runtime through a feature-packaged Haskell API. Add Conduit polling, linear one-shot claim ownership with fenced settlement tokens, and real-engine tests for atomic enqueue, retries, deferrals, renewal, and dead lettering.
Keep the binding on GHC 9.14.1 while disabling only linear-base's incompatible upstream inspection tests; the event-sorcery test suites remain enabled.
Closes #69.
This is part 15 of 32 in a stack made with GitButler:
Summary by CodeRabbit
New Features
Tests