Add requirements traceability gate and Gitea pipelines
Ports JellyTau's traceability tooling to Rust, carrying across the bug it was repaired for. That gate divided a traced count by frozen literal denominators; the requirements file outgrew them and it reported 158% coverage, so it could never fail its own threshold. Two rules, both enforced by the extractor's own tests: - denominators parsed from docs/requirements.md at run time - coverage is |traced ∩ defined| / |defined|, never a raw traced count The gate additionally fails hard on a misconfigured run — zero requirements parsed or zero files scanned — rather than reporting a plausible 0%, and on any orphan tag naming a requirement that does not exist. Adapted for DarkRoom: IDs are FR-CAT-1 / NFR-P13 / FR-DEV-3a shapes rather than JellyTau's fixed three digits, and decisions (D), spikes (S), milestone items (M) and test ids remain taggable while being excluded from the denominator — counting them inflated it by 25. Also adds dr-sync: the RemoteBackend trait and capability model, so the Nextcloud connector is one implementation rather than the only shape the engine understands. No mature Nextcloud crate exists (reqwest_dav is too thin), so the connector will be hand-rolled over reqwest per D7. Gitea workflows follow the same style: containerised, commented with the reasoning, desktop and Android on every push, plus a CI check that no core/ crate depends on the UI toolkit (ARCH §6.5a). Coverage today: 13.3% (19/143). 50 tests passing.
This commit is contained in:
@@ -7,16 +7,30 @@ fn main() {
|
||||
env_logger::init();
|
||||
let ctx = pollster::block_on(GpuContext::new_headless()).unwrap();
|
||||
println!("adapter: {}\n", ctx.adapter_name());
|
||||
println!("{:>12} {:>10} {:>10} {:>8}", "size", "compute", "+readback", "fps");
|
||||
println!(
|
||||
"{:>12} {:>10} {:>10} {:>8}",
|
||||
"size", "compute", "+readback", "fps"
|
||||
);
|
||||
|
||||
for &(w, h) in &[(840u32, 692u32), (1280, 720), (1920, 1080), (2048, 1152), (3840, 2160)] {
|
||||
for &(w, h) in &[
|
||||
(840u32, 692u32),
|
||||
(1280, 720),
|
||||
(1920, 1080),
|
||||
(2048, 1152),
|
||||
(3840, 2160),
|
||||
] {
|
||||
let rt = RenderTarget::new(&ctx, w, h).unwrap();
|
||||
// warm
|
||||
for i in 0..10 { rt.render(i as f32 * 0.01); let _ = pollster::block_on(rt.read_pixels()); }
|
||||
for i in 0..10 {
|
||||
rt.render(i as f32 * 0.01);
|
||||
let _ = pollster::block_on(rt.read_pixels());
|
||||
}
|
||||
|
||||
let n = 40;
|
||||
let t0 = Instant::now();
|
||||
for i in 0..n { rt.render(i as f32 * 0.01); }
|
||||
for i in 0..n {
|
||||
rt.render(i as f32 * 0.01);
|
||||
}
|
||||
ctx.device.poll(wgpu::Maintain::Wait);
|
||||
let compute = t0.elapsed().as_secs_f64() / n as f64;
|
||||
|
||||
@@ -27,7 +41,13 @@ fn main() {
|
||||
}
|
||||
let full = t1.elapsed().as_secs_f64() / n as f64;
|
||||
|
||||
println!("{:>5}x{:<6} {:>8.2}ms {:>8.2}ms {:>8.0}",
|
||||
w, h, compute * 1000.0, full * 1000.0, 1.0 / full);
|
||||
println!(
|
||||
"{:>5}x{:<6} {:>8.2}ms {:>8.2}ms {:>8.0}",
|
||||
w,
|
||||
h,
|
||||
compute * 1000.0,
|
||||
full * 1000.0,
|
||||
1.0 / full
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// TRACES: NFR-R7 | NFR-R8
|
||||
/// Failures from the GPU layer.
|
||||
///
|
||||
/// `DeviceLost` is deliberately a distinct variant rather than folded into a
|
||||
|
||||
+5
-13
@@ -116,6 +116,7 @@ struct Params {
|
||||
/// Stands in for the develop pipeline in v0.1. What matters is the shape:
|
||||
/// compute writes a texture, the texture is handed to the compositor, and
|
||||
/// pixels never travel back through the CPU.
|
||||
/// TRACES: FR-DEV-4 | R4
|
||||
pub struct RenderTarget {
|
||||
ctx: GpuContext,
|
||||
texture: wgpu::Texture,
|
||||
@@ -142,9 +143,7 @@ impl RenderTarget {
|
||||
.device
|
||||
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("gradient"),
|
||||
source: wgpu::ShaderSource::Wgsl(
|
||||
include_str!("shaders/gradient.wgsl").into(),
|
||||
),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/gradient.wgsl").into()),
|
||||
});
|
||||
|
||||
let bind_group_layout =
|
||||
@@ -282,12 +281,8 @@ impl RenderTarget {
|
||||
return;
|
||||
}
|
||||
let (texture, view) = Self::create_texture(&self.ctx, width, height);
|
||||
self.bind_group = Self::create_bind_group(
|
||||
&self.ctx,
|
||||
&self.bind_group_layout,
|
||||
&view,
|
||||
&self.params_buf,
|
||||
);
|
||||
self.bind_group =
|
||||
Self::create_bind_group(&self.ctx, &self.bind_group_layout, &view, &self.params_buf);
|
||||
self.texture = texture;
|
||||
self.view = view;
|
||||
self.width = width;
|
||||
@@ -369,10 +364,7 @@ impl RenderTarget {
|
||||
}
|
||||
let buf = &slot.as_ref().unwrap().0;
|
||||
|
||||
let mut enc = self
|
||||
.ctx
|
||||
.device
|
||||
.create_command_encoder(&Default::default());
|
||||
let mut enc = self.ctx.device.create_command_encoder(&Default::default());
|
||||
enc.copy_texture_to_buffer(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: &self.texture,
|
||||
|
||||
Reference in New Issue
Block a user