|
| 1 | + |
| 2 | +# Task Plan: Phase 4 - Zero-Overhead Static Command Registry (Revised & Elaborated) |
| 3 | + |
| 4 | +### Goal |
| 5 | +* To implement Phase 4 of the `unilang` roadmap, focusing on the mandatory performance non-functional requirement for a zero-overhead static command system. This will be achieved by creating a hybrid command registry that uses a Perfect Hash Function (PHF) map for all compile-time commands, ensuring instantaneous startup and sub-millisecond command resolution. |
| 6 | + |
| 7 | +### Ubiquitous Language (Vocabulary) |
| 8 | +* **Static Command:** A command whose definition is known at compile-time. |
| 9 | +* **`StaticCommandDefinition`:** A `const`-compatible representation of a command, using `&'static str` and `&'static [...]` instead of `String` and `Vec`. |
| 10 | +* **Runtime Command:** A command registered dynamically after the application has started. |
| 11 | +* **PHF (Perfect Hash Function):** A hash function that maps a static set of keys to a set of integers with no collisions. |
| 12 | +* **Static Registry:** The part of the `CommandRegistry` that stores static commands in a PHF map, generated at compile-time. |
| 13 | +* **Dynamic Registry:** The part of the `CommandRegistry` that stores runtime commands in a standard `HashMap`. |
| 14 | +* **Hybrid Registry:** The final `CommandRegistry` design that combines the static PHF and the dynamic `HashMap`. |
| 15 | + |
| 16 | +### Progress |
| 17 | +* **Roadmap Milestone:** Phase 4: Zero-Overhead Static Command Registry |
| 18 | +* **Primary Editable Crate:** `module/move/unilang` |
| 19 | +* **Overall Progress:** 0/6 increments complete |
| 20 | +* **Increment Status:** |
| 21 | + * ⚫ Increment 1: Project Setup and `StaticCommandDefinition` |
| 22 | + * ⚫ Increment 2: Implement PHF Generation Logic in `build.rs` |
| 23 | + * ⚫ Increment 3: Refactor `CommandRegistry` to a Hybrid Model |
| 24 | + * ⚫ Increment 4: Create Performance Stress Test Harness |
| 25 | + * ⚫ Increment 5: Implement and Run Performance Assertions |
| 26 | + * ⚫ Increment 6: Finalization |
| 27 | + |
| 28 | +### Permissions & Boundaries |
| 29 | +* **Mode:** code |
| 30 | +* **Run workspace-wise commands:** false |
| 31 | +* **Add transient comments:** true |
| 32 | +* **Additional Editable Crates:** None |
| 33 | + |
| 34 | +### Relevant Context |
| 35 | +* Control Files to Reference: |
| 36 | + * `module/move/unilang/spec.md` |
| 37 | + * `module/move/unilang/roadmap.md` |
| 38 | +* Files to Include (for AI's reference): |
| 39 | + * `module/move/unilang/src/registry.rs` |
| 40 | + * `module/move/unilang/src/data.rs` |
| 41 | + * `module/move/unilang/src/semantic.rs` |
| 42 | + * `module/move/unilang/Cargo.toml` |
| 43 | + |
| 44 | +### Expected Behavior Rules / Specifications |
| 45 | +* **NFR-Performance:** For an application with 1,000+ static commands, the framework must introduce zero runtime overhead for command registration. Startup time must not be impacted by the number of static commands. The p99 latency for resolving a command `FullName` must be less than 1 millisecond. |
| 46 | +* The `CommandRegistry` must function as a hybrid, seamlessly resolving both compile-time (static) and run-time (dynamic) commands, with static lookups taking precedence. |
| 47 | + |
| 48 | +### Tests |
| 49 | +| Test ID | Status | Notes | |
| 50 | +|---|---|---| |
| 51 | +| | | | |
| 52 | + |
| 53 | +### Crate Conformance Check Procedure |
| 54 | +* Run `timeout 180 cargo test -p unilang -- --nocapture` and verify it passes with no warnings. |
| 55 | +* Run `timeout 180 cargo clippy -p unilang -- -D warnings -A clippy::too-many-lines` and verify it passes with no warnings. |
| 56 | + |
| 57 | +### Increments |
| 58 | + |
| 59 | +##### Increment 1: Project Setup and `StaticCommandDefinition` |
| 60 | +* **Goal:** To prepare the `unilang` crate for build-time code generation by adding dependencies, creating the `build.rs` script, and defining the necessary `const`-compatible static data structures. |
| 61 | +* **Specification Reference:** `roadmap.md` M4.1 |
| 62 | +* **Steps:** |
| 63 | + 1. **Read `Cargo.toml`:** Use `read_file` to load `module/move/unilang/Cargo.toml`. |
| 64 | + 2. **Add Dependencies:** Use `insert_content` to add `phf = { version = "0.11", features = ["macros"] }` to the `[dependencies]` section. |
| 65 | + 3. **Add Build Dependencies:** Use `insert_content` to add a `[build-dependencies]` section with `phf_codegen = "0.11"`, `serde = "1.0"`, and `serde_yaml = "0.9"`. |
| 66 | + 4. **Create `build.rs`:** Use `write_to_file` to create `module/move/unilang/build.rs` with the initial content: |
| 67 | + ```rust |
| 68 | + fn main() { |
| 69 | + println!("cargo:rerun-if-changed=build.rs"); |
| 70 | + } |
| 71 | + ``` |
| 72 | + 5. **Create Static Data Models:** Use `write_to_file` to create a new file `module/move/unilang/src/static_data.rs`. This file will contain `const`-compatible versions of the data models. |
| 73 | + ```rust |
| 74 | + // module/move/unilang/src/static_data.rs |
| 75 | + //! Contains `const`-compatible data structures for static command definitions. |
| 76 | + |
| 77 | + // Note: These structs will be expanded in the build script and here. |
| 78 | + // For now, we just create the file. |
| 79 | + ``` |
| 80 | + 6. **Declare Module:** Use `insert_content` in `module/move/unilang/src/lib.rs` to add `pub mod static_data;`. |
| 81 | + 7. **Perform Increment Verification.** |
| 82 | +* **Increment Verification:** |
| 83 | + 1. Execute `timeout 180 cargo build -p unilang`. The build must complete successfully, confirming the `build.rs` script is recognized and dependencies are resolved. |
| 84 | +* **Commit Message:** "chore(unilang): Set up build script and static data models for PHF generation" |
| 85 | + |
| 86 | +##### Increment 2: Implement PHF Generation Logic in `build.rs` |
| 87 | +* **Goal:** To implement the core logic in `build.rs` that reads a manifest of static commands and generates a Rust source file containing a PHF map and all associated `const` data. |
| 88 | +* **Specification Reference:** `roadmap.md` M4.2 |
| 89 | +* **Steps:** |
| 90 | + 1. **Create Manifest:** Use `write_to_file` to create `module/move/unilang/unilang.commands.yaml` with a few static command definitions. |
| 91 | + 2. **Define Static Structs:** In `build.rs`, define the `StaticCommandDefinition` and related structs. These need to be `serde::Deserialize` for parsing the YAML and must be `const`-compatible for code generation. This is a known challenge; the approach will be to deserialize into temporary structs and then generate code for the `const` static structs. |
| 92 | + 3. **Implement Build Logic:** Update `build.rs` to: |
| 93 | + a. Read and parse `unilang.commands.yaml` into `Vec<CommandDefinition>` (the existing, dynamic struct). |
| 94 | + b. Determine the output path: `let path = Path::new(&env::var("OUT_DIR").unwrap()).join("static_commands.rs");`. |
| 95 | + c. Open this path for writing. |
| 96 | + d. Write `use` statements for `phf` and the static data models. |
| 97 | + e. Iterate through the parsed definitions and generate `const` data as a string (e.g., `const CMD_GREET_NAME: &'static str = "greet";`). |
| 98 | + f. Generate `const` instances of the `StaticCommandDefinition` structs. |
| 99 | + g. Generate the `phf_codegen::Map` builder code, mapping full command names to the `const` structs. |
| 100 | + h. Write the final `phf::Map` to the file. |
| 101 | + i. Add `println!("cargo:rerun-if-changed=unilang.commands.yaml");`. |
| 102 | + 4. **Perform Increment Verification.** |
| 103 | +* **Increment Verification:** |
| 104 | + 1. Execute `timeout 180 cargo build -p unilang`. |
| 105 | + 2. Use `read_file` to inspect the generated `target/debug/build/unilang-*/out/static_commands.rs`. It must contain valid Rust code defining `const` data and a `phf::Map`. |
| 106 | +* **Commit Message:** "feat(unilang): Implement build-time generation of PHF for static commands" |
| 107 | + |
| 108 | +##### Increment 3: Refactor `CommandRegistry` to a Hybrid Model |
| 109 | +* **Goal:** To integrate the generated static PHF map into the runtime `CommandRegistry` and adapt all lookup logic to use this new hybrid structure. |
| 110 | +* **Specification Reference:** `roadmap.md` M4.3 |
| 111 | +* **Steps:** |
| 112 | + 1. **Update `static_data.rs`:** Populate `module/move/unilang/src/static_data.rs` with the final `StaticCommandDefinition` and related structs, making them public. Add an implementation of `From<&'static StaticCommandDefinition>` for `CommandDefinition` to convert from the static to the dynamic version. |
| 113 | + 2. **Modify `registry.rs`:** |
| 114 | + a. Use `include!(concat!(env!("OUT_DIR"), "/static_commands.rs"));` at the top level. |
| 115 | + b. Change the `CommandRegistry` struct: rename `commands` to `dynamic_commands`. |
| 116 | + c. Create a new public method `command(&self, name: &str) -> Option<CommandDefinition>`. |
| 117 | + d. Implement the hybrid lookup logic in `command()`: check `STATIC_COMMANDS` first, convert the result to `CommandDefinition`, and if not found, fall back to `dynamic_commands`. |
| 118 | + 3. **Update `SemanticAnalyzer`:** In `semantic.rs`, change the lookup logic to use the new `registry.command()` method. |
| 119 | + 4. **Update Tests:** Modify all tests that interact with the registry (e.g., `full_pipeline_test.rs`, `command_loader_test.rs`) to account for the new hybrid lookup. Some tests might need to register commands dynamically to test that part of the registry. |
| 120 | + 5. **Perform Increment Verification.** |
| 121 | +* **Increment Verification:** |
| 122 | + 1. Perform the Crate Conformance Check. All existing tests must pass. |
| 123 | +* **Commit Message:** "refactor(unilang): Integrate static PHF map into a hybrid CommandRegistry" |
| 124 | + |
| 125 | +##### Increment 4: Create Performance Stress Test Harness |
| 126 | +* **Goal:** To create the necessary infrastructure for a performance stress test, including a mechanism to generate a large number of static commands and a dedicated binary to test them. |
| 127 | +* **Specification Reference:** `roadmap.md` M4.4.1, M4.4.2 |
| 128 | +* **Steps:** |
| 129 | + 1. **Create Test File:** Use `write_to_file` to create `module/move/unilang/tests/inc/phase4/performance_stress_test.rs`. |
| 130 | + 2. **Create Test Binary:** Use `write_to_file` to create `module/move/unilang/tests/stress_test_bin.rs`. |
| 131 | + 3. **Implement YAML Generator:** In `performance_stress_test.rs`, write a function `generate_stress_yaml(count: usize) -> String` that creates a YAML string with `count` unique command definitions. |
| 132 | + 4. **Implement Test Binary Logic:** In `stress_test_bin.rs`, write a `main` function that initializes the `CommandRegistry`, performs a large number of random lookups against the static commands, measures the p99 latency using a library like `hdrhistogram`, and prints the result to stdout before printing "Ready". |
| 133 | + 5. **Orchestrate the Test:** In `performance_stress_test.rs`, the main test function will: |
| 134 | + a. Set an environment variable `UNILANG_STATIC_COMMANDS_PATH` to a path in the `target` directory. |
| 135 | + b. Call `generate_stress_yaml(1000)` and write the result to that path. |
| 136 | + c. Modify `build.rs` to read from `UNILANG_STATIC_COMMANDS_PATH` if it is set. |
| 137 | + 6. **Perform Increment Verification.** |
| 138 | +* **Increment Verification:** |
| 139 | + 1. The `performance_stress_test.rs` test should successfully generate the large YAML file. |
| 140 | + 2. Execute `cargo test --test stress_test_bin --no-run`. The binary must compile successfully against the large generated PHF. |
| 141 | +* **Commit Message:** "test(unilang): Create harness for performance stress testing" |
| 142 | + |
| 143 | +##### Increment 5: Implement and Run Performance Assertions |
| 144 | +* **Goal:** To execute the performance stress test and assert that the startup time and command resolution latency meet the non-functional requirements. |
| 145 | +* **Specification Reference:** `roadmap.md` M4.4.3, M4.4.4; `spec.md` NFR-Performance |
| 146 | +* **Steps:** |
| 147 | + 1. **Expand Test Logic:** In `performance_stress_test.rs`, use `assert_cmd::Command::cargo_bin("stress_test_bin")` to run the compiled test binary. |
| 148 | + 2. **Measure Startup:** The test will measure the total execution time of the binary as a proxy for startup time + lookup time. |
| 149 | + 3. **Parse Output:** The test will capture the stdout from the binary, parse the p99 latency value. |
| 150 | + 4. **Assert Performance:** Assert that the total time is within a reasonable bound (e.g., < 200ms) and that the parsed p99 latency is below the required threshold (< 1ms). |
| 151 | + 5. **Perform Increment Verification.** |
| 152 | +* **Increment Verification:** |
| 153 | + 1. Execute `timeout 300 cargo test -p unilang --test performance_stress_test -- --nocapture`. The test must pass all performance assertions. |
| 154 | +* **Commit Message:** "test(unilang): Implement and pass performance stress test for static registry" |
| 155 | + |
| 156 | +##### Increment 6: Finalization |
| 157 | +* **Goal:** To perform a final review, remove any temporary test artifacts, and verify the entire task's output. |
| 158 | +* **Steps:** |
| 159 | + 1. Review all changes made during this phase. |
| 160 | + 2. Ensure all new code is documented. |
| 161 | + 3. Clean up the `unilang.commands.yaml` file, leaving only a few representative examples. |
| 162 | + 4. Unset the `UNILANG_STATIC_COMMANDS_PATH` environment variable logic or make it test-only. |
| 163 | + 5. Perform the full Crate Conformance Check procedure one last time. |
| 164 | + 6. Perform the `Finalization Increment Verification` procedure from the design rules. |
| 165 | +* **Increment Verification:** |
| 166 | + 1. All checks must pass. |
| 167 | +* **Commit Message:** "feat(unilang): Complete and finalize zero-overhead static command registry" |
| 168 | + |
| 169 | +### Notes & Insights |
| 170 | +* **`const` Compatibility is Key:** The core of this phase is the `StaticCommandDefinition` struct. It's crucial that this struct and all its nested types are `const`-compatible, which means no heap allocations (`String`, `Vec`). |
| 171 | +* **Routine Registration Compromise:** This plan explicitly acknowledges that `CommandRoutine`s cannot be stored statically. The performance gain comes from offloading the parsing and storage of command *definitions* to compile time. Routines for all commands (static and dynamic) will still need to be registered at runtime into a `HashMap`. This is a pragmatic approach that meets the performance NFR for command *resolution*. |
| 172 | + |
| 173 | +### Changelog |
| 174 | +* [Initial] Created a new development plan for Phase 4. |
| 175 | +* [Critique] Revised the plan to address a critical flaw regarding Rust's `const` rules by introducing `StaticCommandDefinition` and refining the build process. Clarified the hybrid nature of routine handling. |
| 176 | +* [Elaboration] Provided a full, detailed version of the revised plan with explicit steps for each increment. |
0 commit comments