@calcit/procs 0.13.6 → 0.13.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/build.rs +60 -10
  3. package/editing-history/202608090140-record-tuple-to-struct-enum.md +38 -0
  4. package/editing-history/202608091056-import-rule-cli-regression.md +15 -0
  5. package/editing-history/202608091119-import-rule-review-followup.md +12 -0
  6. package/editing-history/202608091700-definition-attached-core-tests.md +43 -0
  7. package/editing-history/202608091723-migrate-set-string-number-method-tests.md +21 -0
  8. package/editing-history/202608091745-cr-edit-accuracy-and-output.md +34 -0
  9. package/editing-history/202608091745-internal-method-and-proc-test-migration.md +21 -0
  10. package/editing-history/202608091800-review-history-terminology.md +8 -0
  11. package/editing-history/202608091812-release-0.13.7.md +8 -0
  12. package/editing-history/202608091830-test-runner-selection-and-reporting.md +23 -0
  13. package/editing-history/202608092000-inspect-type-path-format.md +4 -0
  14. package/history/202608092318-enum-edn-option-diagnostics.md +21 -0
  15. package/history/202608100022-required-struct-field-access.md +46 -0
  16. package/history/202608100102-docs-field-access-ci.md +22 -0
  17. package/history/202608100145-cirru-edn-doc-fences.md +17 -0
  18. package/history/202608100339-struct-path-boundaries.md +11 -0
  19. package/history/202608100348-caps-branch-checkout.md +8 -0
  20. package/history/202608100349-caps-remote-branch-reset.md +8 -0
  21. package/history/202608100959-struct-path-review-followups.md +11 -0
  22. package/history/202608101042-release-0.13.8.md +6 -0
  23. package/lib/calcit.procs.d.mts +1 -0
  24. package/lib/js-cirru.mjs +7 -6
  25. package/lib/package.json +3 -2
  26. package/package.json +3 -2
  27. package/ts-src/js-cirru.mts +8 -6
Binary file
package/build.rs CHANGED
@@ -79,12 +79,22 @@ pub struct CodeEntry {
79
79
  #[serde(default)]
80
80
  pub examples: Vec<Cirru>,
81
81
  #[serde(default)]
82
+ pub tests: Vec<TestEntry>,
83
+ #[serde(default)]
82
84
  pub tags: Vec<String>,
83
85
  pub code: Cirru,
84
86
  #[serde(default)]
85
87
  pub schema: Option<Edn>,
86
88
  }
87
89
 
90
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91
+ pub struct TestEntry {
92
+ pub name: String,
93
+ pub code: Cirru,
94
+ #[serde(default)]
95
+ pub tags: Vec<String>,
96
+ }
97
+
88
98
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
89
99
  pub struct NsEntry {
90
100
  pub doc: String,
@@ -355,7 +365,7 @@ fn validate_schema_edn_no_legacy_quotes(value: &Edn, owner: &str) -> Result<(),
355
365
  walk(value, owner, &mut path)
356
366
  }
357
367
 
358
- fn parse_tags_from_edn(value: &Edn, owner: &str) -> Result<Vec<String>, String> {
368
+ fn parse_tags_from_edn(value: &Edn, owner: &str, field: &str) -> Result<Vec<String>, String> {
359
369
  match value {
360
370
  Edn::Set(set) => {
361
371
  let mut tags = Vec::with_capacity(set.0.len());
@@ -363,10 +373,7 @@ fn parse_tags_from_edn(value: &Edn, owner: &str) -> Result<Vec<String>, String>
363
373
  match item {
364
374
  Edn::Tag(tag) => tags.push(format!(":{}", tag.ref_str())),
365
375
  other => {
366
- return Err(format!(
367
- "{owner}: CodeEntry.tags expects tag items, got {}",
368
- format_edn_preview(other)
369
- ));
376
+ return Err(format!("{owner}: {field} expects tag items, got {}", format_edn_preview(other)));
370
377
  }
371
378
  }
372
379
  }
@@ -374,10 +381,7 @@ fn parse_tags_from_edn(value: &Edn, owner: &str) -> Result<Vec<String>, String>
374
381
  tags.dedup();
375
382
  Ok(tags)
376
383
  }
377
- other => Err(format!(
378
- "{owner}: CodeEntry.tags expects a hashset, got {}",
379
- format_edn_preview(other)
380
- )),
384
+ other => Err(format!("{owner}: {field} expects a hashset, got {}", format_edn_preview(other))),
381
385
  }
382
386
  }
383
387
 
@@ -388,6 +392,7 @@ fn parse_code_entry(edn: Edn, owner: &str) -> Result<CodeEntry, String> {
388
392
  };
389
393
  let mut doc = String::new();
390
394
  let mut examples: Vec<Cirru> = vec![];
