@ainova-systems/intelligence 0.11.5 → 0.11.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/cli/commands/init.sh +3 -1
- package/cli/commands/update.sh +3 -1
- package/cli/internal/align-project.sh +3 -1
- package/cli/internal/check.sh +9 -2
- package/cli/internal/package-list.sh +2 -1
- package/cli/internal/package-update.sh +58 -6
- package/cli/lib/cli-common.sh +35 -0
- package/cli/lib/semver.sh +33 -0
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/engine/lib/contract.sh +26 -6
- package/engine/sync.sh +2 -1
- package/package.json +1 -1
- package/packages/sync/references/conventions.md +2 -2
package/cli/commands/init.sh
CHANGED
|
@@ -124,7 +124,9 @@ case "$IP_MODE" in
|
|
|
124
124
|
if [ "$preview" -eq 1 ]; then
|
|
125
125
|
check_version_compat "$IP_ROOT/intelligence.yaml"
|
|
126
126
|
echo "project: Intelligence CLI at $IP_ROOT"
|
|
127
|
-
if
|
|
127
|
+
if project_stamped_ahead "$IP_ROOT"; then
|
|
128
|
+
echo " project schema $(read_schema_version "$IP_ROOT/intelligence.yaml") is ahead of engine $(bundled_engine_version) — left as is (update the CLI)"
|
|
129
|
+
elif project_needs_upgrade "$IP_ROOT"; then
|
|
128
130
|
echo " would align project lifecycle with engine $(bundled_engine_version)"
|
|
129
131
|
else
|
|
130
132
|
echo " schema/content already match engine $(bundled_engine_version)"
|
package/cli/commands/update.sh
CHANGED
|
@@ -48,7 +48,9 @@ fi
|
|
|
48
48
|
|
|
49
49
|
echo ""
|
|
50
50
|
echo "Project:"
|
|
51
|
-
if
|
|
51
|
+
if project_stamped_ahead "$IP_ROOT"; then
|
|
52
|
+
echo " schema $stamp is ahead of engine $eng — left as is; update the CLI to align it"
|
|
53
|
+
elif project_needs_upgrade "$IP_ROOT"; then
|
|
52
54
|
echo " lifecycle alignment required (stamp ${stamp:-unstamped}, engine $eng)"
|
|
53
55
|
project_change=1
|
|
54
56
|
else
|
|
@@ -24,8 +24,10 @@ legacy_key_count="$(awk '/^sync_version:[[:space:]]*/ { n++ } END { print n + 0
|
|
|
24
24
|
eng="$(bundled_engine_version)"
|
|
25
25
|
[ "$schema_key_count" -le 1 ] || die "manifest has duplicate schema_version keys"
|
|
26
26
|
[ "$legacy_key_count" -le 1 ] || die "manifest has duplicate sync_version keys"
|
|
27
|
+
# Alignment only ever moves a project UP to this engine. A project stamped
|
|
28
|
+
# ahead is never restamped downward, whatever the gap.
|
|
27
29
|
if [ -n "$stamp" ] && _ver_gt "$stamp" "$eng"; then
|
|
28
|
-
die "manifest schema $stamp is newer than this CLI's engine $eng — update the global CLI first"
|
|
30
|
+
die "manifest schema $stamp is newer than this CLI's engine $eng — refusing to align it downward; update the global CLI first"
|
|
29
31
|
fi
|
|
30
32
|
if [ -n "$legacy_stamp" ] && _ver_gt "$legacy_stamp" "$eng"; then
|
|
31
33
|
die "manifest schema $legacy_stamp is newer than this CLI's engine $eng — update the global CLI first"
|
package/cli/internal/check.sh
CHANGED
|
@@ -5,6 +5,7 @@ source "$CLI_DIR/lib/cli-common.sh"
|
|
|
5
5
|
|
|
6
6
|
problems=0
|
|
7
7
|
warn() { echo " ✗ $*"; problems=$((problems + 1)); }
|
|
8
|
+
note() { echo " ! $*"; }
|
|
8
9
|
ok() { echo " ✓ $*"; }
|
|
9
10
|
|
|
10
11
|
echo "environment:"
|
|
@@ -36,7 +37,11 @@ eng="$(bundled_engine_version)"
|
|
|
36
37
|
if [ -z "$stamp" ]; then
|
|
37
38
|
warn "manifest has no schema_version — run 'intelligence init'"
|
|
38
39
|
elif _ver_gt "$stamp" "$eng"; then
|
|
39
|
-
|
|
40
|
+
if [ "$(_ver_major "$stamp")" -gt "$(_ver_major "$eng")" ]; then
|
|
41
|
+
warn "manifest schema $stamp is a newer major than this CLI's engine $eng — update the CLI: npm i -g @ainova-systems/intelligence@latest"
|
|
42
|
+
else
|
|
43
|
+
note "manifest schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it: npm i -g @ainova-systems/intelligence@latest"
|
|
44
|
+
fi
|
|
40
45
|
elif _ver_gt "$eng" "$stamp"; then
|
|
41
46
|
warn "manifest schema $stamp behind engine $eng — run 'intelligence init'"
|
|
42
47
|
else
|
|
@@ -49,6 +54,8 @@ sync_locked="$(qmap_field "$lock" "packages" "$SYNC_PKG_NAME" "resolved")"
|
|
|
49
54
|
if [ -n "$sync_locked" ]; then
|
|
50
55
|
if [ "${sync_locked#v}" = "$eng" ]; then
|
|
51
56
|
ok "$SYNC_PKG_NAME at $sync_locked (matches the engine)"
|
|
57
|
+
elif [ "${sync_locked#v}" = "$stamp" ] && _ver_gt "$stamp" "$eng"; then
|
|
58
|
+
note "$SYNC_PKG_NAME at $sync_locked (matches the project schema; the engine is $eng)"
|
|
52
59
|
else
|
|
53
60
|
warn "$SYNC_PKG_NAME locked at $sync_locked but the engine is $eng — run 'intelligence init'"
|
|
54
61
|
fi
|
|
@@ -82,7 +89,7 @@ while IFS= read -r name; do
|
|
|
82
89
|
warn "$name is locked but not installed — run 'intelligence sync'"
|
|
83
90
|
continue
|
|
84
91
|
fi
|
|
85
|
-
ok "$name @ $(qmap_field "$lock" "packages" "$name" "
|
|
92
|
+
ok "$name @ $(pin_label "$m_ref" "$l_res" "$(qmap_field "$lock" "packages" "$name" "sha")")"
|
|
86
93
|
fi
|
|
87
94
|
done < <(qmap_keys "$manifest" "packages")
|
|
88
95
|
|
|
@@ -14,6 +14,7 @@ while IFS= read -r name; do
|
|
|
14
14
|
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
15
15
|
ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
16
16
|
resolved="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
17
|
+
sha="$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
17
18
|
if [ ! -f "$lock" ]; then
|
|
18
19
|
state="lock missing — restore committed intelligence.lock"
|
|
19
20
|
elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
@@ -24,7 +25,7 @@ while IFS= read -r name; do
|
|
|
24
25
|
printf '%s %s locked:%s %s\n' \
|
|
25
26
|
"$name" \
|
|
26
27
|
"${ref:+ref:$ref}${ref:-${range:-*}}" \
|
|
27
|
-
"$
|
|
28
|
+
"$(pin_label "$ref" "$resolved" "$sha")" \
|
|
28
29
|
"$state"
|
|
29
30
|
done < <(qmap_keys "$manifest" "packages")
|
|
30
31
|
|
|
@@ -22,6 +22,20 @@ require_cli_project
|
|
|
22
22
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
23
23
|
lock="$IP_ROOT/intelligence.lock"
|
|
24
24
|
|
|
25
|
+
# move_label <ref-or-empty> <from-resolved> <to-resolved> <from-sha> <to-sha>
|
|
26
|
+
# A version pin moves between tags, a ref pin between commits under one name —
|
|
27
|
+
# printing "main -> main" would report the move by hiding what moved.
|
|
28
|
+
move_label() {
|
|
29
|
+
local ref="$1" from="$2" to="$3" from_sha="$4" to_sha="$5"
|
|
30
|
+
if [ -z "$ref" ]; then
|
|
31
|
+
printf '%s -> %s' "${from:-<none>}" "$to"
|
|
32
|
+
elif [ -n "$from" ] && [ "$from" != "$ref" ]; then
|
|
33
|
+
printf '%s -> %s@%s' "$from" "$ref" "$(short_sha "$to_sha")"
|
|
34
|
+
else
|
|
35
|
+
printf '%s %s -> %s' "$ref" "$(short_sha "$from_sha")" "$(short_sha "$to_sha")"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
25
39
|
moved=0 found=0
|
|
26
40
|
while IFS= read -r name; do
|
|
27
41
|
[ -n "$name" ] || continue
|
|
@@ -37,6 +51,7 @@ while IFS= read -r name; do
|
|
|
37
51
|
fi
|
|
38
52
|
current="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
39
53
|
locked_requested="$(qmap_field "$lock" "packages" "$name" "requested")"
|
|
54
|
+
locked_sha="$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
40
55
|
url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
41
56
|
path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
42
57
|
[ -n "$url" ] || die "$name has no source in intelligence.lock — restore the committed lock or re-add the package"
|
|
@@ -44,32 +59,69 @@ while IFS= read -r name; do
|
|
|
44
59
|
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
45
60
|
[ -n "$ref" ] || [ -n "$range" ] || die "$name has neither version nor ref in the manifest"
|
|
46
61
|
|
|
62
|
+
ref_moved=0 remote_sha=""
|
|
47
63
|
if [ -n "$ref" ]; then
|
|
48
64
|
tag="$ref"
|
|
49
65
|
requested=""
|
|
66
|
+
# A ref is requested INTENT, and the lock's `resolved` column holds
|
|
67
|
+
# that same ref name — comparing the two asks a question no branch can
|
|
68
|
+
# ever answer differently. The sha column is the only record of where
|
|
69
|
+
# the ref actually pointed, so a ref pin is compared commit to commit:
|
|
70
|
+
# a moved branch, a re-cut tag and a `HEAD` pin all become visible,
|
|
71
|
+
# while a pin that IS a commit stays immutable by construction.
|
|
72
|
+
probe_rc=0
|
|
73
|
+
remote_sha="$(remote_sha_for_ref "$url" "$ref")" || probe_rc=$?
|
|
74
|
+
if [ "$probe_rc" -ne 0 ]; then
|
|
75
|
+
# No answer is not "no change": reporting up to date here would
|
|
76
|
+
# freeze the pin exactly as the ref-name comparison used to.
|
|
77
|
+
echo " WARN: $name: cannot reach $url — ref '$ref' was not checked" >&2
|
|
78
|
+
echo " $name: $ref (not checked — remote unreachable)"
|
|
79
|
+
continue
|
|
80
|
+
fi
|
|
81
|
+
# ls-remote advertises refs, not arbitrary objects, so a commit pin
|
|
82
|
+
# legitimately matches nothing — and cannot move. This verdict applies
|
|
83
|
+
# only once the lock already resolved to this ref; a manifest that now
|
|
84
|
+
# names a ref the lock never resolved is an ordinary move, and falls
|
|
85
|
+
# through to the fetch below.
|
|
86
|
+
if [ -z "$remote_sha" ] && [ "$ref" = "$current" ]; then
|
|
87
|
+
# The lock records the commit this ref resolved to, so a ref that
|
|
88
|
+
# prefixes it IS that commit. Asking the lock beats guessing from
|
|
89
|
+
# the ref's shape: a deleted branch named like a hex string would
|
|
90
|
+
# otherwise pass for a pin and swallow its own disappearance.
|
|
91
|
+
case "$locked_sha" in
|
|
92
|
+
"$ref"*)
|
|
93
|
+
echo " $name: $(short_sha "$ref") (pinned commit)"
|
|
94
|
+
;;
|
|
95
|
+
*)
|
|
96
|
+
echo " WARN: $name pins ref '$ref', which $url no longer advertises" >&2
|
|
97
|
+
echo " $name: $ref (unresolvable — gone upstream)"
|
|
98
|
+
;;
|
|
99
|
+
esac
|
|
100
|
+
continue
|
|
101
|
+
fi
|
|
102
|
+
[ -z "$remote_sha" ] || [ "$remote_sha" = "$locked_sha" ] || ref_moved=1
|
|
50
103
|
else
|
|
51
104
|
picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
|
|
52
105
|
[ -n "$picked" ] || { echo " $name: nothing satisfies '$range' at $url" >&2; continue; }
|
|
53
106
|
read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
54
107
|
requested="$range"
|
|
55
108
|
fi
|
|
56
|
-
if [ "$tag" = "$current" ] && [ "$requested" != "$locked_requested" ]; then
|
|
109
|
+
if [ "$ref_moved" -eq 0 ] && [ "$tag" = "$current" ] && [ "$requested" != "$locked_requested" ]; then
|
|
57
110
|
if [ "$preview" -eq 1 ]; then
|
|
58
111
|
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (keeps $current)"
|
|
59
112
|
else
|
|
60
|
-
locked_sha="$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
61
113
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$current" "$locked_sha"
|
|
62
114
|
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (kept $current)"
|
|
63
115
|
fi
|
|
64
116
|
moved=$((moved + 1))
|
|
65
117
|
continue
|
|
66
118
|
fi
|
|
67
|
-
if [ "$tag" = "$current" ]; then
|
|
68
|
-
echo " $name: $
|
|
119
|
+
if [ "$ref_moved" -eq 0 ] && [ "$tag" = "$current" ]; then
|
|
120
|
+
echo " $name: $(pin_label "$ref" "$current" "$locked_sha") (up to date)"
|
|
69
121
|
continue
|
|
70
122
|
fi
|
|
71
123
|
if [ "$preview" -eq 1 ]; then
|
|
72
|
-
echo " $name: $
|
|
124
|
+
echo " $name: $(move_label "$ref" "$current" "$tag" "$locked_sha" "$remote_sha")"
|
|
73
125
|
moved=$((moved + 1))
|
|
74
126
|
continue
|
|
75
127
|
fi
|
|
@@ -87,7 +139,7 @@ while IFS= read -r name; do
|
|
|
87
139
|
mv "$staging" "$IP_ROOT/$rel"
|
|
88
140
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
89
141
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$tag" "$sha"
|
|
90
|
-
echo " $name: $
|
|
142
|
+
echo " $name: $(move_label "$ref" "$current" "$tag" "$locked_sha" "$sha")"
|
|
91
143
|
moved=$((moved + 1))
|
|
92
144
|
done < <(qmap_keys "$manifest" "packages")
|
|
93
145
|
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -47,6 +47,27 @@ assert_safe_ref() {
|
|
|
47
47
|
esac
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
# --- Resolved-pin display ------------------------------------------------
|
|
51
|
+
# A `ref:` pin's resolved column holds the ref NAME, which never changes: the
|
|
52
|
+
# sha is the only field that says which commit is installed. Every surface
|
|
53
|
+
# reporting such a pin prints both, so a branch that stopped moving is visible
|
|
54
|
+
# without asking the remote.
|
|
55
|
+
|
|
56
|
+
short_sha() {
|
|
57
|
+
[ -n "$1" ] || { printf '<none>'; return 0; }
|
|
58
|
+
printf '%s' "${1:0:7}"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# pin_label <ref-or-empty> <resolved> <sha>
|
|
62
|
+
pin_label() {
|
|
63
|
+
local ref="$1" resolved="$2" sha="$3"
|
|
64
|
+
if [ -n "$ref" ]; then
|
|
65
|
+
printf '%s@%s' "${resolved:-$ref}" "$(short_sha "$sha")"
|
|
66
|
+
else
|
|
67
|
+
printf '%s' "${resolved:-<none>}"
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
50
71
|
source "$CLI_DIR/lib/manifest.sh"
|
|
51
72
|
source "$CLI_DIR/lib/semver.sh"
|
|
52
73
|
source "$CLI_DIR/lib/registry.sh"
|
|
@@ -218,12 +239,26 @@ assert_safe_content_dir() {
|
|
|
218
239
|
done
|
|
219
240
|
}
|
|
220
241
|
|
|
242
|
+
# project_stamped_ahead <root> — the manifest is stamped newer than this CLI's
|
|
243
|
+
# engine, at any SemVer level. Every caller runs check_version_compat first,
|
|
244
|
+
# which has already refused a newer major, so what reaches this predicate is
|
|
245
|
+
# the admitted minor/patch gap; the predicate itself does not re-judge the gap.
|
|
246
|
+
project_stamped_ahead() {
|
|
247
|
+
local stamp
|
|
248
|
+
stamp="$(read_schema_version "$1/intelligence.yaml")"
|
|
249
|
+
[ -n "$stamp" ] && _ver_gt "$stamp" "$(bundled_engine_version)"
|
|
250
|
+
}
|
|
251
|
+
|
|
221
252
|
project_needs_upgrade() {
|
|
222
253
|
local root="$1" manifest="$1/intelligence.yaml" stamp eng pinned locked name
|
|
223
254
|
[ -f "$manifest" ] || return 1
|
|
224
255
|
stamp="$(read_schema_version "$manifest")"
|
|
225
256
|
eng="$(bundled_engine_version)"
|
|
226
257
|
[ -z "$stamp" ] && return 0
|
|
258
|
+
# A project stamped ahead belongs to a newer CLI. Aligning it here would
|
|
259
|
+
# restamp the schema and re-pin the engine content DOWNWARD, and the next
|
|
260
|
+
# teammate on the current CLI would move both back: leave it as found.
|
|
261
|
+
project_stamped_ahead "$root" && return 1
|
|
227
262
|
[ -n "$(top_scalar "$manifest" "sync_version")" ] && return 0
|
|
228
263
|
_ver_gt "$eng" "$stamp" && return 0
|
|
229
264
|
[ -d "$root/.intelligence/engine" ] && return 0
|
package/cli/lib/semver.sh
CHANGED
|
@@ -120,6 +120,39 @@ remote_tag_for_version() {
|
|
|
120
120
|
}'
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
# remote_sha_for_ref <git-url> <ref> — the commit a branch/tag/HEAD pin points
|
|
124
|
+
# at on the remote right now; empty when the remote advertises no such ref
|
|
125
|
+
# (a pin that IS a commit, or a ref deleted upstream). Exit 2 means the remote
|
|
126
|
+
# could not be asked at all: a check that produced no answer must never be
|
|
127
|
+
# read by the caller as "nothing changed".
|
|
128
|
+
#
|
|
129
|
+
# Three git behaviours this mirrors deliberately, because a sha that is not the
|
|
130
|
+
# one `fetch_package` would land on is worse than no answer:
|
|
131
|
+
# - `git ls-remote <url> main` also matches `refs/heads/feature/main`
|
|
132
|
+
# (patterns match at slash boundaries), so candidates are selected by full
|
|
133
|
+
# ref name, never by the pattern that fetched them;
|
|
134
|
+
# - an annotated tag advertises the tag OBJECT on `refs/tags/<t>` and the
|
|
135
|
+
# commit on the peeled `refs/tags/<t>^{}`, while the lock records the
|
|
136
|
+
# commit — so peeled wins, and the peel line only appears when its own
|
|
137
|
+
# pattern is asked for;
|
|
138
|
+
# - on a name shared by a branch and a tag, `git clone --branch <ref>` takes
|
|
139
|
+
# the branch, so `refs/heads` outranks `refs/tags` here too.
|
|
140
|
+
remote_sha_for_ref() {
|
|
141
|
+
local url="$1" ref="$2" out
|
|
142
|
+
assert_safe_source_url "$url"
|
|
143
|
+
assert_safe_ref "$ref"
|
|
144
|
+
[ -n "$ref" ] || return 0
|
|
145
|
+
out="$(GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" \
|
|
146
|
+
"$ref" "$ref^{}" "refs/heads/$ref" "refs/tags/$ref" "refs/tags/$ref^{}" 2>/dev/null)" \
|
|
147
|
+
|| return 2
|
|
148
|
+
printf '%s\n' "$out" | awk -v ref="$ref" '
|
|
149
|
+
{ sub(/\r$/, ""); if ($2 != "") seen[$2] = $1 }
|
|
150
|
+
END {
|
|
151
|
+
split("refs/heads/" ref "\037refs/tags/" ref "^{}\037refs/tags/" ref "\037" ref "^{}\037" ref, C, "\037")
|
|
152
|
+
for (i = 1; i <= 5; i++) if (C[i] in seen) { print seen[C[i]]; exit }
|
|
153
|
+
}'
|
|
154
|
+
}
|
|
155
|
+
|
|
123
156
|
# semver_pick_highest <range> — reads versions on stdin, prints the highest
|
|
124
157
|
# one matching the range (empty output when nothing matches).
|
|
125
158
|
semver_pick_highest() {
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
00308bfbdcd92ebf37924bb1083cee142e655fd7
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.11.
|
|
1
|
+
0.11.7
|
package/engine/lib/contract.sh
CHANGED
|
@@ -100,21 +100,41 @@ _ver_gt() {
|
|
|
100
100
|
return 1
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
# _ver_major V → the leading numeric field of a version ("0.11.6" → 0).
|
|
104
|
+
_ver_major() {
|
|
105
|
+
local m="${1%%.*}"
|
|
106
|
+
m="${m//[!0-9]/}"
|
|
107
|
+
printf '%s' "$((10#${m:-0}))"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# check_version_compat <config_file> — a stale engine on a project stamped
|
|
111
|
+
# NEWER than itself. The gap decides:
|
|
112
|
+
# * a newer MAJOR is refused: the manifest may carry shapes this engine
|
|
113
|
+
# cannot read, and rendering or rewriting it would destroy them. Emits
|
|
114
|
+
# status + returns IS_RC_AHEAD;
|
|
115
|
+
# * a newer minor or patch within the same major warns once and returns 0:
|
|
116
|
+
# the schema is additive there, so an older engine renders what it
|
|
117
|
+
# understands. It never restamps the project — the CLI's preflight leaves
|
|
118
|
+
# a project stamped ahead exactly as it found it (project_needs_upgrade).
|
|
107
119
|
check_version_compat() {
|
|
108
120
|
local cf="$1" stamp eng
|
|
109
121
|
stamp="$(read_schema_version "$cf")"
|
|
110
122
|
[ -n "$stamp" ] || return 0
|
|
111
123
|
eng="$(engine_version)"
|
|
112
124
|
[ -n "$eng" ] || return 0
|
|
113
|
-
|
|
125
|
+
_ver_gt "$stamp" "$eng" || return 0
|
|
126
|
+
if [ "$(_ver_major "$stamp")" -gt "$(_ver_major "$eng")" ]; then
|
|
114
127
|
is_status ahead-of-engine "stamp=$stamp engine=$eng"
|
|
115
|
-
echo " ERROR: project stamped $stamp but this engine is $eng — refusing." >&2
|
|
128
|
+
echo " ERROR: project stamped $stamp but this engine is $eng — a newer major schema; refusing." >&2
|
|
116
129
|
echo " Update the CLI first: npm i -g @ainova-systems/intelligence@latest" >&2
|
|
117
130
|
return "$IS_RC_AHEAD"
|
|
118
131
|
fi
|
|
132
|
+
# One warning per command, however many processes re-check on the way to
|
|
133
|
+
# the engine (CLI preflight first, then the engine itself). The line has
|
|
134
|
+
# no indent on purpose: `sync --compact` keeps `WARNING:` lines.
|
|
135
|
+
if [ "${IS_SCHEMA_AHEAD_WARNED:-}" != "$stamp" ]; then
|
|
136
|
+
echo "WARNING: project schema $stamp is newer than this CLI's engine $eng — the project uses a newer CLI; update it: npm i -g @ainova-systems/intelligence@latest" >&2
|
|
137
|
+
export IS_SCHEMA_AHEAD_WARNED="$stamp"
|
|
138
|
+
fi
|
|
119
139
|
return 0
|
|
120
140
|
}
|
package/engine/sync.sh
CHANGED
|
@@ -31,7 +31,8 @@ fi
|
|
|
31
31
|
# Schema version lives in the manifest (the frozen contract key).
|
|
32
32
|
_cf="$CONFIG_FILE"
|
|
33
33
|
|
|
34
|
-
# Stale engine vs project schema stamped NEWER (ahead-of-engine)
|
|
34
|
+
# Stale engine vs project schema stamped NEWER (ahead-of-engine): a newer
|
|
35
|
+
# major refuses, a newer minor/patch warns and renders (see contract.sh).
|
|
35
36
|
_vc_rc=0
|
|
36
37
|
check_version_compat "$_cf" || _vc_rc=$?
|
|
37
38
|
if [ "$_vc_rc" -ne 0 ]; then exit "$_vc_rc"; fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
4
4
|
"description": "Build, version and distribute AI agent intelligence across your organization — one CLI, versioned Intelligence Packages, and a sync engine for Claude Code, Cursor, Copilot, Codex, Pi and OpenCode.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"intelligence": "bin/intelligence.js"
|
|
@@ -391,7 +391,7 @@ The public lifecycle is deliberately compact:
|
|
|
391
391
|
- `intelligence adapter list|create|enable|disable|remove` owns adapter inventory and target state.
|
|
392
392
|
- `intelligence status [--check]` reports state; `--check` runs deep consistency checks.
|
|
393
393
|
|
|
394
|
-
Implement Intelligence schema changes as idempotent structural checks. Stage and verify replacement state before deleting or replacing prior state. A stale engine refuses a manifest whose `schema_version` is newer;
|
|
394
|
+
Implement Intelligence schema changes as idempotent structural checks. Stage and verify replacement state before deleting or replacing prior state. A stale engine refuses a manifest whose `schema_version` is a newer major; a newer minor or patch within the same major warns once and proceeds without restamping the project. Normal project entry points close a behind-project gap through lifecycle preflight.
|
|
395
395
|
|
|
396
396
|
Breaking changelog entries use a `### Breaking` checklist of verifiable post-conditions. The update skill reads every release across the version gap, chooses the package/CLI/project command sequence and verifies those conditions after the deterministic command completes.
|
|
397
397
|
|
|
@@ -406,7 +406,7 @@ The engine emits one machine-readable line, `IS_STATUS=<code> [IS_DETAIL=...]`,
|
|
|
406
406
|
| `error` | 1 | Generic failure |
|
|
407
407
|
| `config-missing` | 2 | Required manifest is absent |
|
|
408
408
|
| `ambiguous` | 3 | Conflicting state requiring agent/human judgment; reserved |
|
|
409
|
-
| `ahead-of-engine` | 4 | Manifest schema is newer than the engine |
|
|
409
|
+
| `ahead-of-engine` | 4 | Manifest schema is a newer major than the engine (a newer minor or patch warns and continues) |
|
|
410
410
|
| `aborted-incomplete` | 5 | Staged replacement was incomplete; prior state remains |
|
|
411
411
|
| `needs-update` | 6 | Project schema is behind the engine; rerun through a public lifecycle command |
|
|
412
412
|
|