Add an AGENTS.md file to project

This commit is contained in:
Hassan Abedi 2026-03-24 14:24:30 +01:00
parent dfe05b2456
commit 8728e0062a
7 changed files with 182 additions and 41 deletions

131
AGENTS.md Normal file
View File

@ -0,0 +1,131 @@
# AGENTS.md
This file provides guidance to coding agents working in this repository.
## Mission
`integrations` is an early-stage repository for integration work across Rust, Python, and Haskell-related research notes.
At the time of writing, the repository is still close to a template scaffold. Some names and files still reflect that origin. Treat the current codebase as incomplete infrastructure unless the user says otherwise.
Priorities, in order:
1. Keep changes aligned with the actual repository state.
2. Prefer small, targeted edits over broad cleanup.
3. Preserve a clear separation between code, tooling, and research notes.
4. Keep documentation factual and easy to scan.
## Current Repository State
- The Rust code lives under `rust/` rather than the usual `src/` layout.
- The repository still contains some scaffold residue, but the main Rust crate naming should match `integrations`.
- `README.md` is minimal and does not define a stable product scope yet.
- `pyproject.toml` sets up a Python environment with `maturin[zig]` and `numpy`.
- `haskell/notes/` contains project review notes.
- `tmp/` contains temporary or inspected repositories and should not be treated as product code unless the user asks for that specifically.
Do not assume the repository is complete or internally consistent.
## Directory Guide
- `rust/`
- Main Rust sources.
- Current files include CLI and logging scaffolding.
- Keep changes here minimal and consistent with the existing file layout unless the user asks for restructuring.
- `haskell/notes/`
- Notes and reviews of Haskell-related projects or integration experiments.
- Use `haskell/notes/_template.md` as the default format.
- Write in a factual review style.
- `tmp/`
- Scratch space, cloned repos, and inspection targets.
- Do not treat files here as part of the maintained codebase unless the task explicitly targets them.
- `Makefile`
- Project convenience commands.
- Some targets may still reflect template assumptions.
- `.pre-commit-config.yaml`
- Hook configuration for formatting, linting, and tests.
## Core Rules
- Use English for code, comments, docs, tests, and notes.
- Keep edits scoped to the user request.
- Do not perform broad template cleanup unless the user asks for it.
- Do not remove template residue just because it is present.
- When you find template leftovers, note them if relevant, but do not expand scope without approval.
- Add comments only when they clarify non-obvious behavior.
- Keep documentation factual. Do not use poetic wording or decorative language.
## Rust Conventions
- Target the toolchain in `rust-toolchain.toml`.
- Follow existing lint expectations from the `Makefile`.
- Avoid `unwrap()` and `expect()` in production code unless there is a clear justification.
- Prefer explicit error handling with `Result`.
- Prefer simple APIs and small modules over speculative abstraction.
- Use `tracing` consistently where logging is already part of the flow.
## Python Conventions
- Treat `pyproject.toml` as environment support for the repository, not as proof that a Python application exists.
- Do not add Python dependencies unless the task requires them.
- Keep Python-related changes minimal and tied to integration needs.
## Notes Conventions
Applies to files under `haskell/notes/`:
- Use Title Case for headings.
- Write like a reviewer.
- Keep the tone factual and direct.
- Avoid colorful adjectives and adverbs.
- Prefer short paragraphs and short lists.
- State strengths, weaknesses, and status directly.
- If something is incomplete, fragile, or early, say so plainly.
## Validation
Validate as narrowly as possible.
For Rust changes, prefer the smallest relevant check first. Examples:
1. `cargo test` for the affected crate or module
2. `cargo clippy` if the Rust workspace is in a runnable state
3. `cargo fmt --check` or `cargo fmt`
If the repository is missing required manifests or is otherwise not runnable, do not fix unrelated scaffolding unless the user asks. Report the limitation clearly.
For notes-only changes, no build validation is required.
## Change Design Checklist
Before editing:
1. Confirm whether the target is maintained code, tooling, notes, or scratch material.
2. Check whether the repository state supports validation.
3. Keep the change limited to the stated task.
Before finishing:
1. Verify the requested files were updated.
2. Run targeted validation when it applies.
3. Call out any template residue or missing infrastructure that affects confidence.
## Review Guidance
When reviewing or summarizing work in this repository:
- Focus on correctness, maintainability, and scope control.
- Distinguish between template residue and task-specific issues.
- Do not recommend broad cleanup unless it is required.
- Keep findings concise and factual.
## Practical Notes For Agents
- The current repository is not a finished product. Avoid assumptions about intended architecture.
- `tmp/` is usually for inspection work, not implementation work.
- `haskell/notes/` is part of the maintained repo and should stay consistent in tone and format.
- If the user asks for project-level cleanup, identify template leftovers first and then propose a scoped plan.

