Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
043a230
docs: design pchronicle judge removal
reiase Aug 17, 2026
c0a130a
docs: include pchronicle web judge removal
reiase Aug 17, 2026
049265c
docs: plan pchronicle judge removal
reiase Aug 17, 2026
aa44221
refactor: remove pchronicle judge subsystem
reiase Aug 17, 2026
ae98879
docs: design pchronicle post-judge cleanup
reiase Aug 17, 2026
59dfaf0
docs: expand pchronicle cleanup design
reiase Aug 17, 2026
66e242f
docs: translate pchronicle cleanup design
reiase Aug 17, 2026
5e26e47
docs: plan pchronicle post-judge cleanup
reiase Aug 17, 2026
dfcaea7
style: remove orphaned pchronicle web selectors
reiase Aug 17, 2026
d82bdf2
refactor: replace pchronicle unreachable branches
reiase Aug 17, 2026
ab7e9d8
refactor: colocate storyline row model
reiase Aug 17, 2026
2e93bbb
fix: declare gateway reqwest json feature
reiase Aug 17, 2026
d95b833
refactor: consolidate agenticmd domain
reiase Aug 17, 2026
47fb74c
ci: check pchronicle feature matrix
reiase Aug 17, 2026
4c06395
docs: design pchronicle document source convergence
reiase Aug 17, 2026
c13486c
docs: plan pchronicle document source convergence
reiase Aug 17, 2026
b463711
refactor: define pchronicle document formats
reiase Aug 17, 2026
40a03f9
fix: reject malformed pchronicle documents
reiase Aug 17, 2026
363c466
feat: align storyline with atif presence semantics
reiase Aug 17, 2026
09bf2d9
fix: make actf residual roundtrips authoritative
reiase Aug 17, 2026
42b317f
fix: make openai residual roundtrips authoritative
reiase Aug 17, 2026
fedcc2c
test: enforce lossless storyline lance roundtrips
reiase Aug 17, 2026
2301bfa
refactor: project gateway agenticmd through storyline
reiase Aug 17, 2026
ed13df0
feat: unify pchronicle document sources
reiase Aug 17, 2026
f48f3a4
refactor: converge pchronicle query engine entrypoints
reiase Aug 17, 2026
5a33198
refactor: remove legacy atif data source
reiase Aug 17, 2026
bdb984a
refactor: use persisting events trajectory protocol directly
reiase Aug 17, 2026
0f9a0d9
refactor: narrow pchronicle public facade
reiase Aug 17, 2026
12e9d0e
Merge remote-tracking branch 'origin/main'
reiase Aug 18, 2026
7314a12
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 19, 2026
4a52403
docs: design storyline resource retention fixes
reiase Aug 19, 2026
33625e3
docs: plan storyline resource retention fixes
reiase Aug 19, 2026
1dae4cd
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 19, 2026
d1c063d
docs(pchronicle): design unified unknown fields residual
reiase Aug 19, 2026
a4920b2
docs(pchronicle): plan unknown fields implementation
reiase Aug 19, 2026
4b68dfb
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 20, 2026
411794d
docs(pchronicle): design storyline squash import
reiase Aug 20, 2026
ba7e669
docs(pchronicle): plan storyline squash import
reiase Aug 20, 2026
f56f578
docs: define OpenAI messages field mappings
reiase Aug 20, 2026
092dae6
docs: plan OpenAI messages field mapping
reiase Aug 20, 2026
2950a13
docs: design serve control consolidation
reiase Aug 20, 2026
33ef1f8
docs: plan serve control consolidation
reiase Aug 20, 2026
1c3a129
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 21, 2026
dc95ec2
docs: design automatic Storyline projection
reiase Aug 21, 2026
7290985
docs: plan automatic Storyline projection
reiase Aug 21, 2026
48be79c
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 21, 2026
62c76db
docs: design reliable replay modes and adapters
reiase Aug 21, 2026
5ec27cc
docs: plan persisting replay reliability work
reiase Aug 21, 2026
4582553
Merge branch 'main' of github.com:DeepLink-org/Persisting
reiase Aug 22, 2026
a6e9fe6
docs: design storyline writer fencing
reiase Aug 22, 2026
3568ca8
docs(pchronicle): design trajectory compare workspace
reiase Aug 22, 2026
96e77e4
docs: plan storyline writer fencing
reiase Aug 22, 2026
928efff
docs(pchronicle): design Copilot tool-calling loop
reiase Aug 22, 2026
dcc8565
docs(pchronicle): plan Copilot tool-calling loop
reiase Aug 22, 2026
687aa01
feat(pchronicle-web): persist Copilot threads per run key
reiase Aug 22, 2026
cc98487
fix(pchronicle-web): stop Copilot LLM compress from hanging
reiase Aug 22, 2026
bdd225f
feat(pchronicle-web): parse Copilot tool calls and JSON fallback
reiase Aug 22, 2026
508f3bc
fix(pchronicle-web): reject non-object Copilot tool arguments
reiase Aug 22, 2026
50cb4ba
fix(pchronicle-web): treat non-array tool_calls as invalid
reiase Aug 22, 2026
d5dd21f
feat(pchronicle-web): format Copilot tool results
reiase Aug 22, 2026
51d4ad2
feat(pchronicle-web): drive Copilot tool loop without an LLM
reiase Aug 22, 2026
5873b17
feat(pchronicle-web): run Copilot as a BYOK tool loop
reiase Aug 22, 2026
13ea163
refactor(pchronicle): add storyline writer control envelope
reiase Aug 22, 2026
cf0a677
fix(pchronicle): fence storyline replacements
reiase Aug 22, 2026
6785e65
fix(pchronicle-web): emit valid Copilot tool-call history
reiase Aug 22, 2026
4fc901d
fix(pchronicle-web): keep Copilot tool-call rounds and arguments
reiase Aug 22, 2026
abd323d
feat(pchronicle-web): persist Copilot chat and submit on Enter
reiase Aug 22, 2026
4cb35c0
fix(pchronicle): fence storyline maintenance
reiase Aug 22, 2026
6444b53
fix(pchronicle-web): harden Copilot JSON fallback and prompts
reiase Aug 22, 2026
26f9d85
docs(pchronicle): design Copilot analysis workspace
reiase Aug 22, 2026
7fabb9b
docs(pchronicle): plan Copilot analysis workspace
reiase Aug 22, 2026
3ca7cf1
refactor(pchronicle-web): share browser LLM client
reiase Aug 22, 2026
17511eb
feat(pchronicle-web): model analysis sessions
reiase Aug 22, 2026
9a64898
fix(pchronicle-web): fence analysis attempts
reiase Aug 22, 2026
ec2c418
fix(pchronicle-web): normalize restored analysis work
reiase Aug 22, 2026
e9a17eb
feat(pchronicle-web): profile query result columns
reiase Aug 22, 2026
0bf1006
fix(pchronicle-web): enforce categorical ratio
reiase Aug 22, 2026
77a2e5d
fix(pchronicle-web): harden result profiling
reiase Aug 22, 2026
ee71df6
feat(pchronicle-web): generate reviewable analysis plans
reiase Aug 22, 2026
0c8efe0
fix(pchronicle-web): validate analysis plan evidence
reiase Aug 22, 2026
6049a57
feat(pchronicle-web): add Copilot analysis workspace
reiase Aug 22, 2026
f5fd529
fix(pchronicle-web): fence stale analysis questions
reiase Aug 22, 2026
0b8e62f
feat(pchronicle-web): explore result distributions
reiase Aug 22, 2026
96bf54c
fix(pchronicle-web): fence stale result refinements
reiase Aug 22, 2026
3f641ae
feat(pchronicle-web): interpret analysis evidence
reiase Aug 22, 2026
4411217
feat(pchronicle-web): persist analysis sessions
reiase Aug 22, 2026
52a013d
fix(pchronicle-web): retain unpersisted analysis scope
reiase Aug 22, 2026
e3d4f0a
fix(pchronicle-web): use browser-safe timestamps
reiase Aug 22, 2026
677d7aa
fix(pchronicle-web): restore analysis after catalog load
reiase Aug 22, 2026
4839a32
test(pchronicle-web): verify Copilot analysis workflow
reiase Aug 22, 2026
771ca22
fix(pchronicle-web): close analysis review gaps
reiase Aug 22, 2026
58139e1
feat(pchronicle-cli): enhance storage management and explorer functio…
reiase Aug 23, 2026
f8b8172
chore: remove outdated implementation plans for storyline and pchroni…
reiase Aug 23, 2026
0d1779b
fix(pchronicle-cli): update projection status fixture
reiase Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions crates/persisting-pchronicle-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export support ATIF, OpenAI Messages, ACTF, and Storyline JSON.
## Orchestrator control plane

