Skip to content

Commit 9bf1573

Browse files
Merge pull request #3 from pasunboneleve/dev
Prepare 0.8.0 watcher reliability release
2 parents ce43171 + 8256420 commit 9bf1573

20 files changed

Lines changed: 902 additions & 95 deletions

.beads/issues.jsonl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{"id":"devloop-0nx","title":"Define project docs, AGENTS, and roadmap","description":"Add README, PLAN, and repository-specific AGENTS guidance for the standalone tool.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T01:27:27Z","closed_at":"2026-03-24T01:27:27Z","close_reason":"Closed"}
22
{"id":"devloop-8km","title":"Implement config and workflow engine","description":"Load config, watch paths, classify events, and execute ordered workflows against named processes and hooks.","status":"closed","priority":2,"issue_type":"feature","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-8km","depends_on_id":"devloop-0nx","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]}
3+
{"id":"devloop-c6l","title":"Polish 0.8.0 release docs and test ergonomics","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T07:12:58Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T07:20:27Z","closed_at":"2026-04-08T07:20:27Z","close_reason":"Closed"}
34
{"id":"devloop-d81","title":"Move real client config out of devloop examples","description":"Keep only generic examples in devloop and move the working blog config into the client repository root.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:31:27Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:39:07Z","closed_at":"2026-03-24T02:39:07Z","close_reason":"Kept only generic examples in devloop and moved the working blog config into the client repo root."}
45
{"id":"devloop-dzy","title":"Make client hook paths repo-relative","description":"Resolve devloop config command paths relative to the client repo or config, not the tool checkout.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:16:34Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:22:17Z","closed_at":"2026-03-24T02:22:17Z","close_reason":"Closed"}
56
{"id":"devloop-mml","title":"Address roborev findings on state ownership and client-specific URL composition","status":"closed","priority":1,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:44:51Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:53:31Z","closed_at":"2026-03-24T02:53:31Z","close_reason":"Shared session state is now owned in memory, generic state templating replaced blog-specific derivation, redundant writes are skipped, and the review job was addressed."}
67
{"id":"devloop-nmu","title":"Add blog client example and verification","description":"Create example config/hooks for the blog repo and verify the tool runs against it.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-nmu","depends_on_id":"devloop-8km","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]}
78
{"id":"devloop-s2h","title":"Bootstrap configurable dev-loop engine MVP","description":"Create a standalone Rust CLI in /tmp/devloop with config-driven file watching, process supervision, workflows, hooks, and documentation. Use the blog repo as the first client.","status":"open","priority":2,"issue_type":"epic","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:14:54Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T01:14:54Z","dependencies":[{"issue_id":"devloop-s2h","depends_on_id":"devloop-nmu","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]}
89
{"id":"devloop-ufx","title":"Add client adapter for dynamic tunnel url consumption","description":"The blog app still treats SITE_URL as startup-only state. Add a client-side integration pattern that reads devloop state dynamically so tunnel restarts affect rendered metadata.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:27:27Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed"}
10+
{"id":"devloop-uog","title":"Add watcher regression smoke test and poll backend fallback","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T05:45:33Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T06:46:22Z","closed_at":"2026-04-08T06:46:22Z","close_reason":"Closed"}
911
{"id":"devloop-vxg","title":"Support process-emitted state updates","description":"Long-running processes such as cloudflared need a first-class way to publish readiness and state into the engine instead of relying on wrapper scripts mutating the session file.","status":"closed","priority":2,"issue_type":"feature","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:27:27Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed"}
1012
{"id":"devloop-w34","title":"Support workflow composition to reduce repeated setup steps","description":"Add a generic way for one workflow to reuse another so client configs can avoid duplicating repeated step sequences such as wait-for-tunnel plus templated write_state composition.","status":"closed","priority":3,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:53:31Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T03:01:13Z","closed_at":"2026-03-24T03:01:13Z","close_reason":"Added reusable run_workflow steps, validated nested workflow recursion and missing references, and updated the generic example."}
13+
{"id":"devloop-y2l","title":"Fix repeated literal file edit watch flakiness","description":"A Rust integration smoke test now reproduces missed workflow triggers during repeated edits to the same watched file. Fix the watch pipeline so the smoke test passes deterministically without sleeps.","status":"closed","priority":1,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T06:23:10Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T06:46:22Z","closed_at":"2026-04-08T06:46:22Z","close_reason":"Closed"}

AGENTS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ without hard-coding knowledge of any one repository.
1818
avoid baking it into the core.
1919
- Do not push unless explicitly asked. This repository may not even have a
2020
remote during early development.
21+
- Do not use `sleep` to resolve races. Races must be resolved with
22+
deterministic logic, explicit readiness signals, or ordered state
23+
transitions.
24+
- When tests must mutate process-global state such as environment
25+
variables, isolate the unsafe operation in a small helper, serialize
26+
access with a lock, and document the safety rationale instead of
27+
scattering raw unsafe calls through test bodies.
2128
- Run quality gates for code changes: `cargo fmt`, `cargo test`,
2229
`cargo clippy --all-targets --all-features -- -D warnings`.
2330

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@ All notable changes to `devloop` will be recorded in this file.
44

55
## [Unreleased]
66

7+
## [0.8.0] - 2026-04-08
8+
9+
### Added
10+
- Added a configurable watcher backend with non-breaking `native`
11+
default behavior plus a `poll` fallback mode for environments where
12+
native filesystem notifications are unreliable.
13+
- Added a Rust repeated-edit watch flake smoke test that can be run
14+
locally with `DEVLOOP_RUN_WATCH_FLAKE_SMOKE=1 cargo test --test
15+
watch_flake_smoke -- --nocapture`.
16+
- Added explicit trailing-slash syntax for literal directory watch
17+
targets, for example `content/`, so recursive directory intent is
18+
preserved even when the directory does not yet exist at startup.
19+
- Added a development guide under [`docs/development.md`](docs/development.md)
20+
and exposed it in the CLI as `devloop docs development`.
21+
22+
### Changed
23+
- `devloop` now derives concrete watch targets from configured watch
24+
patterns and asks the backend to watch only those files or
25+
directories instead of always watching the whole repository root.
26+
- The watch flake smoke test is now opt-in instead of running during
27+
every default `cargo test` or CI run. The existing runtime smoke test
28+
remains in CI.
29+
30+
### Fixed
31+
- Native watch registration now resolves file and directory targets at
32+
runtime, so startup no longer depends on those paths already existing
33+
when config is parsed.
34+
- Fixed a real watch flake where the debounce batch could be dropped if
35+
another `tokio::select!` branch won the race while filesystem events
36+
were already buffered.
37+
- Test-only environment mutation now lives behind locked helpers with
38+
documented safety rationale instead of scattered raw unsafe blocks.
39+
740
## [0.7.0] - 2026-03-27
841

942
### Added

Cargo.lock

Lines changed: 40 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "devloop"
3-
version = "0.7.0"
3+
version = "0.8.0"
44
edition = "2024"
55

66
[dependencies]
@@ -21,3 +21,6 @@ toml = "0.8.22"
2121
tracing = "0.1.41"
2222
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt"] }
2323
unicode-width = "0.2"
24+
25+
[dev-dependencies]
26+
tempfile = "3.20.0"

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ Built-in reference docs are also available from the CLI:
100100
```bash
101101
devloop docs config
102102
devloop docs behavior
103+
devloop docs development
103104
devloop docs security
104105
```
105106

@@ -186,6 +187,9 @@ For the runtime behavior reference, see
186187
For the full configuration reference, see
187188
[`docs/configuration.md`](docs/configuration.md).
188189

190+
For local contributor workflow details, including the opt-in watch
191+
flake smoke test, see [`docs/development.md`](docs/development.md).
192+
189193
For the external-event trust model and push-versus-polling tradeoffs,
190194
see [`docs/security.md`](docs/security.md).
191195

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- [Behavior Reference](behavior.md)
44
- [Configuration Reference](configuration.md)
5+
- [Development Guide](development.md)
56
- [Security Notes](security.md)
67

78
This directory holds detailed reference material for `devloop`.

docs/behavior.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,20 @@ merged back into the live session.
4949

5050
## Watching and debounce
5151

52-
`devloop` watches the configured `root` recursively.
52+
`devloop` derives concrete filesystem watch targets from the configured
53+
watch-group patterns and watches only those files or directories.
5354

5455
- Only relevant file-system events are considered.
5556
- Events are batched for `debounce_ms`.
5657
- Matching changes are grouped by workflow name before execution.
5758
- Each workflow receives the set of changed relative paths that matched
5859
it during the debounce window.
60+
- The default backend uses native filesystem notifications. A polling
61+
backend can be selected in config as a fallback for environments
62+
where native events are unreliable.
63+
- Literal file targets are watched as narrowly as the backend allows.
64+
Use a trailing `/` in the config when you mean an explicit directory
65+
target that should be watched recursively.
5966

6067
If multiple watch groups map to the same workflow, their matched paths
6168
are merged for that workflow run.

docs/configuration.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ startup_workflows = ["startup"]
2626
- `startup_workflows`: workflows to run after autostart processes have
2727
been started.
2828

29+
Optional watcher backend config:
30+
31+
```toml
32+
[watcher]
33+
kind = "native"
34+
poll_interval_ms = 250
35+
```
36+
37+
- `watcher.kind`: watcher backend to use. `native` is the default and
38+
uses the platform-recommended `notify` backend. `poll` uses
39+
`notify`'s polling watcher as a fallback for environments where
40+
native filesystem events are unreliable.
41+
- `watcher.poll_interval_ms`: polling interval used when
42+
`watcher.kind = "poll"`. Default: `250`.
43+
2944
Optional browser reload server config:
3045

3146
```toml
@@ -41,16 +56,26 @@ bind = "127.0.0.1:0"
4156
Watch groups map file patterns to workflows.
4257

4358
```toml
44-
[watch.rust]
45-
paths = ["src/**/*.rs", "Cargo.toml"]
46-
workflow = "rust"
59+
[watch.content]
60+
paths = ["content/", "templates/**/*.html"]
61+
workflow = "content"
4762
```
4863

4964
- Table name: the watch-group name.
5065
- `paths`: glob patterns evaluated relative to `root`.
66+
Use a trailing `/` for a literal directory target that should be
67+
watched recursively, including when the directory may not exist yet
68+
at startup. Without the trailing slash, a literal path is treated as
69+
a file target.
5170
- `workflow`: workflow to run when a matching file changes. If omitted,
5271
the watch-group name is used as the workflow name.
5372

73+
`devloop` derives concrete watch targets from these patterns and asks
74+
the backend to watch only those literal files or directories instead of
75+
always watching the whole repository root recursively. `native` remains
76+
the default backend; `poll` exists as a fallback for environments where
77+
filesystem notifications are unreliable.
78+
5479
## Processes
5580

5681
Processes are long-running commands supervised by `devloop`.

docs/development.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Development Guide
2+
3+
This guide covers local development workflows for `devloop` itself.
4+
5+
## Quality gates
6+
7+
Run the standard checks before committing:
8+
9+
```bash
10+
cargo fmt
11+
cargo test
12+
cargo clippy --all-targets --all-features -- -D warnings
13+
./scripts/ci-smoke.sh
14+
```
15+
16+
`./scripts/ci-smoke.sh` is the fast runtime smoke test used in CI. It
17+
checks that `devloop run` can start, begin watching, react to one file
18+
change, and shut down cleanly.
19+
20+
## Opt-in watch flake smoke
21+
22+
The repeated-edit watch flake smoke test is intentionally opt-in. It is
23+
useful when changing watch registration, debounce logic, or event
24+
delivery, but it is slower and more environment-sensitive than the
25+
standard test suite.
26+
27+
Run it locally with:
28+
29+
```bash
30+
DEVLOOP_RUN_WATCH_FLAKE_SMOKE=1 cargo test --test watch_flake_smoke -- --nocapture
31+
```
32+
33+
Without that environment variable, the test exits early so normal
34+
`cargo test` and CI runs stay fast.
35+
36+
## Test policy
37+
38+
When a test must mutate process-global state such as environment
39+
variables:
40+
41+
- serialize access with a test-local lock
42+
- keep `unsafe` in a narrow helper
43+
- document the safety rationale at the helper
44+
45+
Do not scatter raw `unsafe { std::env::set_var(...) }` calls across test
46+
bodies.

0 commit comments

Comments
 (0)