Add journaling guidance to the orchestration skill
Intent: Introduce a lightweight practice of keeping a local, untracked JOURNAL.md during multi-chunk implementation programs so that cleanup ideas are captured without widening the active chunk of work.
Affected files: .agents/skills/orchestrate/SKILL.md
Evidence
@@ -19,6 +19,10 @@ Use this skill when running a multi-chunk implementation program in `pika` where
+- Keep a local untracked `JOURNAL.md` in the worktree for simplification opportunities, abstraction ideas, and cleanup candidates noticed in the weeds.
+ - Capture the note quickly and keep moving.
+ - Do not widen the active chunk just because the journal grew.
+ - Triage the journal at the end of the broader project or migration once the primary seams are landed.
A new bullet block is added to the orchestration skill's workflow rules. The guidance addresses a common problem during large migrations: developers notice refactoring opportunities while deep in implementation but risk scope creep if they act on them immediately.
The rule prescribes four behaviors:
- Maintain a local, untracked
JOURNAL.md per worktree.
- Capture simplification ideas, abstraction candidates, and cleanup notes quickly.
- Do not expand the current chunk based on journal entries.
- Triage the journal only after the primary seams of the broader project have landed.
This keeps the working branch focused while preserving institutional knowledge about tech-debt opportunities.
Implement encode_runtime_event_line and decode_runtime_event_line
Intent: Provide a pair of helpers that serialize a `LifecycleEvent` to a compact, single-line JSON string and deserialize it back, suitable for newline-delimited event streams or structured log lines.
Affected files: crates/pika-cloud/src/lifecycle.rs
Evidence
@@ -129,6 +129,14 @@ pub fn decode_runtime_terminal_result(bytes: &[u8]) -> serde_json::Result<Runtim
+pub fn encode_runtime_event_line(event: &LifecycleEvent) -> serde_json::Result<String> {
+ serde_json::to_string(event)
+}
+
+pub fn decode_runtime_event_line(line: &str) -> serde_json::Result<LifecycleEvent> {
+ serde_json::from_str(line)
+}
Two new public functions are added to crates/pika-cloud/src/lifecycle.rs:
encode_runtime_event_line(&LifecycleEvent) -> Result<String> — delegates to serde_json::to_string, producing compact (non-pretty) JSON with no trailing newline. This is the key difference from the existing encode_runtime_status_pretty which targets file persistence.
decode_runtime_event_line(&str) -> Result<LifecycleEvent> — delegates to serde_json::from_str, the string-slice counterpart to the existing decode_runtime_status which operates on &[u8].
The naming convention (*_event_line) signals that these are intended for line-oriented protocols (e.g., one JSON object per line in a stream), distinguishing them from the byte-oriented status snapshot helpers.
Export the new helpers from the crate root
Intent: Make `encode_runtime_event_line` and `decode_runtime_event_line` accessible to downstream crates through the `pika-cloud` public API.
Affected files: crates/pika-cloud/src/lib.rs
Evidence
@@ -11,8 +11,8 @@ pub use incus::{
pub use lifecycle::{
LIFECYCLE_SCHEMA_VERSION, LifecycleEvent, LifecycleState, RuntimeResultStatus,
- RuntimeStatusSnapshot, RuntimeTerminalResult, decode_runtime_status,
- decode_runtime_terminal_result, encode_runtime_status_pretty,
+ RuntimeStatusSnapshot, RuntimeTerminalResult, decode_runtime_event_line, decode_runtime_status,
+ decode_runtime_terminal_result, encode_runtime_event_line, encode_runtime_status_pretty,
The pub use lifecycle::{…} block in crates/pika-cloud/src/lib.rs is updated to include both decode_runtime_event_line and encode_runtime_event_line. The items remain in alphabetical order, consistent with the existing style of the re-export list.
Add round-trip and rejection tests for event line helpers
Intent: Verify that the new helpers correctly round-trip a fully populated `LifecycleEvent` and that deserialization rejects unknown `LifecycleEventKind` variants, ensuring forward-compatibility safety.
Affected files: crates/pika-cloud/src/lifecycle.rs
Evidence
@@ -162,6 +170,41 @@ mod tests {
+ #[test]
+ fn runtime_event_line_helpers_round_trip() {
+ let event = LifecycleEvent {
+ schema_version: LIFECYCLE_SCHEMA_VERSION,
+ seq: 7,
+ timestamp: "2026-03-25T20:00:00Z".to_string(),
+ kind: LifecycleEventKind::Ready,
+ message: "guest declared readiness".to_string(),
+ boot_id: Some("boot-123".to_string()),
+ details: Some(serde_json::json!({ "service": "openclaw" })),
+ };
+
+ let encoded = encode_runtime_event_line(&event).expect("encode");
+ let decoded = decode_runtime_event_line(&encoded).expect("decode");
+
+ assert_eq!(decoded, event);
+ assert!(!encoded.ends_with('\n'));
+ }
+
+ #[test]
+ fn runtime_event_line_rejects_unknown_kind() {
Two unit tests are added to the existing mod tests block:
runtime_event_line_helpers_round_trip
Constructs a LifecycleEvent with all optional fields populated (boot_id, details with a nested JSON object). It encodes the event, decodes it, and asserts structural equality. A secondary assertion confirms the encoded string does not end with '\n', codifying the contract that callers are responsible for adding their own line terminator.
runtime_event_line_rejects_unknown_kind
Feeds a JSON payload containing "kind": "passed" (a value not in the LifecycleEventKind enum) into decode_runtime_event_line and asserts that:
- Deserialization returns an
Err.
- The error message contains
"unknown variant".
This test documents that the enum uses serde's default deny-unknown-variants behavior, which is important for safely evolving the lifecycle protocol — producers must not emit kinds that consumers cannot understand.