-
Notifications
You must be signed in to change notification settings - Fork 0
Support serial dependency ordering (3.14.3) (#552) #557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
leynos
merged 69 commits into
main
from
issue-552-support-serial-dependency-ordering-for-actions-and-targets
Aug 18, 2026
Merged
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
cf7e200
Ignore `.vtcode` workspace metadata
leynos 805a825
Plan serial dependency ordering with dyndep (#552)
leynos 5a859ab
Add serial dependency_order contract to AST and IR (#552)
leynos e45b2b9
Generate Ninja dyndep bundles for serial dependency ordering (#552)
leynos 173a415
Materialize dyndep sidecars in every runner path (#552)
leynos 75e6bed
Group dyndep staging state in one struct
leynos 6da0a81
Harden serial dyndep implementation checks (#552)
leynos 1e1f3b7
Use capabilities in dyndep materializer tests (#552)
leynos e98a7e8
Record serial dependency validation progress (#552)
leynos 10232ea
Document serial dependency ordering (#552)
leynos 7e74106
Record serial dependency review completion
leynos fecb169
Deduplicate dyndep staging assertions
leynos eb4ff59
Record dyndep test refactor validation
leynos b7bf0a2
Linearize dyndep atomic sidecar writes
leynos da80d2c
Record atomic dyndep refactor validation
leynos cea4c43
Split dyndep bundle edge rendering
leynos d98e618
Record bundle rendering refactor validation
leynos 5d59097
Skip empty dyndep materialization (#552)
leynos e73d3c3
Harden dyndep temporary-file creation (#552)
leynos 5fd927e
Cover serial dependency runtime guarantees (#552)
leynos c30e649
Separate dyndep generation from publication (#552)
leynos c0ff02c
Record serial ordering review evidence (#552)
leynos 8c94c44
Resolve serial ordering review findings (#552)
leynos add0fdb
Reject Ninja control characters in paths (#552)
leynos 572ca0a
Close serial dyndep review gaps (#552)
leynos ff6ab6a
Refresh serial dyndep documentation
leynos 1333506
Cover serial CLI publication paths (#552)
leynos 3e6387f
Record final serial dyndep review (#552)
leynos 1f07837
Diagram serial dyndep publication flow (#552)
leynos d6d50ce
Branch Ninja command flow in design diagram
leynos 0d57c3d
Harden serial dyndep publication and rendering (#552)
leynos 84f73ee
Record final serial dyndep review evidence (#552)
leynos dcaa8f1
Renumber serial dyndep decision after rebase (#552)
leynos d5070ea
Record final rebase validation evidence (#552)
leynos 59b7612
Correct serial dyndep diagram boundary
leynos b21c74b
Remove stale dyndep race diagnostic (#552)
leynos 8f78bf4
Correct serial dyndep locale wording
leynos d416476
Correct serial dependency documentation
leynos 6937138
Remove redundant serial edge key clone (#552)
leynos 1c7e5e3
Document bounded dyndep sidecar retention (#552)
leynos f69b936
Bound generated dyndep sidecar retention (#552)
leynos 9ae52a3
Document conditional Ninja version floor (#552)
leynos 988201c
Correct locale translations for dyndep errors (#552)
leynos a89cccc
Correct dyndep documentation and translations
leynos 5642e63
Test cross-process dyndep publication leases (#552)
leynos f2da1cd
Bound dyndep retention scan memory (#552)
leynos ce43fd5
Simplify retention telemetry assertions (#552)
leynos 1c0fc3a
Share retention publication test setup (#552)
leynos d45dc6f
Bound dyndep retention candidate memory (#552)
leynos d05b35f
Strengthen dyndep retention validation (#552)
leynos d7cf1c1
Integrate serial dyndep after rebase (#552)
leynos 73aa9ec
Fix dyndep retention ADR date (#552)
leynos 37ce330
Share command-list integration setup (#552)
leynos 3b03227
Clarify migration and locale wording
leynos b039fea
Harden serial generation review coverage (#552)
leynos 62a64d6
Centralize dyndep namespace ownership (#552)
leynos 0f9e1bb
Correct dyndep rename translations (#552)
leynos d70702e
Document dependency ordering locale fixes
leynos 8c4fd69
Harden serial dyndep review paths (#552)
leynos 0e14fb1
Simplify dyndep retention traversal (#552)
leynos a3b98f6
Clarify serial dyndep documentation
leynos d938bfc
Clarify staged sidecar ownership (#552)
leynos c5cea71
Stabilize dyndep retention selection (#552)
leynos 0a8776f
Document fs4 retention lock dependency
leynos d01ea95
Repair rebased serial-dyndep review fixes (#552)
leynos 37f7dda
Clarify serial-dyndep contracts (#552)
leynos 783734e
Reuse validated spelling cache on HTTP 429
leynos 7bdd97a
Correct serial-dyndep review details (#552)
leynos 05079a9
Clarify Russian dyndep directory path (#552)
leynos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
docs/adr-011-use-ninja-dyndep-for-serial-dependency-ordering.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| # Architecture decision record (ADR): Use Ninja dyndep for serial dependency ordering | ||
|
|
||
| ## Status | ||
|
|
||
| Accepted. | ||
|
|
||
| ## Date | ||
|
|
||
| 2026-08-11 | ||
|
|
||
| ## Context and problem statement | ||
|
|
||
| Manifest authors need an explicit way to run the direct `deps` of an action or | ||
| target in declaration order. The existing Ninja dependency classes preserve | ||
| freshness and scheduling constraints, but ordinary implicit dependencies are | ||
| all visible to the scheduler and may run concurrently. | ||
|
|
||
| The implementation must retain a single Ninja invocation so a shared dependency | ||
| runs once, propagate a failing early dependency to stop later work through the | ||
| annotated path, and leave unrelated branches available for normal concurrent | ||
| scheduling. It must also leave the Intermediate Representation (IR) | ||
| backend-agnostic and make generated Ninja output executable in every command | ||
| path. | ||
|
|
||
| ## Decision | ||
|
|
||
| **Y-statement:** In the context of declaration-ordered direct dependencies, | ||
| and facing the forces of shared-work reuse, failure short-circuiting, | ||
| unrelated-branch concurrency, backend-neutral IR, and executable generated | ||
| output, we decided to use staged Ninja dyndep sidecars for | ||
| `dependency_order: serial`, accepting the Ninja 1.10 floor, reserved generated | ||
| state beneath `.netsuke`, and a path-scoped ordering guarantee. | ||
|
|
||
| In the context of a `dependency_order: serial` manifest `deps` list, Netsuke | ||
| will use staged Ninja dyndep sidecars to reveal one direct dependency at a time | ||
| and will materialize those sidecars atomically beneath `.netsuke/dyndep`. The | ||
| runner materializes every sidecar file before Ninja starts; no Ninja edge | ||
| produces sidecar content. | ||
|
|
||
| `dependency_order` is a closed `parallel`/`serial` enum on the shared action | ||
| and target AST shape. It is copied to `BuildEdge`, where it remains a logical | ||
| graph annotation. Only the Ninja generator lowers a serial list containing two | ||
| or more direct dependencies into synthetic phony gates beneath | ||
| `.netsuke/serial` and content-addressed dyndep sidecars beneath | ||
| `.netsuke/dyndep`. | ||
|
|
||
| The gate edge associated with the next sidecar depends on the preceding gate, | ||
| so each later direct dependency remains unavailable until earlier work | ||
| succeeds. | ||
|
|
||
| The main generated build file declares `ninja_required_version = 1.10` only | ||
| when staged serial lowering is present. The generator exposes a complete bundle | ||
| containing main-file text and every required sidecar. String-only generation | ||
| rejects a graph requiring sidecars instead of returning an incomplete file. | ||
|
|
||
| The serial guarantee is deliberately path-scoped: each direct dependency in the | ||
| annotated list becomes schedulable only after its predecessor succeeds. A later | ||
| dependency independently reachable through another requested path remains free | ||
| to run through that other path. | ||
|
|
||
| ## Rationale | ||
|
|
||
| - **One scheduler preserves shared work.** The generated gates stay inside one | ||
| Ninja invocation, so Ninja continues to deduplicate a repeated or diamond | ||
| dependency. | ||
| - **Dyndep controls visibility.** A later real dependency is absent from the | ||
| relevant graph path until its sidecar is revealed, unlike an order-only edge | ||
| whose transitive inputs are already visible to Ninja. | ||
| - **The IR remains portable.** Gates, sidecar paths, and Ninja version syntax | ||
| are backend mechanics rather than manifest graph concepts. | ||
| - **Bundle ownership prevents incomplete output.** Treating sidecars as part of | ||
| the generated artefact makes every runner path materialize them before Ninja | ||
| loads the main file. | ||
| - **Content addressing makes state reusable.** Existing matching sidecars are | ||
| safely reused; mismatching content is corruption and is reported rather than | ||
| overwritten. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Serial lists with zero or one dependency use ordinary Ninja lowering; no | ||
| relative order needs enforcing and no dyndep version floor is emitted. | ||
| - User graph paths in outputs, inputs, implicit dependencies, and order-only | ||
| dependencies cannot use `.netsuke/serial` or `.netsuke/dyndep`, because those | ||
| names are reserved generated state. | ||
| - `build`, `clean`, and `generate` each materialize sidecars relative to the | ||
| effective Ninja working directory. `clean` may leave the immutable, | ||
| content-addressed sidecars in place. | ||
| - `src/ninja_gen/dyndep.rs` owns staging and naming. The command-boundary | ||
| module `src/runner/dyndep_publication.rs` opens the effective capability and | ||
| orchestrates publication and retention; `src/runner/process/dyndep_files.rs` | ||
| owns atomic sidecar writes and verification, while | ||
| `src/runner/process/dyndep_retention.rs` owns the lease and cleanup. Neither | ||
| side of the boundary may broaden the path-scoped guarantee with a global | ||
| scheduler. | ||
| - Tests must continue to use real Ninja for ordered starts, failure | ||
| short-circuiting, shared-work reuse, and unrelated-branch concurrency. | ||
|
|
||
| ## Alternatives considered | ||
|
|
||
| ### Order-only phony gate chain | ||
|
|
||
| Rejected. Ninja eagerly schedules already-visible transitive inputs, so an | ||
| order-only chain can order gate completion without preventing later real | ||
| dependencies from starting early. | ||
|
|
||
| ### Ninja pool with depth one | ||
|
|
||
| Rejected. A pool provides mutual exclusion, not declaration order, and would | ||
| serialize unrelated work outside the annotated dependency list. | ||
|
|
||
| ### Recursive Ninja or Netsuke invocation per dependency | ||
|
|
||
| Rejected. Separate child schedulers lose the enclosing build's memoization and | ||
| can execute a shared dependency more than once. | ||
|
|
||
| ### A Netsuke-owned global scheduler | ||
|
|
||
| Rejected for this feature. It would change the execution architecture and | ||
| global reachability semantics rather than implement the requested scoped | ||
| manifest policy. It requires a separately approved design. | ||
|
|
||
| ## Implementation references | ||
|
|
||
| - Manifest and IR contract: [`src/ast/mod.rs`](../src/ast/mod.rs), | ||
| [`src/ir/graph.rs`](../src/ir/graph.rs), and | ||
| [`src/ir/from_manifest.rs`](../src/ir/from_manifest.rs) | ||
| - Ninja bundle generation: | ||
| [`src/ninja_gen/dyndep.rs`](../src/ninja_gen/dyndep.rs) | ||
| - Atomic sidecar materialization: | ||
| [`src/runner/process/dyndep_files.rs`](../src/runner/process/dyndep_files.rs) | ||
| - User contract: [user's guide](users-guide.md#run-direct-dependencies-serially) | ||
| - Implementation history: | ||
| [issue #552 ExecPlan](execplans/issue-552-support-serial-dependency-ordering-for-actions-and-targets.md) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # Architecture decision record (ADR): Bound dyndep sidecar retention | ||
|
|
||
| ## Status | ||
|
|
||
| Accepted. | ||
|
|
||
| ## Date | ||
|
|
||
| 2026-08-15 | ||
|
|
||
| ## Context and problem statement | ||
|
|
||
| Serial dependency ordering uses immutable, content-addressed Ninja dyndep | ||
| sidecars beneath `.netsuke/dyndep`. A changed manifest therefore produces new | ||
| `.dd` files without changing or overwriting files that an existing generated | ||
| Ninja manifest references. Without a cleanup policy, obsolete sidecars would | ||
| accumulate indefinitely. Cleanup must also avoid removing a sidecar that a | ||
| concurrent Netsuke command is still consuming. | ||
|
|
||
| The policy must preserve every sidecar in the current generated bundle, | ||
| remove stale temporary files left by interrupted atomic writes, and bound | ||
| obsolete storage deterministically. It must define the failure boundary for | ||
| `clean` and make the compatibility consequence of retaining an old | ||
| `generate --output` manifest explicit. | ||
|
|
||
| ## Decision | ||
|
|
||
| Netsuke will retain immutable, content-addressed dyndep sidecars. Each | ||
| sidecar-capable `build`, `generate`, or `clean` command materializes every | ||
| sidecar in its current bundle before writing or invoking the generated Ninja | ||
| file. Publication and cleanup use a capability-scoped, exclusive lease for | ||
| the `.netsuke/dyndep` directory. The lease remains held through Ninja | ||
| consumption for `build` and `clean`, or through generated-output consumption | ||
| for `generate`. | ||
|
|
||
| While that lease is held, Netsuke removes stale `.tmp` files and applies the | ||
| following deterministic policy to obsolete `.dd` files: | ||
|
|
||
| - every sidecar in the current bundle is retained; | ||
| - at most 32 obsolete `.dd` files are retained; and | ||
| - at most 1 MiB of obsolete `.dd` bytes is retained. | ||
|
|
||
| Obsolete files are considered in deterministic path order. A sidecar's | ||
| content is never changed in place. `build` and `generate` prune after | ||
| materialization. `clean` prunes only after `ninja -t clean` succeeds; a failed | ||
| clean does not prune sidecars. | ||
|
|
||
| ## Rationale | ||
|
|
||
| - **Content addressing preserves active bundles.** A matching sidecar can be | ||
| reused and a mismatching file is corruption, not permission to overwrite | ||
| it. | ||
| - **The lease protects consumption.** Publication, temporary-file cleanup, | ||
| and pruning share one directory lease, so cleanup cannot remove files while | ||
| another serial command is using its bundle. | ||
| - **Fixed budgets are predictable.** File-count and byte limits provide a | ||
| bounded cache without relying on filesystem timestamps or an age-based | ||
| policy. | ||
| - **`clean` keeps failure evidence.** Deferring cleanup until successful | ||
| `ninja -t clean` avoids deleting historical state when the requested clean | ||
| did not complete. | ||
|
|
||
| ## Consequences | ||
|
|
||
| An old arbitrary manifest written by `generate --output` may lose referenced | ||
| sidecars after a later Netsuke command applies retention. Such a manifest | ||
| must be regenerated before use when its sidecars have been pruned. Generated | ||
| manifests should therefore be treated as command outputs paired with the | ||
| current sidecar cache, not as permanently self-contained artefacts. | ||
|
|
||
| The policy does not use sidecar age, and it does not make sidecars mutable. | ||
| There is no guarantee that an obsolete sidecar remains available merely | ||
| because its manifest was generated successfully in an earlier command. | ||
|
|
||
| ## Alternatives considered | ||
|
|
||
| ### Retain sidecars by age | ||
|
|
||
| Rejected. Wall-clock age is not deterministic and does not bound storage. | ||
|
|
||
| ### Keep every content-addressed sidecar | ||
|
|
||
| Rejected. Immutable files would accumulate without bound as manifests change. | ||
|
|
||
| ### Mutate or overwrite existing sidecars | ||
|
|
||
| Rejected. A content-addressed path must continue to identify one byte | ||
| sequence, and overwriting it could change the graph seen by an existing | ||
| manifest. | ||
|
|
||
| ### Prune without a directory lease | ||
|
|
||
| Rejected. Publication and cleanup could race with a command that is consuming | ||
| the current bundle, removing a required sidecar between materialization and | ||
| Ninja or output consumption. | ||
|
|
||
| ## Implementation references | ||
|
|
||
| - Runner publication boundary: | ||
| [`src/runner/dyndep_publication.rs`](../src/runner/dyndep_publication.rs) | ||
| - Atomic sidecar materialization: | ||
| [`src/runner/process/dyndep_files.rs`](../src/runner/process/dyndep_files.rs) | ||
| - Retention and lease implementation: | ||
| [retention implementation](../src/runner/process/dyndep_retention.rs) | ||
| - User contract: [user's guide](users-guide.md#run-direct-dependencies-serially) | ||
| - Serial dyndep architecture: | ||
| [ADR-011](adr-011-use-ninja-dyndep-for-serial-dependency-ordering.md) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.