@neocompose/cli 0.22.6 → 0.22.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.22.7] - 2026-08-05
4
+
5
+ ### Fixed
6
+
7
+ - Pull and reset cache canonical required-constructor calls reconstructed from
8
+ legacy materialized rows that predate durable constructor arguments.
9
+ - A pull followed by status or push remains a semantic no-op for projected
10
+ animation track and generic segment-frame rows, while later constructor edits
11
+ are still detected normally.
12
+
3
13
  ## [0.22.6] - 2026-08-05
4
14
 
5
15
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -69928,7 +69928,9 @@ function materializedInitializerReconciliationFailuresV4(args) {
69928
69928
  [...args.reconciliations.values()].map((entry) => [entry.valueId, entry])
69929
69929
  );
69930
69930
  const candidateValueIds = new Set(
69931
- [...reconciliationByValueId.values()].filter((reconciliation) => reconciliation.storedConstructorArgs !== null).map((reconciliation) => reconciliation.valueId)
69931
+ [...reconciliationByValueId.values()].filter(
69932
+ (reconciliation) => reconciliation.storedConstructorArgs !== null || reconciliation.requiresCanonicalConstruction === true
69933
+ ).map((reconciliation) => reconciliation.valueId)
69932
69934
  );
69933
69935
  const candidateOwners = resolveOwnerMembersForValues(
69934
69936
  document,
@@ -69968,13 +69970,24 @@ function materializedInitializerReconciliationFailuresV4(args) {
69968
69970
  }
69969
69971
  }
69970
69972
  }
69973
+ const authoredConstructionChangedByValueId = /* @__PURE__ */ new Map();
69971
69974
  const replayableValueIds = /* @__PURE__ */ new Set();