`pchronicle serve --storage URI --control 127.0.0.1:0` starts the write-capable
storage control plane used by pPilot and pVisor. It owns Run lease acquisition
storage control plane used by pPilot and pVisor. Repeat `--storage` to mount
several read-only Datasets; `--control` still requires a Dataset named
`default`. It owns Run lease acquisition
and renewal, fencing, terminal commits, Attempt registry access, and trajectory
append. The process publishes one structured readiness record through stdout,
including the bound loopback endpoint and one-time token, then serves versioned
Expand Down Expand Up @@ -115,7 +117,8 @@ Its response reports `fact_rows` and omits `input_bytes`; explicit
`--output-format preserve` is invalid for canonical events.

`serve --storage URI` converges deterministic sibling Storyline projections
before readiness and maintains them as canonical events are appended. Runtime
before readiness and maintains them as canonical events are appended. Repeated
`--storage` values each become a Dataset mount. Runtime
failures are retried without blocking durable writes. `status URI --format
json` reports each projection's state and watermark.

Expand Down
155 changes: 134 additions & 21 deletions crates/persisting-pchronicle-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,9 +491,9 @@ struct ServeArgs {
#[arg(long, value_name = "FILE", conflicts_with = "storage")]
config: Option<PathBuf>,

/// Single Dataset URI and durable Control root.
/// Dataset URI and durable Control root. Repeatable; NAME=URI overrides the derived name.
#[arg(long, value_name = "URI", conflicts_with = "config")]
storage: Option<String>,
storage: Vec<String>,

/// Loopback address for the read-only API and Web UI.
#[arg(long)]
Expand Down Expand Up @@ -1214,7 +1214,14 @@ fn write_projection_diagnostic<W: Write + ?Sized>(

async fn run_serve(args: ServeArgs, stdout: &mut dyn Write, stderr: &mut dyn Write) -> Result<()> {
let config = resolve_serve_config(&args)?;
prepare_local_control_storage(&args).await?;
let control_uri = args
.control
.is_some()
.then(|| control_storage_uri(&config).map(str::to_owned))
.transpose()?;
if let Some(uri) = control_uri.as_deref() {
prepare_local_control_storage(uri).await?;
}
let (diagnostic_tx, diagnostic_rx) = tokio::sync::mpsc::channel(256);
let mut projections =
projection_supervisor::ProjectionSupervisor::new(config.clone(), None, diagnostic_tx);
Expand All @@ -1241,9 +1248,9 @@ async fn run_serve(args: ServeArgs, stdout: &mut dyn Write, stderr: &mut dyn Wri
let control = match args.control {
Some(listen) => Some(
control::PreparedControl::bind(
args.storage
.as_deref()
.context("pChronicle Control requires --storage")?,
control_uri.as_deref().context(
"pChronicle Control requires a Dataset named 'default'; pass --storage default=URI",
)?,
listen,
)
.await?,
Expand Down Expand Up @@ -1322,15 +1329,8 @@ async fn run_serve(args: ServeArgs, stdout: &mut dyn Write, stderr: &mut dyn Wri
.await
}

async fn prepare_local_control_storage(args: &ServeArgs) -> Result<()> {
if args.control.is_none() {
return Ok(());
}
let storage = args
.storage
.as_deref()
.context("pChronicle Control requires --storage")?;
let Some(path) = local_dataset_path(storage)? else {
async fn prepare_local_control_storage(uri: &str) -> Result<()> {
let Some(path) = local_dataset_path(uri)? else {
return Ok(());
};
tokio::fs::create_dir_all(&path)
Expand All @@ -1339,16 +1339,129 @@ async fn prepare_local_control_storage(args: &ServeArgs) -> Result<()> {
}

fn resolve_serve_config(args: &ServeArgs) -> Result<server::ChronicleServerConfig> {
match (args.config.as_deref(), args.storage.as_deref()) {
(Some(config), None) => load_warehouse_config(config),
(None, Some(storage)) => server::ChronicleServerConfig::mounted(vec![DatasetMount::new(
SERVE_STORAGE_DATASET_NAME,
storage,
)?]),
match (args.config.as_deref(), args.storage.as_slice()) {
(Some(config), []) => load_warehouse_config(config),
(None, storage) if !storage.is_empty() => {
let mut config =
server::ChronicleServerConfig::mounted(resolve_storage_mounts(storage)?)?;
if config
.datasets
.iter()
.any(|dataset| dataset.name == SERVE_STORAGE_DATASET_NAME)
{
config.default_dataset = Some(SERVE_STORAGE_DATASET_NAME.into());
}
// A single unreadable source (for example a trajectory file that
// exceeds max_file_bytes) must degrade to an error source instead
// of preventing the Warehouse from serving the remaining data.
config.catalog_options.error_policy = CatalogErrorPolicy::Report;
Ok(config)
}
_ => bail!("serve requires exactly one of --config or --storage"),
}
}

fn resolve_storage_mounts(storages: &[String]) -> Result<Vec<DatasetMount>> {
anyhow::ensure!(!storages.is_empty(), "serve requires --storage");
let parsed = storages
.iter()
.map(|value| parse_storage_argument(value))
.collect::<Result<Vec<_>>>()?;
if parsed.len() == 1 {
let (name, uri) = &parsed[0];
let name = name.as_deref().unwrap_or(SERVE_STORAGE_DATASET_NAME);
return Ok(vec![DatasetMount::new(name, uri.clone())?]);
}
parsed
.into_iter()
.map(|(name, uri)| {
let name = match name {
Some(name) => name,
None => derived_dataset_name(&uri)?,
};
DatasetMount::new(name, uri)
})
.collect()
}

fn parse_storage_argument(raw: &str) -> Result<(Option<String>, String)> {
let raw = raw.trim();
anyhow::ensure!(!raw.is_empty(), "--storage URI must not be empty");
if let Some((name, uri)) = raw.split_once('=') {
if looks_like_dataset_name(name) {
let uri = uri.trim();
anyhow::ensure!(!uri.is_empty(), "--storage NAME=URI must include a URI");
return Ok((
Some(DatasetMount::new(name, "validation")?.name),
uri.to_string(),
));
}
}
Ok((None, raw.to_string()))
}

fn looks_like_dataset_name(name: &str) -> bool {
DatasetMount::new(name, "validation").is_ok()
}

fn derived_dataset_name(uri: &str) -> Result<String> {
let basename = storage_basename(uri)?;
sanitize_derived_dataset_name(&basename).with_context(|| {
format!("cannot derive Dataset name from '{uri}'; pass --storage NAME=URI")
})
}

fn storage_basename(uri: &str) -> Result<String> {
if uri.contains("://") {
let url = Url::parse(uri).with_context(|| format!("parse --storage URI '{uri}'"))?;
if let Some(segment) = url
.path_segments()
.into_iter()
.flatten()
.rev()
.find(|segment| !segment.is_empty())
{
return Ok(segment.to_string());
}
if let Some(host) = url.host_str() {
return Ok(host.to_string());
}
bail!("cannot derive Dataset name from '{uri}'; pass --storage NAME=URI");
}
match Path::new(uri).file_name().and_then(|name| name.to_str()) {
Some(name) if name != "." && name != ".." => Ok(name.to_string()),
_ => bail!("cannot derive Dataset name from '{uri}'; pass --storage NAME=URI"),
}
}

fn sanitize_derived_dataset_name(raw: &str) -> Result<String> {
let name: String = raw
.chars()
.map(|character| match character {
'-' | '.' => '_',
other => other,
})
.collect();
DatasetMount::new(&name, "validation")
.map(|mount| mount.name)
.with_context(|| {
format!(
"derived Dataset name from '{raw}' is not a valid SQL alias; pass --storage NAME=URI"
)
})
}

fn control_storage_uri(config: &server::ChronicleServerConfig) -> Result<&str> {
config
.datasets
.iter()
.find(|dataset| dataset.name == SERVE_STORAGE_DATASET_NAME)
.map(|dataset| dataset.uri.as_str())
.context(
"pChronicle Control requires a Dataset named 'default'; pass --storage default=URI",
)
}

async fn run_echo(args: EchoArgs, stderr: &mut dyn Write) -> Result<()> {
anyhow::ensure!(
args.listen.ip().is_loopback(),
Expand Down
Loading
Loading