18
Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "integrations"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
publish = false
[lib]
path = "rust/lib.rs"
[[bin]]
name = "integrations"
path = "rust/main.rs"
[dependencies]
ctor = "0.2"
tracing = "0.1"
tracing-subscriber = "0.3"

View File

@ -1,14 +1,6 @@
# Load environment variables from .env file
ifneq (,$(wildcard ./.env))
include .env
export $(shell sed 's/=.*//' .env)
else
$(warning .env file not found. Environment variables not loaded.)
endif
# Variables
PROJ_REPO = github.com/habedi/template-rust-project
BINARY_NAME := $(or $(PROJ_BINARY), $(notdir $(PROJ_REPO)))
PROJ_REPO = code.obsidian.systems/habedi-work/integrations
BINARY_NAME := $(or $(PROJ_BINARY), integrations)
BINARY := target/release/$(BINARY_NAME)
PATH := /snap/bin:$(PATH)
DEBUG_PROJ := 0

View File

@ -12,7 +12,7 @@ The repo is testing a specific pipeline:
Rust exports a C ABI -> `cbindgen` emits a header -> `hs-bindgen` produces Haskell bindings -> hand-written Haskell wrappers clean up the API.
It succeeds as a demo. It does not read as production-ready infrastructure.
The repo demonstrates the pipeline. It is not production-ready infrastructure.
## What It Contains
@ -25,50 +25,49 @@ It succeeds as a demo. It does not read as production-ready infrastructure.
## Review
The project makes the intended architecture clear. Separating raw bindings from the wrapper layer is the right call, and the examples are broad enough
to be useful. This is more than a toy "add two numbers" demo.
The architecture is clear. The split between raw bindings and wrapper code is appropriate. The examples cover more than trivial FFI cases.
That said, the repo still looks early. The build is custom, the dependency stack is pinned to git revisions, and the Rust build script contains a
workaround for upstream tooling issues. That combination is acceptable in an experiment, but it is a weak base for a maintained integration layer.
The project is still early. The build is custom, the dependencies are pinned to git revisions, and the Rust build script contains a workaround for
upstream tooling issues. That is acceptable for an experiment, but it is a weak base for a maintained integration layer.
The biggest limitation is that code generation does not remove the hard part. The Haskell side still needs manual wrapper code to turn raw bindings
into something ergonomic. The repo proves that the pipeline works, but it also shows that the pipeline is not yet cheap.
The main limitation is that code generation does not remove the manual work. The Haskell side still needs wrapper code to produce a usable API.
The repo shows that the pipeline works. It also shows that the workflow still has overhead.
## Pros
- The architecture is clean enough to follow.
- The examples cover realistic FFI shapes, not just trivial functions.
- The split between generated and hand-written code is sensible.
- The repo is small enough to serve as a reference implementation.
- The architecture is easy to inspect.
- The examples cover multiple FFI cases.
- The split between generated and hand-written code is reasonable.
- The repo is small enough to review quickly.
## Cons
- The build flow is manual and brittle.
- The build flow is manual.
- The setup depends on alpha-stage or pinned upstream tooling.
- The local header patch is a red flag for maintainability.
- The wrapper layer still carries noticeable manual boilerplate.
- There is no obvious sign of tests, CI, or a long-term stability plan.
- The local header patch adds maintenance risk.
- The wrapper layer still requires manual boilerplate.
- There is no visible test, CI, or long-term maintenance plan.
## Status
Current status: working prototype.
It appears far enough along to prove the interop path and demonstrate the tooling choices. It does not appear far enough along to support repeated
reuse without more work on build integration, test coverage, and dependency stability.
The repo is far enough along to prove the interop path and demonstrate the tooling choices. It is not far enough along to support repeated reuse
without more work on build integration, test coverage, and dependency stability.
In reviewer terms: promising experiment, useful reference, not ready to trust as infrastructure.
Reviewer verdict: useful experiment, not ready to treat as infrastructure.
## Ecosystem Maturity
Rust/Haskell interop is uneven.
The low-level foundation is mature enough. Haskell has had a solid FFI story for years, and Rust is comfortable exporting or importing a C ABI.
The low-level foundation is mature enough. Haskell has had an FFI story for years, and Rust can export or import a C ABI.
If the boundary is narrow and C-like, the approach is viable.
The weak part is the tooling layer. Header generation on the Rust side is serviceable. Binding generation on the Haskell side is improving, but it
The weak part is the tooling layer. Header generation on the Rust side is usable. Binding generation on the Haskell side is improving, but it
still looks early. The Garnet repo reflects that gap exactly: the ABI path works, but the workflow still needs custom glue and hand-written cleanup.
Reviewer verdict: mature enough for disciplined teams, not mature enough to feel easy.
Reviewer verdict: usable for a disciplined team, not easy.
## Calling Direction
@ -79,14 +78,14 @@ This is the easier direction.
Rust can expose a conventional C ABI, and Haskell can consume it through its normal FFI mechanisms. That keeps the ownership model and runtime story
relatively simple, at least compared with the reverse direction.
This is the path Garnet takes, and it is the right first choice.
This is the path Garnet takes. It is the better default.
### Calling Haskell From Rust
This is the harder direction.
Once Rust starts calling into Haskell, the GHC runtime becomes part of the design. That raises the complexity around runtime initialization,
threading, callbacks, shutdown, and operational correctness. It is possible, but it is a sharper tool and a worse default.
threading, callbacks, shutdown, and operational correctness. It is possible, but it is a worse default.
Reviewer verdict: call Rust from Haskell when possible; only call Haskell from Rust when there is a strong reason.
@ -100,14 +99,14 @@ Current best default:
- keep Haskell bindings thin
- write the final Haskell API by hand
That is not glamorous, but it is the most credible path today.
That is the most credible path today.
For teams that want the lowest risk, hand-written Haskell FFI bindings are still easier to trust than a large generated surface. For teams exploring
automation, `hs-bindgen` is interesting but still reads as early-stage technology.
automation, `hs-bindgen` is worth watching but is still early-stage technology.
## Practical Read On Garnet
Garnet is valuable because it shows both sides of the story.
Garnet is useful because it shows both sides of the story.
It shows that the basic interop path works. It also shows that the surrounding tooling is not yet smooth enough to disappear into the background.
That makes it a good experiment and a fair warning.
It is a useful experiment and a warning about the current workflow cost.

View File

@ -28,7 +28,7 @@ Say what still looks early, brittle, overcomplicated, missing, or high-risk.
Call out the main constraint directly: <build glue / unstable deps / missing tests / wrapper burden / runtime complexity / unclear ownership / etc.>
Keep this section short and judgmental in the useful sense. Write like a reviewer, not a tour guide.
Keep this section short. Write like a reviewer, not a tour guide.
## Pros
@ -75,3 +75,6 @@ Add only if they help:
- Prefer verdicts over summaries when the evidence is already clear.
- If something is fragile, say it is fragile.
- If something is promising but early, say that plainly.
- Do not use poetic wording.
- Avoid colorful adjectives and adverbs.
- Prefer factual wording over rhetorical wording.

View File

@ -3,8 +3,6 @@ use tracing::error;
pub fn run(args: impl IntoIterator<Item = OsString>) -> Result<(), i32> {
let _args: Vec<OsString> = args.into_iter().collect();
// Your implementation here
// Expecting at least 2 arguments
if _args.len() < 2 {
error!("Expecting at least 2 arguments");
return Err(1);

View File

@ -1,4 +1,4 @@
use template_rust_project::cli::run;
use integrations::cli::run;
fn main() {
if let Err(code) = run(std::env::args_os()) {