69972
69975
  for (const valueId of candidateValueIds) {
69973
69976
  const reconciliation = reconciliationByValueId.get(valueId);
69974
69977
  const owner = candidateOwners.get(valueId);
69975
69978
  if (reconciliation === void 0 || owner === void 0) continue;
69976
- const authoredConstructionChanged = args.manifest !== void 0 && canonicalExpressions.get(valueId) !== constructorExpressionSlice(reconciliation.code);
69977
- if (authoredConstructionChanged || recompileTargets.valueIds.has(valueId)) {
69979
+ const canonicalExpression = canonicalExpressions.get(valueId);
69980
+ if (args.manifest !== void 0 && canonicalExpression === void 0) {
69981
+ throw new Error(
69982
+ `Cannot reconcile stored construction for value "${valueId}": its canonical pulled constructor expression is unavailable.`
69983
+ );
69984
+ }
69985
+ const authoredConstructionChanged = args.manifest !== void 0 && canonicalExpression !== constructorExpressionSlice(reconciliation.code);
69986
+ authoredConstructionChangedByValueId.set(
69987
+ valueId,
69988
+ authoredConstructionChanged
69989
+ );
69990
+ if (reconciliation.storedConstructorArgs !== null && (authoredConstructionChanged || recompileTargets.valueIds.has(valueId))) {
69978
69991
  replayableValueIds.add(valueId);
69979
69992
  }
69980
69993
  }
@@ -69982,7 +69995,26 @@ function materializedInitializerReconciliationFailuresV4(args) {
69982
69995
  const compilationProject = replayableValueIds.size === 0 ? void 0 : args.useBuildCaches === false ? createNeoScriptCompilationProject(document) : loadOrBuildNeoScriptProjectV1(args.workspace.root, document);
69983
69996
  const failures = [];
69984
69997
  for (const reconciliation of args.reconciliations.values()) {
69985
- if (reconciliation.storedConstructorArgs === null) continue;
69998
+ if (reconciliation.storedConstructorArgs === null) {
69999
+ if (reconciliation.requiresCanonicalConstruction !== true) continue;
70000
+ if (authoredConstructionChangedByValueId.get(reconciliation.valueId) !== true) {
70001
+ continue;
70002
+ }
70003
+ const position2 = composeReferenceSitePosition(
70004
+ reconciliation.site,
70005
+ args.sourceTextByUri
70006
+ );
70007
+ failures.push(
70008
+ new SchemaSourceError(
70009
+ `construction-arguments-conflict: Value "${reconciliation.valueId}" is already materialized without durable constructor arguments, and this construction no longer matches its pulled canonical projection. Delete and recreate the value to change its construction.`,
70010
+ position2.file,
70011
+ position2.line,
70012
+ position2.column,
70013
+ "construction-arguments-conflict"
70014
+ )
70015
+ );
70016
+ continue;
70017
+ }
69986
70018
  if (!replayableValueIds.has(reconciliation.valueId)) continue;
69987
70019
  const owner = owners.get(reconciliation.valueId);
69988
70020
  if (owner === void 0) {
@@ -70525,7 +70557,12 @@ function computeWorkspaceStatus(workspace, options) {
70525
70557
  seeds: /* @__PURE__ */ new Map()
70526
70558
  };
70527
70559
  let projectAnalysisV4 = null;
70528
- const valueLowerRegistry = createValueLowerRegistryV4();
70560
+ const valueLowerRegistry = createValueLowerRegistryV4({
70561
+ materializedConstructorExpressions: options.useBuildCaches === false ? /* @__PURE__ */ new Map() : readMaterializedConstructionBuildCacheV1(
70562
+ workspace.root,
70563
+ workspace.state
70564
+ ) ?? /* @__PURE__ */ new Map()
70565
+ });
70529
70566
  try {
70530
70567
  if (!baseManifest) {
70531
70568
  throw new Error(
@@ -71593,6 +71630,7 @@ var init_workspace_status_core = __esm({
71593
71630
  init_push_change_intent();
71594
71631
  init_initializer_replay();
71595
71632
  init_compiler_adapter();
71633
+ init_materialized_construction_cache();
71596
71634
  IGNORED_SCHEMA_DIRECTORIES = /* @__PURE__ */ new Set([
71597
71635
  ".git",
71598
71636
  ".neo",
@@ -81868,11 +81906,20 @@ function emitStoredConstructorExpression(context, member, valueId) {
81868
81906
  value,
81869
81907
  instanceGenericEnvironment(context, classId, resolvedMember, void 0)
81870
81908
  );
81871
- const targetValueIds = animationConstructorProjectionTargetValueIds(
81909
+ if (!isObjectRecord2(value.value)) {
81910
+ throw new Error(
81911
+ `Stored materialized value ${valueId} has no Class body to project.`
81912
+ );
81913
+ }
81914
+ const hasStoredConstruction = isObjectRecord2(value.constructorArgs);
81915
+ const projection = hasStoredConstruction ? null : constructorProjectionSource(
81872
81916
  context,
81873
81917
  classId,
81874
- value
81918
+ value.value,
81919
+ /* @__PURE__ */ new Set([valueId]),
81920
+ storedEnvironment
81875
81921
  );
81922
+ const targetValueIds = projection?.targetValueIds ?? animationConstructorProjectionTargetValueIds(context, classId, value);
81876
81923
  const environment = inferAnimationChildOverrideEmitEnvironment(
81877
81924
  context,
81878
81925
  classId,
@@ -81894,12 +81941,13 @@ function emitStoredConstructorExpression(context, member, valueId) {
81894
81941
  /* @__PURE__ */ new Set([valueId]),
81895
81942
  false
81896
81943
  );
81897
- if (constructor2 === null) {
81944
+ if (constructor2 !== null) return constructor2;
81945
+ if (projection === null || projection.arguments.length === 0 && schemaClass2.requiredConstructorId === void 0) {
81898
81946
  throw new Error(
81899
81947
  `Stored materialized value ${valueId} has no constructor arguments.`
81900
81948
  );
81901
81949
  }
81902
- return constructor2;
81950
+ return projection.arguments.length === 0 ? `new ${className}()` : constructorCallSource(`new ${className}`, projection.arguments);
81903
81951
  }
81904
81952
  function animationConstructorProjectionTargetValueIds(context, classId, value) {
81905
81953
  if (context.manifestClasses.get(classId)?.system?.worldKind !== "animationChildOverride" || !isObjectRecord2(value.value)) {
@@ -82040,8 +82088,9 @@ function qualifiedProjectFileSymbolsV4(records2) {
82040
82088
  }
82041
82089
  return result;
82042
82090
  }
82043
- function createValueLowerRegistryV4() {
82091
+ function createValueLowerRegistryV4(options = {}) {
82044
82092
  return {
82093
+ materializedConstructorExpressions: options.materializedConstructorExpressions ?? /* @__PURE__ */ new Map(),
82045
82094
  pendingValues: /* @__PURE__ */ new Map(),
82046
82095
  pendingLocalizedTexts: /* @__PURE__ */ new Map(),
82047
82096
  pendingBindingMembersByClassId: /* @__PURE__ */ new Map(),
@@ -82145,7 +82194,8 @@ function buildValueLowerContext(state, manifest, options = {}) {
82145
82194
  referenceObligations: registry.referenceObligations,
82146
82195
  loweringFailures: registry.loweringFailures,
82147
82196
  pendingValueIdentitySites: registry.pendingValueIdentitySites,
82148
- initializerReconciliations: registry.initializerReconciliations
82197
+ initializerReconciliations: registry.initializerReconciliations,
82198
+ materializedConstructorExpressions: registry.materializedConstructorExpressions
82149
82199
  };
82150
82200
  }
82151
82201
  function indexMemberOverrides(members) {
@@ -83590,20 +83640,28 @@ function lowerValueRow(context, member, sourceExpression, expectedValueId, sourc
83590
83640
  }
83591
83641
  indexInitAuthoredRowIds(context, code, source.label);
83592
83642
  const storedConstructorArgs = isObjectRecord2(base.constructorArgs) ? base.constructorArgs : null;
83593
- const materializedPlainClass = resolvedMember.kind === "class" && isObjectRecord2(base.value) && context.classes.get(
83643
+ const materializedClass = resolvedMember.kind === "class" && isObjectRecord2(base.value);
83644
+ const materializedPlainClass = materializedClass && context.classes.get(
83594
83645
  typeof base.classId === "string" ? base.classId : resolvedMember.classId
83595
83646
  )?.requiredConstructorId === void 0;
83596
- if (storedConstructorArgs !== null || materializedPlainClass) {
83647
+ const requiresCanonicalConstruction = storedConstructorArgs === null && materializedClass && !materializedPlainClass;
83648
+ const canonicalConstruction = context.materializedConstructorExpressions.get(expectedValueId);
83649
+ const authoredConstruction = constructorExpressionSlice(code);
83650
+ const canonicalConstructionMatches = canonicalConstruction !== void 0 && canonicalConstruction === authoredConstruction;
83651
+ const canonicalConstructionMissing = requiresCanonicalConstruction && canonicalConstruction === void 0;
83652
+ const preserveRequiredStoredConstruction = requiresCanonicalConstruction && source.purpose === "stored";
83653
+ if (storedConstructorArgs !== null || materializedPlainClass || canonicalConstructionMatches || canonicalConstructionMissing || preserveRequiredStoredConstruction) {
83597
83654
  if (resolvedMember.kind !== "class") {
83598
83655
  throw new Error(
83599
83656
  `Value ${expectedValueId} stores constructor arguments but its declared member ${resolvedMember.name} is not a Class.`
83600
83657
  );
83601
83658
  }
83602
- if (source.purpose === "stored") {
83659
+ if (source.purpose === "stored" || canonicalConstructionMissing) {
83603
83660
  context.initializerReconciliations.set(expectedValueId, {
83604
83661
  valueId: expectedValueId,
83605
83662
  code,
83606
83663
  storedConstructorArgs: storedConstructorArgs === null ? null : structuredClone(storedConstructorArgs),
83664
+ ...requiresCanonicalConstruction ? { requiresCanonicalConstruction: true } : {},
83607
83665
  settleFields: expression.kind === "new" ? (expression.initializer ?? []).map(
83608
83666
  (assignment) => assignment.name
83609
83667
  ) : [],
@@ -85781,9 +85839,6 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
85781
85839
  { cause: error }
85782
85840
  );
85783
85841
  }
85784
- if (storedConstruction !== null && typeof value.id === "string") {
85785
- context.materializedConstructors.set(value.id, storedConstruction);
85786
- }
85787
85842
  const replayConstruction = storedConstruction === null ? null : storedConstructorCallSource(
85788
85843
  context,
85789
85844
  schemaClass2,
@@ -85862,6 +85917,9 @@ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
85862
85917
  }`;
85863
85918
  }
85864
85919
  const constructor2 = storedConstruction !== null ? storedConstruction : projection.arguments.length > 0 ? constructorCallSource(`new ${name}`, projection.arguments) : targetTyped ? "new()" : `new ${name}()`;
85920
+ if (typeof value.id === "string" && (storedConstruction !== null || projection.arguments.length > 0 || schemaClass2.requiredConstructorId !== void 0)) {
85921
+ context.materializedConstructors.set(value.id, constructor2);
85922
+ }
85865
85923
  return fields.length === 0 ? constructor2 : `${storedConstruction !== null || projection.arguments.length > 0 ? constructor2 : targetTyped ? "new()" : `new ${name}`} {
85866
85924
  ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
85867
85925
  }`;
@@ -86697,16 +86755,22 @@ function instanceGenericEnvironment(context, classId, member, outerEnvironment)
86697
86755
  const bindings = classGenericBindings(context.manifestClasses, classId);
86698
86756
  const memberId = stringField3(member, "id");
86699
86757
  const manifestMember = context.manifestMembers.get(memberId);
86700
- if (manifestMember?.kind === "class") {
86701
- for (const [genericParamId, argument2] of Object.entries(
86702
- manifestMember.classArguments
86758
+ const rawClassArguments = isObjectRecord2(member.classArguments) ? member.classArguments : null;
86759
+ const classArguments2 = rawClassArguments ?? (manifestMember?.kind === "class" ? manifestMember.classArguments : null);
86760
+ if (classArguments2 !== null) {
86761
+ for (const [genericParamId, rawArgument] of Object.entries(
86762
+ classArguments2
86703
86763
  )) {
86764
+ const argument2 = isObjectRecord2(rawArgument) ? rawArgument : null;
86704
86765
  if (bindings.get(genericParamId)?.kind === "member") continue;
86705
- if (argument2.kind === "member") {
86706
- bindings.set(genericParamId, argument2);
86766
+ if (argument2?.kind === "member" && typeof argument2.memberId === "string") {
86767
+ bindings.set(genericParamId, {
86768
+ kind: "member",
86769
+ memberId: argument2.memberId
86770
+ });
86707
86771
  continue;
86708
86772
  }
86709
- const outerMemberId = outerEnvironment?.get(argument2.genericParamId);
86773
+ const outerMemberId = argument2?.kind === "generic" && typeof argument2.genericParamId === "string" ? outerEnvironment?.get(argument2.genericParamId) : void 0;
86710
86774
  if (outerMemberId !== void 0) {
86711
86775
  bindings.set(genericParamId, {
86712
86776
  kind: "member",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.22.6",
3
+ "version": "0.22.7",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.22.6 -->
12
+ <!-- reviewed-through-cli: 0.22.7 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -78,7 +78,7 @@ wrappers.
78
78
  The marker near the top of `SKILL.md` must exactly match the package version:
79
79
 
80
80
  ```html
81
- <!-- reviewed-through-cli: 0.22.6 -->
81
+ <!-- reviewed-through-cli: 0.22.7 -->
82
82
  ```
83
83
 
84
84
  The quoted version above is checked too, so this instruction cannot go stale