Skip to content

Commit 4315990

Browse files
feat(manifest): add explicit enable_constraints; cap enabled_count at 8 (#70)
Closes #34. OctadConfig previously had no `enable_constraints` field. The Constraints dimension was "implied" via `if enabled_count > 2 { count += 1; }`, which meant: 1. Toggling any other dimension silently flipped Constraints ON or OFF in `print_status`. 2. The cap of 8 dimensions was implicit; nothing prevented overflow. 3. ADR-0001's first-class treatment of Constraints had no representation in the data model. Changes: - Add `enable_constraints: bool` to `OctadConfig` (defaults to true, serde rename `enable-constraints`). - Rewrite `enabled_count()` to count each `enable_*` field once. The implicit `+1` arithmetic is gone; the result is now guaranteed to be in `2..=8` by construction. - `print_status` reads `enable_constraints` directly instead of inferring it from the total count. - `init_manifest` template emits `enable-constraints = true`. - Update six existing test fixtures and one integration test that construct OctadConfig literals to include the new field. Add three new unit tests in `manifest::octad_tests`: - `enabled_count_is_in_range_2_to_8`: exhaustively checks all 64 combinations of the six togglable flags and asserts the result is always in 2..=8. - `enabled_count_with_all_off_is_two`: data + metadata baseline. - `enabled_count_with_all_on_is_eight`: full octad. `cargo clippy --all-targets -- -D warnings` clean; 29 unit tests pass. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 14634e1 commit 4315990

4 files changed

Lines changed: 75 additions & 6 deletions

File tree

src/codegen/overlay.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ mod tests {
267267
enable_lineage: true,
268268
enable_temporal: true,
269269
enable_access_control: true,
270+
enable_constraints: true,
270271
enable_simulation: true,
271272
};
272273
let ddl = generate_sidecar_schema(&schema, &octad);
@@ -287,6 +288,7 @@ mod tests {
287288
enable_lineage: false,
288289
enable_temporal: false,
289290
enable_access_control: false,
291+
enable_constraints: false,
290292
enable_simulation: false,
291293
};
292294
let ddl = generate_sidecar_schema(&schema, &octad);

src/codegen/query.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ mod tests {
343343
enable_lineage: true,
344344
enable_temporal: true,
345345
enable_access_control: true,
346+
enable_constraints: true,
346347
enable_simulation: false,
347348
};
348349
let interceptors = generate_interceptors(&schema, &octad, DatabaseBackend::SQLite);
@@ -364,6 +365,7 @@ mod tests {
364365
enable_lineage: false,
365366
enable_temporal: false,
366367
enable_access_control: false,
368+
enable_constraints: false,
367369
enable_simulation: false,
368370
};
369371
let interceptors = generate_interceptors(&schema, &octad, DatabaseBackend::SQLite);
@@ -384,6 +386,7 @@ mod tests {
384386
enable_lineage: false,
385387
enable_temporal: false,
386388
enable_access_control: false,
389+
enable_constraints: false,
387390
enable_simulation: false,
388391
};
389392
let interceptors = generate_interceptors(&schema, &octad, DatabaseBackend::SQLite);
@@ -403,6 +406,7 @@ mod tests {
403406
enable_lineage: false,
404407
enable_temporal: true,
405408
enable_access_control: false,
409+
enable_constraints: false,
406410
enable_simulation: false,
407411
};
408412
let interceptors = generate_interceptors(&schema, &octad, DatabaseBackend::SQLite);

src/manifest/mod.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ pub struct OctadConfig {
149149
#[serde(rename = "enable-access-control", default = "default_true")]
150150
pub enable_access_control: bool,
151151

152+
/// Enable cross-dimensional invariant enforcement.
153+
#[serde(rename = "enable-constraints", default = "default_true")]
154+
pub enable_constraints: bool,
155+
152156
/// Enable simulation/sandbox mode (what-if queries on branched data).
153157
#[serde(rename = "enable-simulation", default)]
154158
pub enable_simulation: bool,
@@ -161,13 +165,18 @@ impl Default for OctadConfig {
161165
enable_lineage: true,
162166
enable_temporal: true,
163167
enable_access_control: true,
168+
enable_constraints: true,
164169
enable_simulation: false,
165170
}
166171
}
167172
}
168173