395
+ let mut tests: Vec<TestEntry> = vec![];
391
396
  let mut tags: Vec<String> = Vec::new();
392
397
  let mut code: Option<Cirru> = None;
393
398
  let mut schema: Option<Edn> = None;
@@ -395,7 +400,24 @@ fn parse_code_entry(edn: Edn, owner: &str) -> Result<CodeEntry, String> {
395
400
  match key.arc_str().as_ref() {
396
401
  "doc" => doc = from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:doc`: {e}"))?,
397
402
  "examples" => examples = from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:examples`: {e}"))?,
398
- "tags" => tags = parse_tags_from_edn(value, owner)?,
403
+ "tests" => {
404
+ let Edn::List(items) = value else {
405
+ return Err(format!("{owner}: `:tests` expects a list, got {}", format_edn_preview(value)));
406
+ };
407
+ tests = items
408
+ .0
409
+ .iter()
410
+ .cloned()
411
+ .map(|item| parse_test_entry(item, owner))
412
+ .collect::<Result<Vec<_>, _>>()?;
413
+ let mut names = std::collections::HashSet::new();
414
+ for test in &tests {
415
+ if !names.insert(test.name.as_str()) {
416
+ return Err(format!("{owner}: duplicate test name `{}`", test.name));
417
+ }
418
+ }
419
+ }
420
+ "tags" => tags = parse_tags_from_edn(value, owner, "CodeEntry.tags")?,
399
421
  "code" => code = Some(from_edn(value.clone()).map_err(|e| format!("{owner}: invalid `:code`: {e}"))?),
400
422
  "schema" if !matches!(value, Edn::Nil) => {
401
423
  schema = Some(parse_schema_from_edn(value, owner).map_err(|e| format!("{owner}: invalid `:schema`: {e}"))?);
@@ -406,12 +428,40 @@ fn parse_code_entry(edn: Edn, owner: &str) -> Result<CodeEntry, String> {
406
428
  Ok(CodeEntry {
407
429
  doc,
408
430
  examples,
431
+ tests,
409
432
  tags,
410
433
  code: code.ok_or_else(|| format!("{owner}: missing `:code` field in CodeEntry"))?,
411
434
  schema,
412
435
  })
413
436
  }
414
437
 
438
+ fn parse_test_entry(edn: Edn, owner: &str) -> Result<TestEntry, String> {
439
+ let struct_value = match edn {
440
+ Edn::Struct(value) => value,
441
+ other => return Err(format!("{owner}: expected TestEntry struct, got {}", format_edn_preview(&other))),
442
+ };
443
+ let mut name = None;
444
+ let mut code = None;
445
+ let mut tags = Vec::new();
446
+ for (key, value) in &struct_value.pairs {
447
+ match key.ref_str() {
448
+ "name" => name = Some(from_edn(value.clone()).map_err(|error| format!("{owner}: invalid test `:name`: {error}"))?),
449
+ "code" => code = Some(from_edn(value.clone()).map_err(|error| format!("{owner}: invalid test `:code`: {error}"))?),
450
+ "tags" => tags = parse_tags_from_edn(value, owner, "TestEntry.tags")?,
451
+ _ => {}
452
+ }
453
+ }
454
+ let name: String = name.ok_or_else(|| format!("{owner}: test is missing `:name`"))?;
455
+ if name.trim().is_empty() {
456
+ return Err(format!("{owner}: test name must not be empty"));
457
+ }
458
+ Ok(TestEntry {
459
+ name,
460
+ code: code.ok_or_else(|| format!("{owner}: test is missing `:code`"))?,
461
+ tags,
462
+ })
463
+ }
464
+
415
465
  fn parse_ns_entry(edn: Edn, owner: &str) -> Result<NsEntry, String> {
416
466
  let struct_value: EdnStructView = match edn {
417
467
  Edn::Struct(r) => r,
@@ -0,0 +1,38 @@
1
+ # Migrate remaining record/tuple naming to struct/enum
2
+
3
+ Follow-up to the earlier terminology migration: located and updated the
4
+ remaining stale `record`/`tuple` naming that refers to the current
5
+ struct/enum data model, keeping genuine legacy-compat and EDN semantics.
6
+
7
+ ## Updated
8
+
9
+ - `calcit/test-wasm.cirru`: renamed `test-record-*` → `test-struct-*` and
10
+ `test-tuple-*` → `test-enum-*` (incl. `test-type-of-tuple` →
11
+ `test-type-of-enum`) via `cr edit rename`; updated 12 doc strings. Kept
12
+ `defrecord Point :x :y` as the WASM legacy-compat coverage.
13
+ - `scripts/test-wasm.mjs`: synced the renamed def references and section
14
+ labels.
15
+ - `src/runner/preprocess/mod.rs`: test-namespace labels `tests.record` →
16
+ `tests.struct` (kept the legacy `record` tag coverage in the loose-struct
17
+ test).
18
+ - `src/builtins/meta.rs`: `Calcit::Enum(tuple)` local bindings →
19
+ `enum_value`.
20
+ - `src/codegen/emit_wasm.rs`: comments `tuple fields`/`tuple pointer` →
21
+ `enum fields`/`enum pointer`.
22
+ - `src/bin/cli_handlers/query.rs`: test message `tuple operations` →
23
+ `enum operations`.
24
+
25
+ ## Deliberately kept
26
+
27
+ - Legacy migration tables in `docs/`, `deprecated_api.rs`, and
28
+ `removed_data_api_replacement`.
29
+ - Type-name aliases (`record`/`tuple` → Struct/Enum parsing), `SIMPLE_TYPES`
30
+ `tuple` query name, and IR/format-stable kind tags.
31
+ - `cirru_edn` `Edn::Record`/`Edn::Enum` contexts and hash prefixes.
32
+
33
+ Validation:
34
+
35
+ - `cargo test -q`(368 lib + 192 integration)
36
+ - `cargo clippy --lib --bin cr -- -D warnings`
37
+ - `cargo fmt --check`
38
+ - `yarn try-wasm`(含重命名后的 `test-struct-*` / `test-enum-*`)
@@ -0,0 +1,15 @@
1
+ # Import rule CLI regression coverage
2
+
3
+ - Added direct handler regressions for `cr edit imports` and
4
+ `cr edit add-import` when an import rule has the wrong arity.
5
+ - Both tests assert the indexed validation diagnostic and verify that the
6
+ snapshot remains byte-for-byte unchanged after rejection.
7
+ - This protects the persistence boundary in addition to the existing
8
+ `validate_import_rules` unit coverage.
9
+
10
+ Validation:
11
+
12
+ - `cargo fmt`
13
+ - `cargo clippy --bin cr -- -D warnings`
14
+ - `cargo test --bin cr` (194 passed)
15
+ - `git diff --check`
@@ -0,0 +1,12 @@
1
+ # Import rule regression review follow-up
2
+
3
+ - Changed the malformed-import persistence assertions to compare raw file
4
+ bytes instead of UTF-8 strings, matching the byte-for-byte guarantee and
5
+ keeping the tests independent of snapshot text encoding.
6
+
7
+ Validation:
8
+
9
+ - `cargo fmt`
10
+ - `cargo clippy --bin cr -- -D warnings`
11
+ - `cargo test --bin cr malformed_rule`
12
+ - `git diff --check`
@@ -0,0 +1,43 @@
1
+ # Definition-attached core tests: consolidated history
2
+
3
+ This development window introduced and completed definition-attached tests in
4
+ `src/cirru/calcit-core.cirru`, so pure `calcit.core` API contracts can run from
5
+ the definition that implements them while target-specific and integration
6
+ coverage remains in the legacy fixtures.
7
+
8
+ ## Durable design decisions
9
+
10
+ - Added backward-compatible `:tests` metadata and embedded `calcit.test`
11
+ assertions (`is`, `is=`, `is-not=`, `throws?`, `is-throws`, and `fail`).
12
+ - `cr test` discovers named tests with namespace, definition, name, and tag
13
+ filters, supports list/fail-fast/JSON modes, and uses conservative static
14
+ dependency analysis for `--affected` selection.
15
+ - Unscoped `cr test` stays project-local to the namespaces in the input
16
+ snapshot; core tests are run explicitly (including by CI) so external
17
+ projects do not execute `calcit.core` tests accidentally.
18
+ - Ordinary test execution preprocesses lazily, while `--affected` builds its
19
+ static candidate index once. This keeps fail-fast and normal runs from
20
+ compiling tests that will not execute.
21
+ - Migrated pure guards for collection, string, parsing, numeric, math, set,
22
+ destructuring, and update APIs into `calcit.core` definitions. Removed their
23
+ duplicate assertions from `calcit/test-*.cirru` while retaining method,
24
+ macro, type/preprocess, multi-definition, JavaScript, and WASM fixtures.
25
+ - CI and release workflows explicitly run the embedded core suite. A compact
26
+ JavaScript fixture remains for target-specific bitwise execution.
27
+ - Review fixes standardized whitespace/duplicate test-name validation,
28
+ preserved empty JSON report envelopes on selection errors, corrected built-in
29
+ `query tests` behavior and affected-test diagnostics, executed the
30
+ `test-comma` fixture, corrected `&map:diff-new` documentation, and removed
31
+ 25 duplicate or strict-subset attached tests. The final core suite contains
32
+ 166 tests.
33
+
34
+ ## Verification
35
+
36
+ - `yarn try-core-tests` (166 passed)
37
+ - `yarn try-rs` and `yarn try-js`
38
+ - `yarn check-all` (core, native, JavaScript, IR, WASM, and agent interface)
39
+ - `cargo fmt`, `cargo clippy -- -D warnings`, `cargo test`, `yarn compile`
40
+ - JSON test output remains one parseable report envelope.
41
+
42
+ The detailed per-commit notes were merged into this record; their exact text
43
+ remains recoverable from Git history.
@@ -0,0 +1,21 @@
1
+ # Migrate set, string, and number method tests
2
+
3
+ Move deterministic set, string, and number method assertions from the legacy
4
+ `calcit/test-*.cirru` fixtures to definition-attached tests in
5
+ `src/cirru/calcit-core.cirru`.
6
+
7
+ - Cover the internal set and string primitives at their implementation entries,
8
+ including Unicode character indexing and slicing.
9
+ - Add the remaining public numeric `empty` and `inc` checks at their definitions.
10
+ - Correct `&number:format` metadata to declare its required decimal-place
11
+ argument, which the migrated test made explicit.
12
+ - Delete the redundant `test-methods` blocks from the set, math, and string
13
+ fixtures. Keep a compact set method-dispatch smoke check plus the existing
14
+ target-specific, eval, formatting, Unicode, and WASM coverage.
15
+
16
+ Validation:
17
+
18
+ - `yarn try-core-tests` (210 passed)
19
+ - `target/debug/cr calcit/test-set.cirru`
20
+ - `target/debug/cr calcit/test-math.cirru`
21
+ - `target/debug/cr calcit/test-string.cirru`
@@ -0,0 +1,34 @@
1
+ # Improve cr edit accuracy and output
2
+
3
+ Recent definition-test migrations exposed three CLI failure modes worth fixing:
4
+
5
+ - Running a source `calcit-core.cirru` through an older `cr` binary silently
6
+ replaced its `calcit.core` namespace with the binary's embedded snapshot.
7
+ - Numeric tree paths could still point at a valid adjacent node, allowing a
8
+ delete or insertion to succeed at the wrong location.
9
+ - Tree mutations repeated the same before/after fragments, command echoes
10
+ included every inactive default, and the core suite printed hundreds of PASS
11
+ rows during the normal repository gate.
12
+
13
+ Changes:
14
+
15
+ - Preserve namespaces supplied by the input snapshot while filling only missing
16
+ core namespaces from the embedded snapshot.
17
+ - Add optional quoted-Cirru/JSON `--expect` guards to path-based replace,
18
+ delete, and insert operations. A mismatch fails before the snapshot is saved.
19
+ - Include non-default snapshot paths and all active test filters in command
20
+ echoes. Hide inactive/default options unless `--verbose` is requested.
21
+ - Remove duplicated tree insertion/deletion previews, honor `--depth`, and show
22
+ the actually modified container after child insertion. Run
23
+ `yarn try-core-tests` with `--summary-only`; summary mode also suppresses
24
+ program output from intentionally failing assertion tests.
25
+
26
+ Focused validation:
27
+
28
+ - Unit tests for exact/mismatched node guards and source-core precedence.
29
+ - A guarded deletion with the wrong expected node exited unsuccessfully and
30
+ preserved the temporary snapshot SHA-256.
31
+ - A temporary source core containing one extra test ran 211 tests while the
32
+ binary embedded 210, proving the source namespace won.
33
+ - `cargo fmt --check`, `cargo clippy -- -D warnings`, `cargo test`,
34
+ `yarn compile`, `yarn check-agent-interface`, and `yarn check-all` passed.
@@ -0,0 +1,21 @@
1
+ # Internal method and proc test migration
2
+
3
+ - Added 19 definition-attached `:unit :core` tests beside list and map method
4
+ implementations, including `&list:find-last`, `&list:find-last-index`,
5
+ `&list:map`, `&list:map-pair`, sorting, direct list primitives, map filters,
6
+ mapping, association, removal, emptiness, values, and pair conversion.
7
+ - Attached tests directly to Rust-backed core procs such as `&list:first`,
8
+ `&list:nth`, `&list:sort`, `&map:vals`, and `&map:to-list`. These tests verify
9
+ the proc contract in the core snapshot; backend-specific execution remains in
10
+ the JS/WASM fixtures.
11
+ - Removed the duplicated pure method assertion blocks from `calcit/test-list.cirru`
12
+ and `calcit/test-map.cirru`. Kept list shorthand method coverage and the
13
+ remaining method/type/backend integration fixtures.
14
+
15
+ Validation:
16
+
17
+ - `yarn try-core-tests` (186 passed)
18
+ - `yarn check-all` (core, native, JavaScript, IR, WASM, and agent interface)
19
+ - `cr calcit/test-list.cirru`, `cr calcit/test-map.cirru`, and
20
+ `cr calcit/test-string.cirru`
21
+ - `git diff --check`
@@ -0,0 +1,8 @@
1
+ # Review history terminology
2
+
3
+ - Corrected the retained definition-attached-test history to use the standard
4
+ spelling “built-in” when referring to the `query tests` behavior.
5
+
6
+ Validation:
7
+
8
+ - `git diff --check`
@@ -0,0 +1,8 @@
1
+ # Release 0.13.7
2
+
3
+ Released the merged definition-attached core test and `cr` CLI accuracy work.
4
+
5
+ - Bumped the Rust crate and npm package versions from `0.13.6` to `0.13.7`.
6
+ - Updated `Cargo.lock` with the workspace version through `cargo update --workspace`.
7
+ - Release validation covers the merged PR's core tests, native/JS/WASM targets,
8
+ Agent CLI interface, and the repository test workflow before tagging.
@@ -0,0 +1,23 @@
1
+ # Test runner selection and reporting guardrails
2
+
3
+ - Added `cr test --exclude-tag` so CI and agents can leave slow or integration
4
+ guards out of a focused run without weakening the default suite.
5
+ - Added `--require-match` to make an empty scoped/tagged/affected selection fail
6
+ instead of producing an accidental green result.
7
+ - Added `--summary-only` for compact large-suite output. JSON reports now state
8
+ `detail`, distinguish selected from executed tests, and include per-test
9
+ execution durations in full reports.
10
+ - Kept test execution native-only. JS/WASM checks remain dedicated fixtures;
11
+ an in-process timeout was intentionally not added because the interpreter
12
+ cannot safely cancel a running test thread.
13
+
14
+ Validation:
15
+
16
+ - `cargo fmt --check`
17
+ - `cargo clippy -- -D warnings`
18
+ - `cargo test`
19
+ - `yarn compile`
20
+ - `yarn check-agent-interface`
21
+ - `yarn check-all`
22
+ - JSON summary and empty-selection CLI smoke checks
23
+ - `git diff --check`
@@ -0,0 +1,4 @@
1
+ # `&inspect-type` 坐标输出统一
2
+
3
+ - 将 `&inspect-type` 诊断中的源码坐标从 `[@3.5.1]` 调整为 `@3.5.1`。
4
+ - 复用统一的 `@` 路径格式,并增加 Rust 回归测试,避免方括号格式再次出现。
@@ -0,0 +1,21 @@
1
+ # Cirru EDN enum identity and Option migration diagnostics
2
+
3
+ ## 背景
4
+
5
+ - GitHub #326 暴露 JS `format-cirru-edn` / `parse-cirru-edn` 往返后 enum variant 从 interned `CalcitTag` 变成 `CalcitSymbol`,导致生成的 `match` 按身份比较时无法命中。
6
+ - GitHub #325 暴露 Option migration 诊断仍使用预处理前的 source symbol,普通 `=`, `some?`, `assoc` 等调用不会触发告警,并且 `starts-with?` / `ends-with?` 的 proc metadata 比公开 schema 更宽。
7
+
8
+ ## 修改与边界
9
+
10
+ - JS Cirru EDN parser 在匿名和具名 enum 分支统一把解析出的 symbol variant 通过 `newTag` intern;quoted nominal name 也按 tag key 恢复 options map 中的 `CalcitEnumDef`。
11
+ - Option migration diagnostics 改用预处理后的 `head_form` / `call_head`,避免漏掉普通 source syntax,同时继续区分应用自行定义的同名函数。
12
+ - 结构操作诊断覆盖 `assoc`, `dissoc`, `merge`, `update` 及其 nested/non-nil variants。
13
+ - `starts-with?` / `ends-with?` proc 参数 metadata 收紧为 `String × String`,与文档和 core schema 一致;旧 Tag runtime 兼容测试用显式 `unsafe-coerce` 标明边界。
14
+ - 合法的 nominal enum equality 继续允许,包括类型推断尚未完成时,Option/Result constructor 和已声明返回 nominal enum 的调用之间的比较。
15
+
16
+ ## 验证
17
+
18
+ - JS runtime identity check 覆盖匿名 enum、具名 enum prototype 恢复和 identity-based match 条件。
19
+ - Rust end-to-end snippet test 覆盖 issue 中的 `get-env`, `some?`, `update-in/assoc`, `nth/starts-with?` 普通源码写法。
20
+ - `cargo fmt --all`, `cargo clippy -- -D warnings`, `yarn compile`, `cargo test`, `yarn check-all` 全部通过。
21
+ - 安装当前全局 `cr` 后在 Respo 执行 `cr --check-only`,成功在真实项目中报告 3 个 Option migration warning(另有 1 个既有 JS nullable warning),未进入相关运行时失败路径。
@@ -0,0 +1,46 @@
1
+ # Required Struct field access
2
+
3
+ ## Background
4
+
5
+ Field syntax previously changed its contract according to inferred receiver
6
+ type: `(:field value)` returned the declared payload for a known Struct, but
7
+ silently lowered to Option-producing `get` when the receiver looked like a Map
8
+ or lost type information. This made local edits unstable: adding or losing a
9
+ type annotation changed downstream control flow without changing source syntax.
10
+
11
+ Issue #325 clarified that dynamic container absence and required model fields
12
+ are different boundaries. AI-assisted edits need those boundaries to remain
13
+ visible so diagnostics guide code toward a declared model instead of allowing
14
+ `Dynamic`/`Option` patches to spread.
15
+
16
+ ## Contract
17
+
18
+ - `(:field value)` is required access. The receiver must be a statically known
19
+ named Struct and the field must be declared. Its result is the declared field
20
+ type.
21
+ - `get` and `get-in` are explicit partial lookup APIs for Map and indexed/path
22
+ access. They return nominal `Option<T>`.
23
+ - `get` on a Struct is rejected with a diagnostic pointing to required field
24
+ syntax; the runtime also rejects dynamically hidden Struct receivers.
25
+ - Loose/anonymous Struct field access is rejected until an expected named
26
+ Struct type rewrites or narrows it.
27
+ - Low-level `&struct:get` remains available for internal dynamic boundaries
28
+ such as reusable trait implementation bodies.
29
+
30
+ ## Type inference details
31
+
32
+ - `update` preserves the receiver's collection/Struct type.
33
+ - `&struct:from-map` preserves the Struct definition supplied as its first
34
+ argument.
35
+ - Struct pattern-match expansion is exempt from source field validation because
36
+ all nominal branches are preprocessed before runtime guards select a variant.
37
+
38
+ ## Diagnostics
39
+
40
+ - `W_REQUIRED_STRUCT_FIELD_TYPE`: required field syntax lacks a named Struct
41
+ contract.
42
+ - `W_UNKNOWN_STRUCT_FIELD`: a named Struct does not declare the requested field.
43
+ - `W_STRUCT_FIELD_OPTIONAL_LOOKUP`: `get` is used on a Struct.
44
+
45
+ Tests cover typed access, Map fallback rejection, unknown fields, `get` misuse,
46
+ nominal type preservation, loose Struct access, and the existing Struct suite.
@@ -0,0 +1,22 @@
1
+ # Documentation validation for required Struct fields
2
+
3
+ ## Background
4
+
5
+ The PR's local `yarn check-all` suite passed, but GitHub's Test workflow also
6
+ runs `scripts/check-docs-md.sh`. Several executable documentation snippets
7
+ still relied on the former context-sensitive field syntax.
8
+
9
+ ## Documentation migration
10
+
11
+ - Map reads that branch on absence retain their `Option` value for `tag-match`
12
+ instead of unwrapping it first.
13
+ - Generic enum payloads, local trait implementations, generic Struct bodies,
14
+ and anonymous Struct examples use explicit `&struct:get` when their receiver
15
+ has no statically resolvable named Struct declaration.
16
+ - Snapshot/schema fragments now use standard `cirru.edn` fences with valid
17
+ EDN roots and quoted symbols, so `check-md` validates them as data instead
18
+ of evaluating tags such as `:entries` or `:where` as required field access.
19
+
20
+ ## Verification
21
+
22
+ - `bash scripts/check-docs-md.sh`: 54 files, 286 blocks, all passed.
@@ -0,0 +1,17 @@
1
+ # Standard Cirru EDN documentation fences
2
+
3
+ ## Background
4
+
5
+ Executable documentation fences use the standard `cirru.edn` marker. The
6
+ non-standard `cirru-edn` spelling bypassed EDN validation and hid malformed
7
+ configuration and schema fragments.
8
+
9
+ ## Change
10
+
11
+ - Replaced all newly introduced `cirru-edn` fences with `cirru.edn`.
12
+ - Wrapped standalone configuration/schema fields in top-level EDN maps.
13
+ - Quoted symbolic trait references inside EDN schema values.
14
+
15
+ ## Verification
16
+
17
+ - `bash scripts/check-docs-md.sh`: 54 files and 290 blocks passed.
@@ -0,0 +1,11 @@
1
+ ## Struct path API boundaries
2
+
3
+ - Public collection path APIs (`get-in`, `assoc-in`, `update-in`, `dissoc-in`)
4
+ now stop at nominal Struct boundaries. Required fields remain visible as direct
5
+ `(:field value)` accesses, preserving their declared types for the checker.
6
+ - The preprocessor diagnoses literal and dynamic paths that would enter a Struct;
7
+ `get-in` no longer infers a field type through such a path.
8
+ - Runtime core implementations reject the same traversal after a Dynamic boundary,
9
+ so unsafe coercion cannot silently bypass the typed field-access contract.
10
+ - Updated the type-inference fixture to use nested direct field access instead of
11
+ `get-in` across a Struct.
@@ -0,0 +1,8 @@
1
+ ## caps remote branch checkout
2
+
3
+ - `caps` now fetches remote branch refs as well as tags when resolving an
4
+ existing module clone.
5
+ - If a requested branch exists only as `origin/<branch>`, checkout now creates
6
+ a local tracking branch instead of failing after the fetch succeeds.
7
+ - This keeps branch-based module integration reproducible through `caps
8
+ download`, without manually editing the module cache.
@@ -0,0 +1,8 @@
1
+ ## caps remote branch checkout hardening
2
+
3
+ - Existing module clones can use narrow or non-standard remote refspecs, for
4
+ which `git checkout --track origin/<branch>` may reject an otherwise fetched
5
+ remote ref.
6
+ - `caps` now creates/resets the requested local branch with `git checkout -B
7
+ <branch> origin/<branch>`, keeping branch-based module downloads reliable and
8
+ compatible with later `--pull-branch` updates.
@@ -0,0 +1,11 @@
1
+ ## Struct path-operation review follow-ups
2
+
3
+ - `contains-in?` now follows the same Struct boundary as the other collection
4
+ path APIs: a path may not enter a declared Struct field. Direct typed field
5
+ access keeps required-field diagnostics and return types precise.
6
+ - The preprocessor reports `W_STRUCT_PATH_OPERATION` for `contains-in?`, with
7
+ a regression test covering all five path APIs.
8
+ - Runtime guidance for `dissoc-in` now explains that declared Struct fields
9
+ cannot be removed; model optionality explicitly or convert to a map first.
10
+ - Module ref fetching now uses `--prune`, so stale remote-tracking branches do
11
+ not remain selectable after an upstream branch deletion.
@@ -0,0 +1,6 @@
1
+ ## Release 0.13.8
2
+
3
+ - Publishes the typed Struct field-access and collection path-boundary work
4
+ merged through PR #327.
5
+ - Keeps module branch resolution robust by fetching and pruning remote branch
6
+ refs before checkout.
@@ -23,6 +23,7 @@ export declare const calcit_package_json: {
23
23
  "check-all": string;
24
24
  "check-agent-interface": string;
25
25
  "try-all": string;
26
+ "try-core-tests": string;
26
27
  "try-rs": string;
27
28
  "warn-dyn-method": string;
28
29
  "try-js-brk": string;
package/lib/js-cirru.mjs CHANGED
@@ -225,13 +225,17 @@ let recordFieldOrder = (a, b) => {
225
225
  };
226
226
  /** makes sure we got string */
227
227
  let extractFieldTag = (x) => {
228
- if (x[0] === ":") {
228
+ if (x[0] === ":" || x[0] === "'") {
229
229
  return newTag(x.slice(1));
230
230
  }
231
231
  else {
232
232
  return newTag(x);
233
233
  }
234
234
  };
235
+ let extractEnumTag = (x, options, preserveSourceEntries) => {
236
+ const parsedTag = extract_cirru_edn_inner(x, options, preserveSourceEntries);
237
+ return parsedTag instanceof CalcitSymbol ? newTag(parsedTag.value) : parsedTag;
238
+ };
235
239
  let resolveEnumPrototype = (enumName, options) => {
236
240
  if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
237
241
  let value = options.get(extractFieldTag(enumName));
@@ -412,7 +416,7 @@ const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
412
416
  if (x.length < 2) {
413
417
  throw new Error(`anonymous enum expects at least 1 value, got: ${x}`);
414
418
  }
415
- return new CalcitEnumValue(extract_cirru_edn_inner(x[1], options, preserveSourceEntries), x
419
+ return new CalcitEnumValue(extractEnumTag(x[1], options, preserveSourceEntries), x
416
420
  .slice(2)
417
421
  .filter(notComment)
418
422
  .map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)));
@@ -426,10 +430,7 @@ const extract_cirru_edn_inner = (x, options, preserveSourceEntries) => {
426
430
  throw new Error(`Expected string for enum name, got: ${enumName}`);
427
431
  }
428
432
  let enumPrototype = resolveEnumPrototype(enumName, options);
429
- // unwrap prototype to record then extract name
430
- const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
431
- const enumTag = proto != null ? proto.name.toString() : enumName;
432
- return new CalcitEnumValue(extract_cirru_edn_inner(x[2], options, preserveSourceEntries), x
433
+ return new CalcitEnumValue(extractEnumTag(x[2], options, preserveSourceEntries), x
433
434
  .slice(3)
434
435
  .filter(notComment)
435
436
  .map((x) => extract_cirru_edn_inner(x, options, preserveSourceEntries)), enumPrototype);
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.6",
3
+ "version": "0.13.8",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
@@ -19,9 +19,10 @@
19
19
  "check-js-runtime": "node scripts/check-js-runtime-identity.mjs",
20
20
  "bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru js && node --input-type=module -e \"import { test_loop } from './js-out/test-recursion.main.mjs'; const n=3000; const t0=process.hrtime.bigint(); for(let i=0;i<n;i++) test_loop(); const dt=Number(process.hrtime.bigint()-t0)/1e6; console.log('test_loop_ms='+dt.toFixed(3));\"",
21
21
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
22
- "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
22
+ "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
23
23
  "check-agent-interface": "cargo build --bin cr && node scripts/check-agent-interface.mjs",
24
24
  "try-all": "yarn check-all",
25
+ "try-core-tests": "cargo run --bin cr -- src/cirru/calcit-core.cirru test --tag unit --summary-only",
25
26
  "try-rs": "cargo run --bin cr -- calcit/test.cirru",
26
27
  "warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
27
28
  "try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.6",
3
+ "version": "0.13.8",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
@@ -19,9 +19,10 @@
19
19
  "check-js-runtime": "node scripts/check-js-runtime-identity.mjs",
20
20
  "bench-recur-smoke": "cargo run --bin cr -- calcit/test.cirru js && node --input-type=module -e \"import { test_loop } from './js-out/test-recursion.main.mjs'; const n=3000; const t0=process.hrtime.bigint(); for(let i=0;i<n;i++) test_loop(); const dt=Number(process.hrtime.bigint()-t0)/1e6; console.log('test_loop_ms='+dt.toFixed(3));\"",
21
21
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
22
- "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
22
+ "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
23
23
  "check-agent-interface": "cargo build --bin cr && node scripts/check-agent-interface.mjs",
24
24
  "try-all": "yarn check-all",
25
+ "try-core-tests": "cargo run --bin cr -- src/cirru/calcit-core.cirru test --tag unit --summary-only",
25
26
  "try-rs": "cargo run --bin cr -- calcit/test.cirru",
26
27
  "warn-dyn-method": "cargo run --bin cr -- calcit/test.cirru --warn-dyn-method",
27
28
  "try-js-brk": "cargo run --bin cr -- calcit/test.cirru js && node --inspect-brk js-out/main.mjs",
@@ -220,13 +220,18 @@ let recordFieldOrder = (a: [string, CirruEdnFormat], b: [string, CirruEdnFormat]
220
220
 
221
221
  /** makes sure we got string */
222
222
  let extractFieldTag = (x: string) => {
223
- if (x[0] === ":") {
223
+ if (x[0] === ":" || x[0] === "'") {
224
224
  return newTag(x.slice(1));
225
225
  } else {
226
226
  return newTag(x);
227
227
  }
228
228
  };
229
229
 
230
+ let extractEnumTag = (x: CirruEdnFormat, options: CalcitValue, preserveSourceEntries: boolean): CalcitValue => {
231
+ const parsedTag = extract_cirru_edn_inner(x, options, preserveSourceEntries);
232
+ return parsedTag instanceof CalcitSymbol ? newTag(parsedTag.value) : parsedTag;
233
+ };
234
+
230
235
  let resolveEnumPrototype = (enumName: string, options: CalcitValue) => {
231
236
  if (options instanceof CalcitMap || options instanceof CalcitSliceMap) {
232
237
  let value = options.get(extractFieldTag(enumName));
@@ -405,7 +410,7 @@ const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preser
405
410
  throw new Error(`anonymous enum expects at least 1 value, got: ${x}`);
406
411
  }
407
412
  return new CalcitEnumValue(
408
- extract_cirru_edn_inner(x[1], options, preserveSourceEntries),
413
+ extractEnumTag(x[1], options, preserveSourceEntries),
409
414
  x
410
415
  .slice(2)
411
416
  .filter(notComment)
@@ -421,11 +426,8 @@ const extract_cirru_edn_inner = (x: CirruEdnFormat, options: CalcitValue, preser
421
426
  throw new Error(`Expected string for enum name, got: ${enumName}`);
422
427
  }
423
428
  let enumPrototype = resolveEnumPrototype(enumName, options);
424
- // unwrap prototype to record then extract name
425
- const proto = enumPrototype != null ? unwrap_enum_prototype_local(enumPrototype) : null;
426
- const enumTag = proto != null ? proto.name.toString() : enumName;
427
429
  return new CalcitEnumValue(
428
- extract_cirru_edn_inner(x[2], options, preserveSourceEntries),
430
+ extractEnumTag(x[2], options, preserveSourceEntries),
429
431
  x
430
432
  .slice(3)
431
433
  .filter(notComment)