Skip to content
Closed
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
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,11 @@ jobs:
self_host_ratchet:
if: github.event.pull_request.draft != true
runs-on: ${{ vars.CI_RUNNER || 'ubuntu-latest' }}
timeout-minutes: 30
# Cold ubuntu-latest can spend ~20–23m on the first `--release` v3-compiler
# compile inside `determinism_test`, then `self_host_fixed_point` touches the
# same crate graph again — 30m wall leaves no headroom (job cancellation mid-
# compile). Align headroom with the `v3` job budget.
timeout-minutes: 60
needs: [v3]
continue-on-error: true
steps:
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,8 @@ Tracked debt not dispatched with the 2026-04-21 lane wave (Lane 1e P3.0, SG-2c-4
- **Lane E-I — port `SubValueRelation` + inductive fields + cost algebra (M)**: port Tier 1 (structural) + Tier 2 (lattice) + Tier 3 (`CostBound` + master theorem). Requires E-T, E-C. Pre-flight: verify v3 lowers `CostBound`'s self-referential-through-`List` shape. Acceptance: carrier types ported with round-trip tests; no lens row moves on E-I alone because consumer-visible facts still wait for E-P producer/consumer wiring.
- **Lane E-P — per-call descent-evidence provenance (M)**: first P-c side-table producer slice has landed as `v3_compiler::dag::per_call_descent_evidence` (recursive self-call / arithmetic descent; unproven edges fail closed; `TransformNode` stays unwidened). v2 stores this class of fact on `ExprCall.descent_evidence` (`src/v2/00_core.dag:199`); v3 derives it off-substrate for now. Requires E-T, E-C, E-I. Remaining acceptance: cost/complexity lenses consume the side table, plus a v2-oracle-vs-v3 cementing test against `expr_call_descent_evidence`; carrier parity for `cost.dag` / `complexity.dag` on the non-method-dispatch slice closes there.
- **Lane E-M — `MethodSemantics` port-or-subsume (S–M)**: **closed via M-b structural subsumption.** Transitive facts (`CollectionSizeEffect` / `CostShape` / `AlgebraFieldTemplate`) remain algebra-declaration facts; v3 method-like calls dispatch through `TransformTarget::{Callable, FieldProject, Operator}` and typed declaration/effect metadata. No `src/v3/std/method_semantics.dag`, no Rust mirror, and no `ExprMethodCall` attachment point.
- **CI ratchet architecture — PARTIAL (T-Receipts W1 second bundle)**: surfaced during the 2026-04-21 reflective analysis (`53b3110..ae8825a` range). Commits `37cd6128`, `4898983e`, `f84ed355` hardened the ratchet against CI-log instability, but `2d8396df` widened the exemption list. `scripts/slow-test-exemptions.txt` currently has **39** active non-comment exemptions; `scripts/check-test-timeout.sh` meta-ratchet default `TEST_TIMEOUT_MAX_EXEMPTIONS` matches that count. **Landed partial:** classification audit [docs/debt/ci-ratchet-exemption-audit-2026-04-24.md](docs/debt/ci-ratchet-exemption-audit-2026-04-24.md) (2026-04-24 baseline) plus follow-up routing audit [docs/debt/ci-ratchet-exemption-audit-2026-05-02.md](docs/debt/ci-ratchet-exemption-audit-2026-05-02.md) (bounded scope — not a completed Phase 1 per-test timing pass). **Still open:** fresh CI-shaped per-test timing audit, stale exemption deletion if any entries measure under 2s, and per-exempt duration budgets once fresh timings exist. Dispatch brief: `docs/briefs/ci-ratchet-architecture-audit.md`.
- **`IntegrationRsScan` / `integration_rs_active_line_contains` in `src/v3/compiler/tests/integration/common/mod.rs` is a latent source of byte-constant workarounds in downstream tests** (T-Receipts, surfaced by T-Demo #686). The scanner is "deliberately narrow" and does not model Rust character literals, so any test author touching a file in its scan path cannot write `b'\\'` / `b'"'` directly and must substitute numeric byte constants (e.g., `92` / `34`) instead. Historical evidence: #686's text-slicing helper introduced `DAG_ESCAPE_BYTE` / `DAG_QUOTE_BYTE` constants for exactly this reason; #705 deleted the helper when `TestRunner::run_suite` obsoleted it, and the constants went with it — so there is **no surviving in-tree instance of the workaround today**. The scanner constraint itself remains, which means the next test author landing in the scan path will recreate the pattern. Dissolution: either widen the scanner to model Rust char literals, or replace its scan with a structural reader that doesn't need to exclude char-literal syntax. Small scope; cosmetic for now, but the constraint is a workaround attractor. Owner: unassigned; P2 Boundary Discipline / T-Receipts bundle.
- **CI ratchet architecture — PARTIAL (T-Receipts W1 second bundle)**: surfaced during the 2026-04-21 reflective analysis (`53b3110..ae8825a` range). Commits `37cd6128`, `4898983e`, `f84ed355` hardened the ratchet against CI-log instability, but `2d8396df` widened the exemption list. `scripts/slow-test-exemptions.txt` currently has **40** active non-comment exemptions; `scripts/check-test-timeout.sh` meta-ratchet default `TEST_TIMEOUT_MAX_EXEMPTIONS` matches that count. **Landed partial:** classification audit [docs/debt/ci-ratchet-exemption-audit-2026-04-24.md](docs/debt/ci-ratchet-exemption-audit-2026-04-24.md) (2026-04-24 baseline) plus follow-up routing audit [docs/debt/ci-ratchet-exemption-audit-2026-05-02.md](docs/debt/ci-ratchet-exemption-audit-2026-05-02.md) (bounded scope — not a completed Phase 1 per-test timing pass). **Still open:** fresh CI-shaped per-test timing audit, stale exemption deletion if any entries measure under 2s, and per-exempt duration budgets once fresh timings exist. Dispatch brief: `docs/briefs/ci-ratchet-architecture-audit.md`.
- **`IntegrationRsScan` / `integration_rs_active_line_contains` in `src/v3/compiler/tests/integration/common/mod.rs` — PARTIAL (T-Receipts; char-literal slice closed PR #1791).** Historical context (#686 / #705): the scanner was narrow enough that authors on its scan path avoided `b'\\'` / `b'"'` in favor of numeric byte constants. **Landed:** explicit `ByteCharLiteral` / `CharLiteral` states with escape-aware skipping plus lifetime vs `'…'` disambiguation and focused unit tests — byte-char and character literals no longer false-green Band-C `#[path` / `mod` needles. **Residual:** raw strings (`r#"…"#`) and byte strings (`b"…"`, `br#"…"#`) are still intentionally loud via the existing opener probe (panic in `Code`). Dissolution for that remainder: widen `IntegrationRsScan` for those forms **or** replace this scan with a structural reader. Owner: unassigned; P2 Boundary Discipline / T-Receipts bundle.
- **Stale-receipt sweep in `docs/briefs/` — PARTIAL (T-Receipts W1).** The cited `DeclarationLookup` cleanup brief now carries a historical-receipt banner stating the lane is closed by SG-4b-1-fix and that `DeclarationLookup` / `find_declaration` are gone from `src/v3/lenses/variant_payload.dag`; its stale INVARIANTS refs were also updated to stable anchors. Remaining broad `docs/briefs/` sweep stays open for later post-wave stale claims. **Include:** `docs/design-pure-bootstrap.md`'s hand-maintained count ("78 hand-maintained .rs files") was stale vs. the SG-0 census — **CLOSED in this R1 PR**: all five occurrences (lines 7, 49, 82, 105, 242 of the design doc) now point at the live census, reframed as deltas against the illustrative 78-file baseline, or scoped to the non-test + TESTING residual framing. No frozen absolute count remains.
- **Compiler–`std/` consolidation program — specific migrations**: end-state defined in [docs/thesis/compiler-std-consolidation.md](docs/thesis/compiler-std-consolidation.md). Each of the migrations below is a standalone lane; together they collapse the compiler-specific type surface toward the positive definition (pipeline + regen + lens-specific return-type carriers + accessor). **Ratchet:** count of `type` declarations in `src/v3/compiler/*.dag` AND `src/v3/lenses/*.dag` that are NOT in the positive-def set AND NOT exempted → 0. Positive-def: pipeline/regen types + lens-API return carriers (`Origin`, `UnusedParameter`, `VariantPayloadShapeLookup` 3-variant, `TemplateArgumentBinding` semantic carrier, etc.). Exempted: `parse_tables.dag` (7 types, pending SG-2c-proper per-row classification). In-ratchet: **workaround-shaped infer-helper coproducts with named dissolution triggers (`TemplateArgumentsMatch`, `TemplateArgumentCursor`, `NormalizedInstantiationArgs`)** — Lookup-pattern carriers on lens surfaces import `v3.std.lookup::Lookup` (`complexity`, `cost`, `infer_helpers`). Primary count: 3. See thesis doc table for per-file disposition.
- **`tokenize.dag` → `std/tokenize.dag`** — landed: `Token`, `TokenKind`, `KeywordTokenKind`, `PunctTokenKind`, `LocalPunctSpec`, `StringEscapeSpec` now live in `src/v3/std/tokenize.dag`; compiler-local duplicates deleted. Ratchet drop: 25 → 19.
Expand Down
4 changes: 2 additions & 2 deletions scripts/check-test-timeout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
# (default scripts/slow-test-exemptions.txt).
# TEST_TIMEOUT_MAX_EXEMPTIONS
# Ratchet floor for active exemption entries
# (default 39, captured 2026-04-25). Lower this
# (default 40, captured 2026-05-05). Lower this
# value in the same PR that removes exemptions.

set -euo pipefail
Expand All @@ -55,7 +55,7 @@ log_file_arg=${1:-}
budget_ms=${2:-${TEST_TIMEOUT_MS:-2000}}
pkg=${TEST_TIMEOUT_PACKAGE:-v3-compiler}
exempt_file=${TEST_TIMEOUT_EXEMPT:-scripts/slow-test-exemptions.txt}
max_exemptions=${TEST_TIMEOUT_MAX_EXEMPTIONS:-39}
max_exemptions=${TEST_TIMEOUT_MAX_EXEMPTIONS:-40}

script_dir=$(cd "$(dirname "$0")" && pwd)
repo_root=$(cd "$script_dir/.." && pwd)
Expand Down
2 changes: 2 additions & 0 deletions scripts/slow-test-exemptions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ bootstrap::tests::kernel_bool_path_a_attaches_diagnostic_when_boolean_algebra_un

p0_std_render_repeat_string_test::std_render_repeat_string_and_indent_text_match_interpreter # ROADMAP P0 (`dsl/std/render.dag` `repeat_string` / `indent_text`): observed 29.7s on PR #595's merge commit. Structural blocker: v2 `compile_to_resolved` + interpreter oracle cold-compile the full resolver for every test; no shared bootstrap cache exists between v2-oracle tests and the v3 integration binary. Named paydown trigger: when a shared v2-oracle bootstrap cache lands (TM-0 shared-bootstrap lane or an earlier TESTING.md §shared-setup pass introducing a `OnceLock<ResolvedPipelineResult>`), re-measure and remove this exemption.

t_demo_fixture_test::t_demo_canonical_suites_are_runner_visible # ROADMAP T-Demo (`tests/integration.rs`): cold `compile_to_dag` on `t_demo_fixtures.dag` plus two `TestRunner::run_suite` passes; observed ~2419ms on ubuntu-latest (PR #1791 CI). Paydown: warm shared `Dag` via `OnceLock` across `t_demo_fixture_test` or slim suite probes — TESTING.md § shared-setup / runner-cost lane.

sg1_tokenize_authority_test::tokenize_generated_module_matches_checked_in_snapshot # SG-1 authority snapshot still regenerates/parses full tokenize projection; tokenize authority lane owns future speedup.
sg2_parse_authority_test::parse_surface_dag_compiles_cleanly_for_regen_parse # SG-2 parser-staging: `compile_parse_surface_std_authority_dag` + cold bootstrap work can exceed 2s wall under default libtest parallelism; ROADMAP SG-2 / TESTING.md paydown (same lane as `parse_generated_module_matches_checked_in_snapshot`).
sg2c1_parse_tables_authority_test::parse_tables_generated_module_matches_checked_in_snapshot # SG-2c-1 grammar-tables prototype snapshot: in-process authority compile (parse_tables.dag + tokenize.dag) + rustfmt stdout; same paydown class as SG-1 tokenize / SG-2 parse snapshot gates.
Expand Down
168 changes: 161 additions & 7 deletions src/v3/compiler/tests/integration/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,39 @@ fn code_opens_raw_or_byte_string_literal(bytes: &[u8], i: usize) -> bool {
}
}

/// Byte index after the last byte of a Rust escape sequence starting at `backslash`
/// (`bytes[backslash] == b'\\'`). Matches the suffix forms used in character and byte
/// literals (`\n`, `\xNN`, `\u{…}`, `\uXXXX`, …).
fn integration_rs_escape_sequence_end(bytes: &[u8], backslash: usize) -> usize {
debug_assert_eq!(bytes.get(backslash), Some(&b'\\'));
let i = backslash + 1;
if i >= bytes.len() {
return bytes.len();
}
match bytes[i] {
b'x' => (backslash + 4).min(bytes.len()),
b'u' if bytes.get(i + 1) == Some(&b'{') => {
let mut j = i + 2;
while j < bytes.len() && bytes[j] != b'}' {
j += 1;
}
(j + 1).min(bytes.len())
}
b'u' => (backslash + 6).min(bytes.len()),
_ => (backslash + 2).min(bytes.len()),
}
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum IntegrationRsScan {
Code,
LineComment,
BlockComment(u32),
String,
/// Inside `b'…'` after consuming the `b'` prefix.
ByteCharLiteral,
/// Inside `'…'` after consuming the opening `'`.
CharLiteral,
}

fn integration_rs_panic_on_raw_string(bytes: &[u8], i: usize) {
Expand All @@ -126,7 +153,9 @@ fn integration_rs_panic_on_raw_string(bytes: &[u8], i: usize) {
}

/// Byte offsets of `needle` in `integration_rs` that occur in **Code** (outside
/// `//`, `/* */`, and `"…"` strings). See [`integration_rs_active_line_contains`].
/// `//`, `/* */`, `"…"` strings, `b'…'` byte literals, and `'…'` character literals).
/// Lifetimes and labels (`'a`, `'static`) are skipped without entering string/literal
/// states. See [`integration_rs_active_line_contains`].
fn integration_rs_code_substring_positions(integration_rs: &str, needle: &str) -> Vec<usize> {
let mut hits = Vec::new();
if needle.is_empty() {
Expand All @@ -153,6 +182,34 @@ fn integration_rs_code_substring_positions(integration_rs: &str, needle: &str) -
continue;
}
}
if bytes.get(i) == Some(&b'b') && bytes.get(i + 1) == Some(&b'\'') {
state = IntegrationRsScan::ByteCharLiteral;
i += 2;
continue;
}
if bytes[i] == b'\'' {
if bytes.get(i + 1) == Some(&b'\\') {
state = IntegrationRsScan::CharLiteral;
i += 1;
continue;
}
if let Some(rest) = integration_rs.get(i + 1..) {
let mut chars = rest.chars();
if let Some(c) = chars.next() {
let after_char = i + 1 + c.len_utf8();
if bytes.get(after_char) == Some(&b'\'') {
i = after_char + 1;
continue;
}
}
}
i += 1;
while i < bytes.len() && (bytes[i].is_ascii_alphanumeric() || bytes[i] == b'_')
{
i += 1;
}
continue;
}
if bytes[i] == b'"' {
state = IntegrationRsScan::String;
i += 1;
Expand Down Expand Up @@ -199,6 +256,37 @@ fn integration_rs_code_substring_positions(integration_rs: &str, needle: &str) -
i += 1;
}
},
IntegrationRsScan::ByteCharLiteral => {
if bytes.get(i) == Some(&b'\\') {
i = integration_rs_escape_sequence_end(bytes, i);
} else {
i += 1;
}
if bytes.get(i) == Some(&b'\'') {
i += 1;
}
state = IntegrationRsScan::Code;
}
IntegrationRsScan::CharLiteral => {
if bytes.get(i) == Some(&b'\\') {
i = integration_rs_escape_sequence_end(bytes, i);
} else if let Some(rest) = integration_rs.get(i..) {
let mut ci = rest.chars();
if let Some(c) = ci.next() {
i += c.len_utf8();
} else {
state = IntegrationRsScan::Code;
continue;
}
} else {
state = IntegrationRsScan::Code;
continue;
}
if bytes.get(i) == Some(&b'\'') {
i += 1;
}
state = IntegrationRsScan::Code;
}
}
}
hits
Expand Down Expand Up @@ -312,17 +400,18 @@ pub fn integration_rs_cementing_path_attr_binds_mod_stem(integration_rs: &str, s
}

/// True when `needle` appears in `integration_rs` **outside** Rust line comments
/// (`//` … including `///` / `//!`), **nested** block comments (`/* … */`), and
/// normal `"…"` string literals.
/// (`//` … including `///` / `//!`), **nested** block comments (`/* … */`), normal
/// `"…"` string literals, `b'…'` byte character literals, and `'…'` character
/// literals (excluding lifetimes / labels such as `'a` or `'static`).
///
/// Band-C wiring ratchets use this so `#[path = …]` / `mod …;` matches cannot
/// false-green on commented-out or string-embedded copies. Needles are ASCII
/// (`#[path`, `mod foo`); the scan is byte-oriented on UTF-8 boundaries.
///
/// **Not handled:** raw strings (`r#"…"#`), byte strings (`b"…"`, `br#"…"#`), or
/// char literals — none appear in today’s `tests/integration.rs` module list. If
/// they do, extend `IntegrationRsScan` **or** the scan will **panic** when the
/// raw/byte-string opener probe fires in `Code` (loud failure vs a silent false green).
/// **Not handled:** raw strings (`r#"…"#`), byte strings (`b"…"`, `br#"…"#`). If
/// those appear in scanned source, extend `IntegrationRsScan` **or** the scan will
/// **panic** when the raw/byte-string opener probe fires in `Code` (loud failure vs
/// a silent false green).
pub fn integration_rs_active_line_contains(integration_rs: &str, needle: &str) -> bool {
!integration_rs_code_substring_positions(integration_rs, needle).is_empty()
}
Expand Down Expand Up @@ -537,3 +626,68 @@ macro_rules! budgeted_test {
}
};
}

