Task — engineering-spec@1
"synthesis-author@1 skill — nightly multi-memory auto-evolve composes derived memories from clustered raw captures (P3 — stub scaffold in P1)"
doneTASK-SKILL-107
§1 — Description (BCP-14 normative)
The synthesis-author@1 skill MUST scaffold the nightly multi-memory synthesis surface; full logic ships P3. The contract:
- MUST ship signed bundle with SKILL.md frontmatter
allowed_tools: [MemoryEmit, MemoryRead, MemorySearch]; sync_classshareable(synthesised memories may sync); tenant_scopeany. - MUST expose Rust API
request_synthesis(scope: SynthesisScope, dry_run: bool) -> Result<SynthesisOutcome, SynthesisError>where SynthesisScope ∈Tenant | Engagement | Custom { paths: Vec<String> }. - MUST in P1 return
SynthesisOutcome::DeferredToP3 { reason }regardless of arguments. - MUST emit memory audit
memory.synthesis_requestedper invocation. - MUST be invokable via Rust SDK + bash CLI
cyberos-synthesis-author run --scope tenant --dry-run. - MUST exit Ok in P1.
- MUST emit OTel
skill_synthesis_requests_total{scope, outcome}. - SHOULD P3 slice-1 implementation:
- Cluster captures via BGE-M3 embeddings (TASK-AI-019).
- Compose summary memory via COO persona (TASK-AI-014).
- Emit as
kind: synthesised, sync_class: shareablememory.
§2 — Why this design
Same reasoning as TASK-SKILL-106 stub: reserve ID, capture UX, audit invocations, defer logic to module-owner task (here: the eventual P3 synthesis pipeline). Synthesis is high-leverage but expensive (LLM compose chains); shipping the stub now lets early adopters experiment with the surface API without compute cost.
§3 — API contract
// skills/synthesis-author/src/lib.rs
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SynthesisScope {
Tenant,
Engagement { engagement_id: uuid::Uuid },
Custom { paths: Vec<String> },
}
#[derive(Clone, Debug, serde::Serialize)]
pub enum SynthesisOutcome {
DeferredToP3 { reason: String },
}
#[derive(Debug, thiserror::Error)]
pub enum SynthesisError {
#[error("broker down")] BrokerDown,
}
pub async fn request_synthesis(scope: SynthesisScope, dry_run: bool) -> Result<SynthesisOutcome, SynthesisError> {
emit_memory_row("memory.synthesis_requested", serde_json::json!({
"scope": scope, "dry_run": dry_run,
"slice_version": "p1-stub", "trace_id": current_trace_id(),
})).await;
metrics::counter!("skill_synthesis_requests_total",
"scope" => format!("{scope:?}"), "outcome" => "deferred_p3").increment(1);
Ok(SynthesisOutcome::DeferredToP3 {
reason: "Synthesis ships in P3; this is the scaffold reservation.".into(),
})
}
§4 — Acceptance criteria
- DeferredToP3 returned regardless of args.
- memory audit emitted with
slice_version: "p1-stub". - OTel counter increments.
- CLI prints P3-deferral warning.
- SKILL.md validates.
- Signature verified at release.
- Broker enforces narrow allowed_tools.
§5 — Verification
#[tokio::test]
async fn returns_deferred() {
let outcome = request_synthesis(SynthesisScope::Tenant, false).await.unwrap();
assert!(matches!(outcome, SynthesisOutcome::DeferredToP3 { .. }));
}
#[tokio::test]
async fn audit_emitted() {
let _ = request_synthesis(SynthesisScope::Tenant, true).await.unwrap();
let row = memory_test_helper::latest("memory.synthesis_requested").await;
assert_eq!(row["payload"]["slice_version"], "p1-stub");
}
§6 — Implementation skeleton
(API above.)
§7 — Dependencies
- TASK-SKILL-103/104/105 — frontmatter/broker/SDK pattern.
- TASK-SKILL-106 — sibling stub pattern.
- TASK-MEMORY-108 — search (used in P3 implementation).
- TASK-AI-014, TASK-AI-019 — persona + embeddings (used in P3).
§8 — Example payloads
{
"kind": "memory.synthesis_requested",
"payload": {
"scope": "tenant",
"dry_run": true,
"slice_version": "p1-stub",
"trace_id": "0af..."
}
}
§9 — Open questions
All resolved. Deferred to P3: cluster algorithm choice, LLM cost budget, sync_class for synthesised memories.
§10 — Failure modes inventory
| Failure | Detection | Outcome | Recovery |
|---|---|---|---|
| Broker down | UnixStream Err | SynthesisError::BrokerDown | Operator restores |
| Audit emit fails | sev-2 | Stub still returns Ok | Operator restores memory |
| Invalid scope | serde Err | 422 | Caller fixes |
§11 — Implementation notes
- Same stub pattern as TASK-SKILL-106; reserves OCI tag for P3 launch.
slice_version: "p1-stub"flips to"p3-active"in P3 implementation.- COULD priority — may be deprioritised at P3 planning if other features outweigh.
End of TASK-SKILL-107.