Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,37 @@ max-threads = 1
filter = 'test(/^cli_configuration_fixture_compiles($|::)/)'
test-group = "nested-cargo-builds"
success-output = "immediate"
# This test keeps the profile's own five periods, and deliberately so. Its
# child `cargo check` runs in the same four-slot compiler-admission pool as
# every other build on the host, so its wall clock is the queue's to decide
# rather than the test's. That wait falls inside the running test, because the
# child is awaited with `.output()` in the test body, so it counts towards this
# allowance: a wider budget would cover the delay, and would not remove the
# contention that causes it. The spread is what makes covering it unreliable.
# The same test has finished in 171.4 s on a quiet run, been terminated at the
# 300 s cap on a contended one with 3,484 "sccache server is unreachable" lines
# in that gate's log and every slot held by peer worktrees, and measured 709.2 s
# inside the test body on a cold per-run tree. The ordering below admits at most
# 720 s at 60 s periods, so widening to the largest allowance the file may hold
# would leave about 11 s against that measured cost. This budget stays at five
# periods rather than moving to an allowance the delay can still exceed.

[[profile.default.overrides]]
# `cargo publish --dry-run` builds into a fresh target directory and measured
# nearly five warning periods locally. Allow ten periods for shared-cache and
# compiler-queue delays without widening unrelated tests or adding retries.
#
# Measured against the 300 s allowance on a loaded local host, in a run that
# passed: 259.5 s, leaving 40 s of margin. It has also passed at 145 s and been
# terminated at 300.047 s on one head whose only change between the two runs
# was a word in a plan, so the spread belongs to the host rather than to the
# test. A margin of 40 s is not a budget. The failure names nextest's cap
# rather than the nested build that was slow, so the cost reads as the test's
# when it is the host's to decide. 600 s is a chosen figure inside the ordering
# rather than at its edge: the ceiling `global-timeout > largest per-test
# allowance` permits at 60 s periods is 720 s under the 780 s CI budget, and
# this value does not raise the largest allowance in the file because the
# mutation gate already sits at 600 s.
filter = 'test(/^packaged_manifest_retains_build_script_sources($|::)/)'
test-group = "nested-cargo-builds"
success-output = "immediate"
Expand Down Expand Up @@ -142,8 +168,9 @@ success-output = "immediate"
# this file bounds it and its own cargo watchdog does.
#
# The 13-minute global timeout is above the 600s largest per-test allowance,
# which is the mutation compile gate's widened budget rather than the 300s
# `[profile.default]` gives its own tests. Both comparisons hold:
# which is the widened budget the mutation compile gate and the cold package
# publish check share rather than the 300s `[profile.default]` gives its own
# tests. Both comparisons hold:
#
# global-timeout > largest per-test allowance
# 780 s > 600 s
Expand Down
14 changes: 12 additions & 2 deletions docs/adr-019-structured-command-shell-selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,18 @@ Rule and script items remain execution and stream boundaries.
Named shell blocks retain all Netsuke-managed environment, working-directory,
standard-stream, capture, temporary-directory, sequencing, and pipeline
semantics from RFC 0001 sections 10.3 to 14. Each stage resolves its own
working directory and stream bindings. Shell process state never carries to a
neighbouring structured stage.
working directory and stream bindings, and a relative stream path resolves
against that stage's resolved `cwd` exactly as it does for a direct or
`shell: true` block. Shell process state never carries to a neighbouring
structured stage.

The stream-path base, the ephemeral-versus-named capture distinction, and the
relationship between the two temporary-directory spellings are consolidated in
[ADR-043](adr-043-consolidated-structured-command-execution-contract.md), which
roadmap task 12.1.1 requires this decision to be read alongside. That
consolidation does not change the selection contract, the registry, the
lowering, or the safety model recorded above; it fixes the semantics that every
shell mode shares.

### Built-in registry

Expand Down
341 changes: 341 additions & 0 deletions docs/adr-043-consolidated-structured-command-execution-contract.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs/contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ operator, user, and contributor references are easier to find.
and a host-dependent default.
- [ADR-042](adr-042-run-the-shared-cv005-contract-checker.md): Running the
pinned shared CV-005 contract checker beside the local workflow contracts.
- [ADR-043](adr-043-consolidated-structured-command-execution-contract.md):
One structured-command execution contract across RFC 0001, RFC 0009, RFC
0010, RFC 0011, and ADR-019: the `cwd` stream-path base, absolute-`cwd`
rejection, the two capture forms, the two temporary-directory spellings, and
the reserved `1.1.0` manifest minor version.

## Proposals

Expand Down
70 changes: 49 additions & 21 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -9272,12 +9272,12 @@ fed back upstream.

All four tiers are set here.