#[cfg(test)]
mod integration_rs_scan_tests {
use super::integration_rs_active_line_contains;

#[test]
fn integration_rs_scan_skips_byte_char_literals_so_path_ratchet_still_sees_mod_list() {
let src = concat!(
"fn demo_bytes() {\n",
"let _slash = b'\\\\';\n",
"let _quote = b'\"';\n",
"let _apos = b'\\'';\n",
"}\n",
"#[path = \"integration/cementing/real.rs\"]\n",
"mod real;\n",
);
assert!(integration_rs_active_line_contains(
src,
r#"#[path = "integration/cementing/real.rs"]"#,
));
}

#[test]
fn integration_rs_scan_skips_char_literal_containing_double_quote() {
let src = concat!(
"let _c = '\"';\n",
"#[path = \"integration/cementing/real.rs\"]\n",
"mod real;\n",
);
assert!(integration_rs_active_line_contains(
src,
r#"#[path = "integration/cementing/real.rs"]"#,
));
}

#[test]
fn integration_rs_scan_accepts_path_after_lifetime() {
let src = r#"
fn foo<'a>() {
#[path = "integration/cementing/real.rs"]
mod real;
}
"#;
assert!(integration_rs_active_line_contains(
src,
r#"#[path = "integration/cementing/real.rs"]"#,
));
}

#[test]
fn integration_rs_scan_does_not_match_needle_inside_byte_char_literal() {
assert!(!integration_rs_active_line_contains(
concat!("let _ = b'#';", "\n"),
"#",
));
assert!(integration_rs_active_line_contains(
concat!(
"let _ = b'#';",
"\n#[path = \"integration/cementing/real.rs\"]\n",
"mod real;\n",
),
"#[path",
));
}
}
Loading