feat: Polars row-compute engine and training-set .with glossary - #37
feat: Polars row-compute engine and training-set .with glossary#37ryan-s-roberts wants to merge 12 commits into
Conversation
Fold postfix chains into a fused RowPlan executed in plasm-runtime, replace the JSON walker, and ship .with{k: expr} with now, len, when, arith (/ and string +), and temporal day diffs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…RowPlan
Document the new .with{col: expr} postfix in the canonical language definition
(EBNF, invariants, pitfalls) and expand plasm-row-compute with expression
semantics, chaining order, disambiguation, and RowPlan/Polars execution notes.
Add glossary entries for .with and RowPlan.
Co-authored-by: Ryan Roberts <ryan-s-roberts@users.noreply.github.com>
Co-authored-by: Ryan Roberts <ryan-s-roberts@users.noreply.github.com>
Remove unused sort/compare helpers and FieldPath accessors; keep graph stream APIs test-only so lib clippy stays clean. Co-authored-by: Cursor <cursoragent@cursor.com>
Polars owns numeric sort; keep json_scalar_display for template interpolation. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Documentation review — PR #37 (Polars row-compute +
.with)Verdict: The PR's doc updates are substantially aligned with the implementation. Commits
a3cb9688/3bc99d88correctly extend the canonical language spec, row-compute user guide, glossary, concepts overview, andplasm_tool.txt. Fenced doc examples parse under the language-matrix fixture (doc_fenced_plasm_examples_parse_under_language_matrix✅).What landed well
- Single-source grammar: EBNF
WITH_EXPRdelegates toplasm-row-compute.md#derived-columns-with— avoids duplicating expression semantics in two places.- Layering: Formal grammar + invariants stay in
plasm-language-definition.md; agent-oriented examples and chaining guidance stay inplasm-row-compute.md.- Training surface:
plasm_tool.txtpostfix list includes.with{k: expr}.- Runtime architecture: New "Execution engine" section documents
RowPlan→ Polars adapter and collect barriers — matchesplasm_core::row_plan/plasm_runtime::row_compute.Gaps found (and follow-up)
Issue Antipattern Severity program_param.txtstill omits.withwhileplasm_tool.txtwas updatedMaintenance Nightmare (split training assets) High — agents read both prompts .dedupe/.distinctadded to language invariants + EBNF but absent fromplasm-row-compute.mdon this branchEaster Egg Hunt Medium WITH_EXPRoperator precedence (*//before+/-) not documentedAssumption Gap Low Binding-forms table / suffix-pipeline prose omit .withexamplesAssumption Gap Low No .withteaching TSV exemplar rows (only glossary line in tool text)Assumption Gap Low — matrix tests cover conformance plasm_language_matrix.rscoverage comment linksdocs/plasm-language-definition.md(stale path; canonical isdoc-site/docs/reference/…)Maintenance Nightmare Low doc-site/docs/index.mdquick links omit row computeEaster Egg Hunt Low Supplementary commit pushed
Branch
cursor/system-documentation-quality-4a43(based on this PR) adds:
.withtoprogram_param.txt- Dedupe/distinct section + precedence note in
plasm-row-compute.md- Binding-forms / suffix-pipeline touch-ups in
plasm-language-definition.md- Row compute quick link in
doc-site/docs/index.mdCherry-pick or merge that commit into
feat/polars-row-computebefore merge.Holistic note
doc-site/docs/crates/index.mdstill describesplasm-runtimegenerically — consider one clause mentioningrow_computenow that row plans execute there. Parser-modules table in the language definition could referenceplasm_core::row_plan::with_parsealongsideexpr_parser/postfix.rsfor.withbody parsing.Sent by Cursor Automation: Maintain docs
| Core surface: | ||
| - Get identity: `e#(id)` (parens). Query/filter: `e#{field=…}` (braces). Search when taught: `e#~$` / `e#~"text"`. | ||
| - Postfix from TSV left column: `.filter{…}` `.sort` `.limit` `.group_by` `.aggregate` `[field,…]`. | ||
| - Postfix from TSV left column: `.filter{…}` `.sort` `.limit` `.group_by` `.aggregate` `.with{k: expr}` `[field,…]`. |
There was a problem hiding this comment.
Training-set split: plasm_tool.txt now lists .with{k: expr}, but sibling asset program_param.txt (same postfix bullet, line 8) was not updated in this PR. Agents receive both strings on different tool paths — please sync, or you'll get contradictory training (Maintenance Nightmare). Fixed on branch cursor/system-documentation-quality-4a43.
| | Search filter | `e#~"…"{field=…}` | wire is the **Search**-capability param (homograph-safe vs Create/Update params) | | ||
| | Relation hop | `receiver.r#` (or wire) | `r#` resolves to a declared relation wire; a filter wire after `.` yields `RelationSegmentWrongRole` except LHS-binding coercion (see [Binding RHS shapes](#binding-rhs-shapes-label--)) | | ||
| | Projection / postfix | `[field,…]`, `.sort(field)`, `.group_by(field)`, … | wire names resolve to `rows:` field symbols under the row entity | | ||
| | Projection / postfix | `[field,…]`, `.sort(field)`, `.group_by(field)`, `.with{col: expr}`, `.dedupe(…)`, `.distinct(…)`, … | wire names resolve to `rows:` field symbols under the row entity | |
There was a problem hiding this comment.
Invariants and EBNF now list .dedupe(…) / .distinct(…) alongside .with, but plasm-row-compute.md on this branch still has no user-facing section for dedupe/distinct (only filter / group / .with). Agents looking at the row-compute guide won't find these ops — Easter Egg Hunt. Recommend a short dedupe section in row-compute (formal EBNF stays here).
| | `now` | Catalog-plane UTC clock (not a field lookup — a catalog field named `now` is shadowed) | | ||
| | `a + b`, `a - b`, `a * b`, `a / b` | Arithmetic (`+` also concatenates strings) | | ||
| | `len(field)` | String length | | ||
| | `when(lhs op rhs, then, else)` | Conditional; `op` is `=`, `!=`, `>`, `<`, `>=`, `<=` | |
There was a problem hiding this comment.
Expression table is good, but WITH_EXPR precedence is unspecified: implementation parses *// tighter than +/- (see row_plan/with_parse.rs). Worth one line here so score * 2 + 1 vs (score + 1) * 2 isn't guesswork.
|
|
||
| ## Execution engine | ||
|
|
||
| Row compute lowers fused [`ComputeOp`](https://github.com/PlasmTools/plasm-core/blob/main/crates/plasm-core/src/plasm_monad/payload/compute.rs) chains to a [`RowPlan`](https://github.com/PlasmTools/plasm-core/blob/main/crates/plasm-core/src/row_plan/plan.rs) IR in `plasm_core::row_plan`, then executes through a Polars-backed adapter in `plasm_runtime::row_compute`. Collect barriers (program return, paging, invoke-arg holes, render) are the only legal materialization points — render and derive remain outside the fused pipeline. |
There was a problem hiding this comment.
Execution engine section is accurate and welcome. Consider cross-linking collect barrier semantics back to program-return / paging docs (plasm-long-operations.md) so readers know when materialization happens, not just where in the crate graph.
Standardize agent/runtime instrumentation, wire sqlx-tracing and HTTP MakeSpan, and lock parent/child lineage with force-flush tests on real call sites. Co-authored-by: Cursor <cursoragent@cursor.com>
Align program_param.txt with plasm_tool.txt (.with, dedupe/distinct). Document dedupe/distinct and .with operator precedence in row-compute guide; extend language spec binding/suffix/parser tables; fix stale matrix doc path; add row-compute quick links; note RowPlan execution in crates index; document OTLP semantic span name contracts in plasm-otel. Co-authored-by: Ryan Roberts <ryan-s-roberts@users.noreply.github.com>
- List .dedupe/.distinct in plasm_tool.txt Core surface (parity with program_param) - Clarify .with RowIdentity and chained-RHS continuation in row-compute guide - Cross-link OTLP semantic span contract from oss-core-trace-artifacts and crates index - Align language spec binding table with row-compute continuation rules Co-authored-by: Ryan Roberts <ryan-s-roberts@users.noreply.github.com>
Co-authored-by: Ryan Roberts <ryan-s-roberts@users.noreply.github.com>
There was a problem hiding this comment.
Documentation audit — PR #37 (row compute + OTel spans)
Reviewed doc-site pages, training prompts, and implementation (binding_contract.rs, row_plan/*, with_parse.rs, language matrix) against the PR diff.
What landed well
- Split, not sprawl:
.withexpression semantics live inplasm-row-compute.md; the language definition keeps EBNF + pointers — good single-sourcing. - Surface coverage: EBNF gains
with/dedupe/distinct; invariants, binding table, suffix pipeline, parser-module table, and agent pitfalls were updated. - Agent-facing prompts:
plasm_tool.txt/program_param.txtnow teach.with,.dedupe,.distinct. - Discoverability: glossary entries, index quick-link, mkdocs nav, and crates index mention
RowPlan/ Polars execution. - Trace lane:
oss-core-trace-artifacts.mdnow points operators at theplasm-otelspan-name contract.
Gaps vs grammar / semantics (actionable)
- Continuation contract still incomplete in the canonical spec.
binding_contract.rsassignsContinuationCapability::Terminalto compute bindings whose last op is.filter,.with,.sort,.dedupe,.group_by, or.aggregate— only[projection]and.limitinherit the parent’sRelationDotanchor. The Continuation rules block (~L541–551) still says “row-preserving postfix — projection,.limit(1),.singleton()” and never tabulates this. Agents will keep writingfiltered = issues.filter{…}thenlabels = filtered.labelsunless the spec states the rule once, with a link from row-compute. - Row-compute “terminal” wording is
.with-specific in places;.filter/.sortobey the same relation-dot terminal rule (whilefiltered.with{…}on the next line still works because the RHS is a full expression, not barelabel.relation). Consolidate into one subsection. - OTel span inventory: this PR adds many
plasm_agent.*spans (oauth_link.*,plan.dry_run,plan.step_materialize, …) andspan_graph_testscontracts. The doc-site only links to the README examples table, which lists three illustrative names — not the expanded contract operators need for dashboards. - Minor navigation / maintenance (outside this diff):
concepts.mdmentions.withbut only links the language definition;crates/plasm-core/README.mdstill points at the old monorepo path for the language spec.
Antipattern check
| Pattern | Verdict |
|---|---|
| Everything Document | OK — row plane split to plasm-row-compute.md |
| Easter Egg Hunt | Mostly OK — cross-links exist; continuation table would reduce hunting |
| Assumption Gap | Fix continuation + concepts → row-compute link |
| Maintenance Nightmare | EBNF delegates WITH_EXPR to row-compute (good); chaining order duplicated lightly |
| Corporate Speak | Clean, operational prose throughout |
Recommend one follow-up doc commit: continuation contract table in plasm-language-definition.md + a Relation-dot terminal bindings subsection in plasm-row-compute.md that generalizes beyond .with.
Sent by Cursor Automation: Maintain docs
| **Row identity (`RowIdentity`)** — every row-producing plan node carries a canonical identity handle (qualified entity + [`Ref`] + ambient scope slots) in materialization, not only JSON payload. Projection and `.limit(1)` preserve identity when the suffix pipeline folds [`RowSuffix`] segments; [`PlasmInputRef::NodeInput`] holes resolve via identity, not stripped JSON paths. | ||
|
|
||
| **Suffix pipeline** — after the path head (Get/Query/label), dot/bracket segments classify as [`RowSuffix`] (relation, limit, project, sort, …) and lower through one fold (`lower_suffix_stream`), including interleaved forms such as `repo.commits.limit(1).author`. | ||
| **Suffix pipeline** — after the path head (Get/Query/label), dot/bracket segments classify as [`RowSuffix`] (relation, limit, project, filter, with, sort, dedupe, distinct, group_by, …) and lower through one fold (`lower_suffix_stream`), including interleaved forms such as `repo.commits.limit(1).author` or `issues.with{age_days: (now - updated_at)}.filter{age_days>14}`. |
There was a problem hiding this comment.
Suffix-pipeline update is accurate, but it doesn’t close the spec gap above in Continuation rules (~L541–551). Implementation (binding_contract.rs) marks .filter / .with / .sort / .dedupe / .group_by / .aggregate bindings as ContinuationCapability::Terminal (no label.<relation> on a separate line), while […] projection and .limit inherit the parent anchor.
Please add a small table there (compute op → continuation mode) and link to row-compute for agent pitfalls — otherwise the canonical spec still reads as if only projection / .limit(1) / .singleton() affect continuation.
|
|
||
| ## Derived columns (`.with`) | ||
|
|
||
| Add computed columns to each row while preserving upstream **RowIdentity** (qualified entity + ref). Relation hops and further postfix compose in the **same RHS** (`issues.with{…}.filter{…}`, `issues.with{…}.r#`) or via a follow-on binding whose RHS **starts with the prior label** plus postfix (`sorted = filtered.sort(field)`). A binding whose RHS ends on `.with{…}` alone is **terminal** for bare relation-dot (`stale.labels`) — chain on one line or bind from an earlier surface anchor. |
There was a problem hiding this comment.
The relation-dot terminal rule applies to all terminal compute bindings (.filter, .sort, .dedupe, .group_by, .aggregate, not just .with). Consider a dedicated Relation-dot terminal bindings subsection with one table instead of repeating per operator.
Clarify the positive case too: filtered = issues.filter{…} then stale = filtered.with{…} works because the RHS is a full expression referencing filtered, not bare filtered.labels.
| source → .filter{…} → .with{…} → .dedupe(…) → .group_by(…) → .sort(…) → .limit(n) → [fields] → <<TAG | ||
| ``` | ||
|
|
||
| `group_by` and `aggregate` change the row schema (terminal for relation-dot continuation on that label). After `group_by`, output columns are the **group keys** plus aggregate names (`n`, `total`, …). A chained `.sort(n, desc)` sorts on those aggregate columns — not on fields of the original catalog entity. |
There was a problem hiding this comment.
Same continuation note: .filter is equally terminal for bare label.<relation> — matrix tests already use items = LangItem.filter{…} followed by items.with{…} on the next line. Cross-link the canonical continuation table once you add it to the language definition.
| Legacy opaque `p#` tokens for fields/params are **rejected** at parse. | ||
|
|
||
| Expressions compose with pipes and postfix transforms. Multi-line payloads use tagged **heredocs** — see the [Language definition](reference/plasm-language-definition.md). | ||
| Expressions compose with pipes and postfix transforms (`.filter`, `.with`, `.sort`, `.group_by`, …). Multi-line payloads use tagged **heredocs** — see the [Language definition](reference/plasm-language-definition.md). |
There was a problem hiding this comment.
Now that row compute is a first-class reference page, link it here alongside the language definition — e.g. “postfix transforms (see Row compute)”. Avoids agents reading only the monolithic language-def for .with / dedupe semantics.
|
|
||
| - Document these vars in any **core** onboarding path (README / desktop installer), separate from Helm/object-store guides. | ||
| - Operator UIs that list traces expect agent `/v1/traces*`; durable list/detail requires local archive or sink — see [`http_traces.rs`](https://github.com/PlasmTools/plasm-core/blob/main/crates/plasm-agent-core/src/http_traces.rs). | ||
| - **OTLP export** (optional): when `OTEL_*` collectors are configured, application spans use stable semantic names (`plasm_agent.*`, `plasm_core.*`, `plasm_runtime.*`) — not Rust module paths. Contract and dashboard guidance: [`plasm-otel` README — Semantic span names](https://github.com/PlasmTools/plasm-core/blob/main/crates/plasm-otel/README.md#semantic-span-names-stable-contract). |
There was a problem hiding this comment.
Pointer to plasm-otel is good, but this PR’s span work adds a much larger stable set (plasm_agent.oauth_link.*, plasm_agent.plan.dry_run, plasm_agent.plan.step_materialize, …) with span_graph_tests parent/child contracts. Consider either (a) a short inventory subsection here, or (b) expanding the README examples table so doc-site readers don’t have to spelunk spans.rs for dashboard wiring.


Summary
RowPlanexecuted by Polars inplasm-runtime(engine stays hidden from teaching/grammar)..with{k: expr}with a training-set glossary: fields, literals,+ - * /,now, grouping parens,len,when; string+concatenates; temporal − temporal yields integer days.FillSummaryentities).Test plan
cargo test -p plasm-runtime --lib -- row_computecargo test -p plasm-core --lib -- row_plan:: with_parse with_body peel_withcargo test -p plasm-e2e --test plasm_language_matrix.with{boost: score * 2}and(now - updated_at)programsMade with Cursor