fix(release): restore canonical local gate
req: public_api/001 req: v1_release/007
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|||||||
@@ -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\":["));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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!(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user