169174
impl OctadConfig {
170-
/// Returns the count of enabled octad dimensions (always includes data + metadata = 2).
175+
/// Returns the count of enabled octad dimensions.
176+
///
177+
/// Data and metadata are always enabled (the two inherent dimensions);
178+
/// the other six are toggled via their `enable_*` fields. Result is
179+
/// guaranteed to be in `2..=8`.
171180
pub fn enabled_count(&self) -> usize {
172181
let mut count = 2; // data + metadata are always present
173182
if self.enable_provenance {
@@ -182,17 +191,69 @@ impl OctadConfig {
182191
if self.enable_access_control {
183192
count += 1;
184193
}
185-
if self.enable_simulation {
194+
if self.enable_constraints {
186195
count += 1;
187196
}
188-
// constraints is implied when any other dimension is enabled
189-
if count > 2 {
197+
if self.enable_simulation {
190198
count += 1;
191-
} // constraints
199+
}
192200
count
193201
}
194202
}
195203

204+
#[cfg(test)]
205+
mod octad_tests {
206+
use super::OctadConfig;
207+
208+
/// `enabled_count` must always fall in `2..=8` regardless of which
209+
/// togglable dimensions are on. Exhaustively check all 2^6 = 64
210+
/// combinations of the six togglable flags.
211+
#[test]
212+
fn enabled_count_is_in_range_2_to_8() {
213+
for bits in 0u8..(1 << 6) {
214+
let cfg = OctadConfig {
215+
enable_provenance: bits & 0b000001 != 0,
216+
enable_lineage: bits & 0b000010 != 0,
217+
enable_temporal: bits & 0b000100 != 0,
218+
enable_access_control: bits & 0b001000 != 0,
219+
enable_constraints: bits & 0b010000 != 0,
220+
enable_simulation: bits & 0b100000 != 0,
221+
};
222+
let n = cfg.enabled_count();
223+
assert!(
224+
(2..=8).contains(&n),
225+
"bits={bits:06b} produced enabled_count={n}, expected 2..=8"
226+
);
227+
}
228+
}
229+
230+
#[test]
231+
fn enabled_count_with_all_off_is_two() {
232+
let cfg = OctadConfig {
233+
enable_provenance: false,
234+
enable_lineage: false,
235+
enable_temporal: false,
236+
enable_access_control: false,
237+
enable_constraints: false,
238+
enable_simulation: false,
239+
};
240+
assert_eq!(cfg.enabled_count(), 2);
241+
}
242+
243+
#[test]
244+
fn enabled_count_with_all_on_is_eight() {
245+
let cfg = OctadConfig {
246+
enable_provenance: true,
247+
enable_lineage: true,
248+
enable_temporal: true,
249+
enable_access_control: true,
250+
enable_constraints: true,
251+
enable_simulation: true,
252+
};
253+
assert_eq!(cfg.enabled_count(), 8);
254+
}
255+
}
256+
196257
/// [sidecar] section — where the octad dimension data is physically stored.
197258
///
198259
/// The sidecar is a separate database that holds provenance logs, lineage graphs,
@@ -328,6 +389,7 @@ enable-provenance = true
328389
enable-lineage = true
329390
enable-temporal = true
330391
enable-access-control = true
392+
enable-constraints = true
331393
enable-simulation = {enable_simulation}
332394
333395
[sidecar]
@@ -383,7 +445,7 @@ pub fn print_status(manifest: &Manifest) {
383445
);
384446
println!(
385447
" Constraints: {}",
386-
if manifest.octad.enabled_count() > 2 {
448+
if manifest.octad.enable_constraints {
387449
"ON"
388450
} else {
389451
"off"

tests/integration_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn test_full_pipeline_blog_schema() {
7575
enable_lineage: true,
7676
enable_temporal: true,
7777
enable_access_control: true,
78+
enable_constraints: true,
7879
enable_simulation: false,
7980
};
8081
let overlay_ddl = overlay::generate_sidecar_schema(&schema, &octad);

0 commit comments

Comments
 (0)