@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.
- package/.yarn/install-state.gz +0 -0
- package/build.rs +60 -10
- package/editing-history/202608090140-record-tuple-to-struct-enum.md +38 -0
- package/editing-history/202608091056-import-rule-cli-regression.md +15 -0
- package/editing-history/202608091119-import-rule-review-followup.md +12 -0
- package/editing-history/202608091700-definition-attached-core-tests.md +43 -0
- package/editing-history/202608091723-migrate-set-string-number-method-tests.md +21 -0
- package/editing-history/202608091745-cr-edit-accuracy-and-output.md +34 -0
- package/editing-history/202608091745-internal-method-and-proc-test-migration.md +21 -0
- package/editing-history/202608091800-review-history-terminology.md +8 -0
- package/editing-history/202608091812-release-0.13.7.md +8 -0
- package/editing-history/202608091830-test-runner-selection-and-reporting.md +23 -0
- package/editing-history/202608092000-inspect-type-path-format.md +4 -0
- package/history/202608092318-enum-edn-option-diagnostics.md +21 -0
- package/history/202608100022-required-struct-field-access.md +46 -0
- package/history/202608100102-docs-field-access-ci.md +22 -0
- package/history/202608100145-cirru-edn-doc-fences.md +17 -0
- package/history/202608100339-struct-path-boundaries.md +11 -0
- package/history/202608100348-caps-branch-checkout.md +8 -0
- package/history/202608100349-caps-remote-branch-reset.md +8 -0
- package/history/202608100959-struct-path-review-followups.md +11 -0
- package/history/202608101042-release-0.13.8.md +6 -0
- package/lib/calcit.procs.d.mts +1 -0
- package/lib/js-cirru.mjs +7 -6
- package/lib/package.json +3 -2
- package/package.json +3 -2
- package/ts-src/js-cirru.mts +8 -6
package/.yarn/install-state.gz
CHANGED
|
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
|
-
"
|
|
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
|
+
# 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,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.
|
package/lib/calcit.procs.d.mts
CHANGED
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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/ts-src/js-cirru.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
430
|
+
extractEnumTag(x[2], options, preserveSourceEntries),
|
|
429
431
|
x
|
|
430
432
|
.slice(3)
|
|
431
433
|
.filter(notComment)
|