| Tier | What it bounds | Where it is set | Current value |
| ------------------------ | ---------------------------------- | --------------------------------------------- | ----------------------------------------------------------------- |
| Per-test `slow-timeout` | one test | `.config/nextest.toml` | 300 s (60 s x 5); 600 s (60 s x 10) for the mutation compile gate |
| nextest `global-timeout` | the whole test run | `.config/nextest.toml`, `[profile.ci]` | 780 s (13 m) in CI; unset locally |
| Cargo watchdog | one `cargo` invocation, wall clock | `RUN_RUST_CARGO_WAIT_TIMEOUT` at job level | 1,800 s (30 m), armed twice per coverage step |
| Job `timeout-minutes` | the whole job | job level in `ci.yml` and `coverage-main.yml` | 90 m |
| Tier | What it bounds | Where it is set | Current value |
| ------------------------ | ---------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Per-test `slow-timeout` | one test | `.config/nextest.toml` | 300 s (60 s x 5); 600 s (60 s x 10) for the mutation compile gate and the cold package publish check |
| nextest `global-timeout` | the whole test run | `.config/nextest.toml`, `[profile.ci]` | 780 s (13 m) in CI; unset locally |
| Cargo watchdog | one `cargo` invocation, wall clock | `RUN_RUST_CARGO_WAIT_TIMEOUT` at job level | 1,800 s (30 m), armed twice per coverage step |
| Job `timeout-minutes` | the whole job | job level in `ci.yml` and `coverage-main.yml` | 90 m |

*Table: the timers that can end a run, innermost first. The watchdog is one
tier but not one window: the coverage step here passes `doctests: 'true'`, so
Expand All @@ -9297,23 +9297,51 @@ asserts it explicitly rather than leaving it implied.

There are two such figures and the larger one governs. `[profile.default]`'s own
`slow-timeout` gives five periods — 300 s — to every test no override matches,
which is almost all of them. One override widens it: the mutation compile gate
takes ten periods, 600 s, because it compiles all 18 patched trees through the
Kani frontend into a target directory no cache restores. A whole-run budget has
to sit above the largest allowance in the file, so 600 s is the figure the
ordering below is written against, not the 300 s most tests get.

That override is the targeted, written-rationale case `.config/nextest.toml`'s
own policy asks for, and it is worth reading as the worked example of it. The
gate passed twice at 257.7 s and 261.5 s and then failed at 300.008 s on a head
whose only diff was eight lines of Markdown. The code was not slow; the cap was
too near the cost, and a margin of 38 s is the difference between one runner
and another rather than a budget. The widened allowance is a chosen figure
inside the ordering rather than at its edge: what
which is almost all of them. Two overrides widen it to ten periods, 600 s: the
mutation compile gate, which compiles all 18 patched trees through the Kani
frontend into a target directory no cache restores, and
`packaged_manifest_retains_build_script_sources`, whose
`cargo publish --dry-run` verification build lands in a nested target directory
whatever `CARGO_TARGET_DIR` it is given and so inherits nothing from `target/`.
A whole-run budget has to sit above the largest allowance in the file, so 600 s
is the figure the ordering below is written against, not the 300 s most tests
get.

The second of those is scoped to one test deliberately, and the distinction is
about what a budget can buy rather than about how slow the two look. Their
sibling `cli_configuration_fixture_compiles` spawns a nested Cargo build too
and shares the same `test-group`, but its child `cargo check` runs in the same
four-slot compiler-admission pool as every other build on the host, so its wall
clock is decided by the queue rather than by the build. The child is awaited
with `.output()` inside the test body, so that wait counts towards the test's
allowance and a wider budget would cover it; a wider budget would not remove
the contention that produces it. What makes covering it unreliable is the
spread: the same test has finished in 171.4 s on a quiet run, been terminated
at the 300 s cap on a contended one with the sccache server unreachable and
every slot held by peer worktrees, and measured 709.2 s inside the test body on
a cold per-run tree. The ordering caps an allowance at 720 s, which is roughly
11 s above that measured cost, so widening would buy a margin too thin to rely
on. Sharing a serialization group is a statement about resource contention, not
about budget; the two were briefly treated as one case and are not one.

Those overrides are the targeted, written-rationale case
`.config/nextest.toml`'s own policy asks for, and the mutation gate is worth
reading as the worked example of it. The gate passed twice at 257.7 s and 261.5
s and then failed at 300.008 s on a head whose only diff was eight lines of
Markdown. The code was not slow; the cap was too near the cost, and a margin of
38 s is the difference between one runner and another rather than a budget. The
packaging smoke test failed the same way and for the same reason: it runs
`cargo publish --dry-run`, whose verification build lands in a nested target
directory whatever `CARGO_TARGET_DIR` it is given, and it has passed at 145 s
and timed out at 300.047 s on this host on one head whose only change between
the two runs was a word in a plan, with load average the rest of the
difference. Against the 300 s allowance it measured 259.5 s in the gate run
that passed, 40 s of margin. The widened allowance is a chosen figure inside
the ordering rather than at its edge: what
`global-timeout > largest per-test allowance` permits at 60 s periods is up to
720 s under the 780 s budget, so the 600 s leaves 180 s of margin and the two
tiers still bound each other. That override binds only because its filter
carries the test's module path; for a period it did not, and the two passing
tiers still bound each other. Each override binds only because its filter
carries the test's module path; for a period one did not, and the two passing
runs above were read as evidence the widened allowance was in force when both
sit inside the 300 s default anyway. See "nextest configuration" for the rule.

Expand Down
Loading
Loading