fix(release): restore canonical local gate

req: public_api/001

req: v1_release/007
This commit is contained in:
slhx agent
2026-07-14 01:24:31 +02:00
parent 90e8c3ced5
commit 025d9c1563
10 changed files with 33 additions and 23 deletions
+2 -2
View File
@@ -44,7 +44,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file
- `redgate refs` — show `req:` citations found in the repo. - `redgate refs` — show `req:` citations found in the repo.
- `redgate health` — show uncited requirements, duplicate IDs, and stale citations. - `redgate health` — show uncited requirements, duplicate IDs, and stale citations.
- `redgate lint` — show maintainability warnings such as missing rings and oversized requirement rows; when fixing one row in a section, normalize nearby rows with the same warning if it stays a requirement-only cleanup. - `redgate lint` — show maintainability warnings such as missing rings and oversized requirement rows; when fixing one row in a section, normalize nearby rows with the same warning if it stays a requirement-only cleanup.
- `redgate health --strict` — fail on hard errors: empty requirements, duplicate IDs, or stale citations. - `redgate health` — with the installed CLI, fail on hard errors and report uncited requirements; use `--lax` only when intentionally downgrading those failures.
- `redgate agents` — print this starter template; review, shrink, and edit before committing. - `redgate agents` — print this starter template; review, shrink, and edit before committing.
## Local guidance ## Local guidance
@@ -52,7 +52,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file
- Add only durable style, ownership, gotchas, and at most a few stable commands agents should actually run. - Add only durable style, ownership, gotchas, and at most a few stable commands agents should actually run.
- Prefer links or pointers to canonical sources over copied lists. - Prefer links or pointers to canonical sources over copied lists.
- Avoid project trees, architecture maps, generated inventories, current file sizes, issue lists, TODO inventories, and other snapshots that will rot. - Avoid project trees, architecture maps, generated inventories, current file sizes, issue lists, TODO inventories, and other snapshots that will rot.
- Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate health --strict`. Use the xtask runner for full verification so jobs are capped from local CPU and memory and commands resolve the workspace independently of the caller's directory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. Keep fast crate tests, focused browser smoke, and full xtask authority distinct; the full path should stay within a documented 10 minute local timeout or be split into deterministic shards under the same wrapper. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014 req: test/015 req: test/016 - Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate health`. Use the xtask runner for full verification so jobs are capped from local CPU and memory and commands resolve the workspace independently of the caller's directory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. Keep fast crate tests, focused browser smoke, and full xtask authority distinct; the full path should stay within a documented 10 minute local timeout or be split into deterministic shards under the same wrapper. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014 req: test/015 req: test/016
- Example behavior tests should prefer `hemx_test` generated-resource assertion methods over raw slot constants, raw effect/payload matching, or boolean predicates wrapped in opaque `assert!`; failures should include the expectation and actual effects, while rendered target/handle assertions should name the generated resource. Keep browser selector helpers as test adapters only, not authoring APIs. Process-backed tests use the RAII `TestProcess` harness rather than duplicating readiness loops and child cleanup. req: test/008 req: test/009 req: test/010 req: test/017 req: test/018 req: test/019 - Example behavior tests should prefer `hemx_test` generated-resource assertion methods over raw slot constants, raw effect/payload matching, or boolean predicates wrapped in opaque `assert!`; failures should include the expectation and actual effects, while rendered target/handle assertions should name the generated resource. Keep browser selector helpers as test adapters only, not authoring APIs. Process-backed tests use the RAII `TestProcess` harness rather than duplicating readiness loops and child cleanup. req: test/008 req: test/009 req: test/010 req: test/017 req: test/018 req: test/019
- Run the workout product exemplar with `cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`; set `HEMX_WORKOUT_ADDR=127.0.0.1:3030` if the default port is busy. Its durable visual direction and recovery expectations live in `examples/workout/DESIGN.md`. req: examples/008 - Run the workout product exemplar with `cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`; set `HEMX_WORKOUT_ADDR=127.0.0.1:3030` if the default port is busy. Its durable visual direction and recovery expectations live in `examples/workout/DESIGN.md`. req: examples/008
- Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013 - Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013
+1 -1
View File
@@ -77,4 +77,4 @@ encryption, retention, backup, and deployment policy remain host concerns.
- **Requirements:** `v1_release/001-010`, `versioning/*`, `test/*`, `diag/*`, `performance/*`, `security/008`, and all requirements changed by the preceding slices. - **Requirements:** `v1_release/001-010`, `versioning/*`, `test/*`, `diag/*`, `performance/*`, `security/008`, and all requirements changed by the preceding slices.
- **Proof:** clean-tree formatting, workspace tests, strict all-target Clippy, compile-fail, browser/WASM/offline/multiplayer scenarios, benchmark budgets, approved pinned lockfile audit, requirements proof audit, docs/examples checks, and independent contradiction review all pass with no unresolved P0/P1. - **Proof:** clean-tree formatting, workspace tests, strict all-target Clippy, compile-fail, browser/WASM/offline/multiplayer scenarios, benchmark budgets, approved pinned lockfile audit, requirements proof audit, docs/examples checks, and independent contradiction review all pass with no unresolved P0/P1.
`cargo test -p hemx-core --test effect_batch compatibility_fixture_accepts_only_the_declared_v1_wire_version -- --exact`, `cargo test -p hemx-wasm --test browser flat_patch_persists_offline_then_uploads_with_same_operation_identity -- --exact`, and `cargo test -p hemx-v0-examples -p hemx-client-local-example -p hemx-kanban-example -p hemx-saas-example --lib --bins` prove the first compatibility gate. Execution cursor: run the remaining Slice 7 release audit gates from the canonical `hemx-xtask test` entry point, resolve only genuine local failures, and record exact blocked external audit tools instead of inventing substitutes. `cargo test -p hemx-core --test effect_batch compatibility_fixture_accepts_only_the_declared_v1_wire_version -- --exact`, `cargo test -p hemx-wasm --test browser flat_patch_persists_offline_then_uploads_with_same_operation_identity -- --exact`, and `cargo test -p hemx-v0-examples -p hemx-client-local-example -p hemx-kanban-example -p hemx-saas-example --lib --bins` prove the first compatibility gate. The first `hemx-xtask test` audit found and fixed three genuine local failures: the app-facing durable-sync primitive now accepts ordinary `IntoEffect`, the canonical example contract recognizes the explicitly isolated legacy fixture, and the canonical gate uses the installed `redgate health` syntax. Workspace/browser tests then pass until requirements governance. The remaining exact blocker is a redgate/parser-format mismatch: the installed CLI interprets the elected lines beginning `0<TAB>001...` as duplicate IDs such as `laws/0`; `cargo-audit` and `cargo-deny` are also unavailable locally. Execution cursor: reconcile or provide the project-approved redgate version/format without mutating the concurrently edited `REQUIREMENTS.md`, rerun `hemx-xtask test`, then run or explicitly disposition the unavailable pinned-lockfile audit tools.
+1 -1
View File
@@ -86,4 +86,4 @@ Current recurring checks cover the most common classes:
Before claiming the diagnostics story is closed for v1, run those gates plus Before claiming the diagnostics story is closed for v1, run those gates plus
`cargo run -p hemx-xtask -- test`, `cargo check --workspace`, and `cargo run -p hemx-xtask -- test`, `cargo check --workspace`, and
`redgate health --strict` on a clean tree. req: test/003 req: test/004 `redgate health` on a clean tree. req: test/003 req: test/004
+1 -1
View File
@@ -121,7 +121,7 @@ Before promoting a release:
```sh ```sh
cargo run -p hemx-xtask -- test cargo run -p hemx-xtask -- test
cargo check --workspace cargo check --workspace
redgate health --strict redgate health
``` ```
Then verify deployment-specific behavior: Then verify deployment-specific behavior:
+1 -1
View File
@@ -134,7 +134,7 @@ include upgrade notes with this shape:
```sh ```sh
cargo run -p hemx-xtask -- test cargo run -p hemx-xtask -- test
cargo check --workspace cargo check --workspace
redgate health --strict redgate health
``` ```
```` ````
+10 -6
View File
@@ -1,6 +1,9 @@
#[hemx::surface] #[hemx::surface]
pub mod ui {} pub mod ui {}
#[cfg(feature = "client")]
use hemx_sync::SyncEffect as DurableSync;
#[cfg(feature = "client")] #[cfg(feature = "client")]
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
struct CardId(String); struct CardId(String);
@@ -87,15 +90,15 @@ pub fn reorder_card(
Some(before) => ui::client_board::client_cards.move_before(card.clone(), before.0), Some(before) => ui::client_board::client_cards.move_before(card.clone(), before.0),
None => ui::client_board::client_cards.move_to_end(card.clone()), None => ui::client_board::client_cards.move_to_end(card.clone()),
}; };
let projection = hemx::IntoEffect::into_batch( DurableSync::durable(
patch,
( (
move_effect, move_effect,
ui::client_board::client_notice ui::client_board::client_notice
.text(format!("Moved {card} with {}", projected.input_kind)), .text(format!("Moved {card} with {}", projected.input_kind)),
), ),
ui::BUILD_FINGERPRINT, ui::BUILD_FINGERPRINT,
); )
hemx_sync::SyncEffect::durable(patch, projection)
} }
#[cfg(all(test, feature = "client"))] #[cfg(all(test, feature = "client"))]
@@ -118,9 +121,10 @@ mod client_tests {
) )
.into_batch(ui::BUILD_FINGERPRINT); .into_batch(ui::BUILD_FINGERPRINT);
assert_eq!(batch.ops.len(), 3); assert_eq!(batch.ops.len(), 3);
assert!( let wire = String::from_utf8_lossy(&batch.to_wire()).into_owned();
matches!(&batch.ops[2], hemx::advanced::Effect::Emit { name, payload } if name == hemx_sync::PATCH_EVENT && payload.contains("$hemx-interaction") && payload.contains("\"projection\":[")) assert!(wire.contains(hemx_sync::PATCH_EVENT));
); assert!(wire.contains("$hemx-interaction"));
assert!(wire.contains("\"projection\":["));
} }
} }
+4 -2
View File
@@ -15,7 +15,9 @@ use hemx_axum::{
use hemx_kanban_example::ui::board::{self as board}; use hemx_kanban_example::ui::board::{self as board};
use hemx_kanban_example::ui::board_card as card_board; use hemx_kanban_example::ui::board_card as card_board;
use hemx_kanban_example::ui::{self, board as board_ui}; use hemx_kanban_example::ui::{self, board as board_ui};
use hemx_sync::{Channel, PresenceScope, PresenceTracker, PresenceUpdate, SyncEffect}; use hemx_sync::{
Channel, PresenceScope, PresenceTracker, PresenceUpdate, SyncEffect as FrameworkSync,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::Infallible; use std::convert::Infallible;
@@ -663,7 +665,7 @@ async fn sync_ack(
acknowledgement.clone() acknowledgement.clone()
}; };
let batch = ( let batch = (
SyncEffect::ack(board::atoms::sync_ack), FrameworkSync::ack(board::atoms::sync_ack),
board::sync_status.text(format!( board::sync_status.text(format!(
"Canonical acknowledgement {} at server sequence {}", "Canonical acknowledgement {} at server sequence {}",
acknowledgement.command_id, acknowledgement.server_sequence acknowledgement.command_id, acknowledgement.server_sequence
+10 -6
View File
@@ -1,4 +1,4 @@
use hemx_core::{Atom, Effect, EffectBatch, IntoEffect}; use hemx_core::{Atom, BuildFingerprint, Effect, EffectBatch, IntoEffect};
use serde::{de, Deserialize, Deserializer, Serialize}; use serde::{de, Deserialize, Deserializer, Serialize};
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
@@ -421,8 +421,13 @@ impl SyncEffect {
/// Apply an optimistic projection now and carry the same ordinary batch in /// Apply an optimistic projection now and carry the same ordinary batch in
/// the durable patch event so the framework sync runtime can replay it /// the durable patch event so the framework sync runtime can replay it
/// after reload before acknowledgement. /// after reload before acknowledgement.
pub fn durable(patch: FlatPatch, projection: EffectBatch) -> Self { pub fn durable(
patch: FlatPatch,
projection: impl IntoEffect,
fingerprint: BuildFingerprint,
) -> Self {
patch.validate().expect("FlatPatch must remain valid"); patch.validate().expect("FlatPatch must remain valid");
let projection = projection.into_batch(fingerprint);
let projection_wire = projection let projection_wire = projection
.to_wire() .to_wire()
.iter() .iter()
@@ -459,10 +464,9 @@ mod tests {
let projection = Effect::Emit { let projection = Effect::Emit {
name: "projected".into(), name: "projected".into(),
payload: "card:1".into(), payload: "card:1".into(),
} };
.into_batch(hemx_core::BuildFingerprint(7)); let batch = SyncEffect::durable(patch, projection, hemx_core::BuildFingerprint(7))
let batch = .into_batch(hemx_core::BuildFingerprint(7));
SyncEffect::durable(patch, projection).into_batch(hemx_core::BuildFingerprint(7));
assert_eq!(batch.ops.len(), 2); assert_eq!(batch.ops.len(), 2);
assert!(matches!(&batch.ops[0], Effect::Emit { name, .. } if name == "projected")); assert!(matches!(&batch.ops[0], Effect::Emit { name, .. } if name == "projected"));
assert!( assert!(
+2 -2
View File
@@ -357,8 +357,8 @@ fn allowed_example_script(path: &Path, line: &str) -> bool {
&& line.contains(r#"<script src="/island.js" defer></script>"#)) && line.contains(r#"<script src="/island.js" defer></script>"#))
|| (path.ends_with("examples/saas/templates/app_shell.heml") || (path.ends_with("examples/saas/templates/app_shell.heml")
&& line.contains(r#"<script src="/metrics.js" defer></script>"#)) && line.contains(r#"<script src="/metrics.js" defer></script>"#))
|| (path.ends_with("examples/kanban/templates/sync_shell.heml") || (path.ends_with("examples/kanban/templates/legacy_sync_fixture.heml")
&& line.contains(r#"<script src="/sync.js" defer></script>"#)) && line.contains(r#"<script type="module" src="/sync.js"></script>"#))
} }
fn contains_inline_event_handler(line: &str) -> bool { fn contains_inline_event_handler(line: &str) -> bool {
+1 -1
View File
@@ -1190,7 +1190,7 @@ fn run_test_plan() -> ExitCode {
"techdemo-unit-http", "techdemo-unit-http",
["test", "-p", "hemx-techdemo", "--test", "e2e"], ["test", "-p", "hemx-techdemo", "--test", "e2e"],
), ),
Step::new("redgate", ["health", "--strict"]).tool("redgate"), Step::new("redgate", ["health"]).tool("redgate"),
]; ];
if !budget.skip_browser { if !budget.skip_browser {