@ainova-systems/intelligence 0.11.0-rc.8 → 0.11.0-rc.9

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/README.md CHANGED
@@ -42,4 +42,8 @@ intelligence sync
42
42
 
43
43
  Any git repo with `rules/`, `agents/` or `skills/` at its root is already an Intelligence Package: install it from an explicit source — `intelligence package add github:your-org/your-repo` — with versions from its `x.y.z` git tags. Names (`@scope/name`) resolve only through registries this project explicitly trusts; a monorepo of packages needs one line in such an index.
44
44
 
45
+ Every package has the same compact manifest entry: requested `version` or
46
+ `ref` only. Repository URL/path, resolved tag/ref and commit SHA are recorded
47
+ in the committed `intelligence.lock`.
48
+
45
49
  Requires `git` (and on Windows the bash it ships with — Git for Windows). Engine and docs: [ainova-systems/intelligence](https://github.com/ainova-systems/intelligence).
@@ -57,9 +57,9 @@ if [ -d "$IP_ROOT/.intelligence/engine" ]; then
57
57
  warn "stale .intelligence/engine directory (pre-package layout) — run 'intelligence init'"
58
58
  fi
59
59
 
60
- # Manifest packages: names valid, locked, installed — and the lock's source
61
- # still the one resolution would pick today (a registry silently re-pointing
62
- # an installed name is the dependency-confusion tail this check cuts off).
60
+ # Manifest packages: names valid, requested intent only, locked and installed.
61
+ # The lock is authoritative for resolved source URL/path until an explicit
62
+ # package re-add changes that trust decision.
63
63
  while IFS= read -r name; do
64
64
  [ -n "$name" ] || continue
65
65
  assert_valid_pkg_name "$name"
@@ -68,32 +68,21 @@ while IFS= read -r name; do
68
68
  else
69
69
  m_ver="$(qmap_field "$manifest" "packages" "$name" "version")"
70
70
  m_ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
71
- m_url="$(qmap_field "$manifest" "packages" "$name" "url")"
72
- m_path="$(qmap_field "$manifest" "packages" "$name" "path")"
73
71
  l_req="$(qmap_field "$lock" "packages" "$name" "requested")"
74
72
  l_res="$(qmap_field "$lock" "packages" "$name" "resolved")"
75
- l_url="$(qmap_field "$lock" "packages" "$name" "url")"
76
- l_path="$(qmap_field "$lock" "packages" "$name" "path")"
77
73
  [ -z "$m_ver" ] || [ "$m_ver" = "$l_req" ] \
78
74
  || warn "$name requests '$m_ver' but the lock recorded '$l_req' — run 'intelligence update --preview'"
79
75
  [ -z "$m_ref" ] || [ "$m_ref" = "$l_res" ] \
80
76
  || warn "$name pins ref '$m_ref' but the lock resolved '$l_res'"
81
- [ -z "$m_url" ] || [ "$m_url" = "$l_url" ] \
82
- || warn "$name declares url '$m_url' but the lock recorded '$l_url'"
83
- [ -z "$m_path" ] || [ "$m_path" = "$l_path" ] \
84
- || warn "$name declares path '$m_path' but the lock recorded '$l_path'"
77
+ if [ -n "$(qmap_field "$manifest" "packages" "$name" "url")" ] \
78
+ || [ -n "$(qmap_field "$manifest" "packages" "$name" "path")" ]; then
79
+ warn "$name stores source details in the manifest — run 'intelligence init --apply' to move them to the lock-only model"
80
+ fi
85
81
  if [ ! -d "$IP_ROOT/.intelligence/packages/$name" ]; then
86
82
  warn "$name is locked but not installed — run 'intelligence sync'"
87
83
  continue
88
84
  fi
89
85
  ok "$name @ $(qmap_field "$lock" "packages" "$name" "resolved")"
90
- if [ -z "$(qmap_field "$manifest" "packages" "$name" "url")" ]; then
91
- locked_url="$(qmap_field "$lock" "packages" "$name" "url")"
92
- resolve_package_source "$manifest" "$name" 2>/dev/null || true
93
- if [ -n "${RES_URL:-}" ] && [ -n "$locked_url" ] && [ "$RES_URL" != "$locked_url" ]; then
94
- warn "$name now resolves to $RES_URL but the lock pins $locked_url — a registry re-pointed the name; verify with 'intelligence update --preview'"
95
- fi
96
- fi
97
86
  fi
98
87
  done < <(qmap_keys "$manifest" "packages")
99
88
 
@@ -194,6 +194,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
194
194
  if [ -z "$sha" ] || [ "$sha" = "unknown" ]; then
195
195
  sha="$(GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" "$ref" 2>/dev/null | awk '{print $1; exit}')"
196
196
  fi
197
+ [ -n "$ref" ] || ref="HEAD"
197
198
  printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
198
199
  "$name" "" "$url" "" "$ref" "$sha" >> "$lock_rows"
199
200
  done < "$pack_rows"
@@ -261,17 +262,14 @@ while IFS= read -r line || [ -n "$line" ]; do
261
262
  done < "$config" > "$manifest_stage"
262
263
  stamp_schema_version "$manifest_stage" "$eng"
263
264
 
264
- # packages: entries (pin preserved — no invented ranges).
265
+ # packages: requested pins only; resolved source details live in the lock.
265
266
  while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
266
267
  [ -n "$pack" ] || continue
267
- qmap_set "$manifest_stage" "packages" "$name" "url" "$url"
268
- [ -n "$ref" ] && qmap_set "$manifest_stage" "packages" "$name" "ref" "$ref"
268
+ qmap_set "$manifest_stage" "packages" "$name" "ref" "${ref:-HEAD}"
269
269
  done < "$pack_rows"
270
270
 
271
271
  # The engine's own content rides along as a package, pinned to the engine.
272
272
  qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "version" "$eng"
273
- qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "url" "$SYNC_PKG_URL"
274
- qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "path" "$SYNC_PKG_PATH"
275
273
  printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
276
274
  "$SYNC_PKG_NAME" "$eng" "$SYNC_PKG_URL" "$SYNC_PKG_PATH" "v$eng" "$sync_pkg_sha" >> "$lock_rows"
277
275
 
@@ -117,6 +117,7 @@ if [ -z "$ref" ]; then
117
117
  die "'$name' has no stable version tags at $url — a registry package must be versioned; use github:/git+ with @<ref> to pin a branch or commit"
118
118
  fi
119
119
  echo " NOTE: no version tags at $url — pinning the default branch head" >&2
120
+ ref="HEAD"
120
121
  fi
121
122
  fi
122
123
 
@@ -125,22 +126,16 @@ sha="$(fetch_package "$url" "${ref:-$resolved_tag}" "$path" "$IP_ROOT/$rel")"
125
126
 
126
127
  wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
127
128
 
128
- # Manifest entry: a registry package records its range; a direct one records
129
- # its source so resolution never has to guess it again. The entry is replaced
130
- # WHOLE — re-adding under a different spec must not leave the old spec's
131
- # fields behind (a stale ref: would freeze update, a stale url: would pin the
132
- # old source).
129
+ # The manifest records requested intent only. The resolved source URL/path,
130
+ # tag/ref and SHA live in the required lock. Re-adding a package is the
131
+ # explicit operation for changing its trusted source.
133
132
  qmap_delete_key "$manifest" "packages" "$name"
134
- if [ "$mode" = "registry" ]; then
133
+ if [ -n "$ref" ]; then
134
+ qmap_set "$manifest" "packages" "$name" "ref" "$ref"
135
+ elif [ -n "$requested" ]; then
135
136
  qmap_set "$manifest" "packages" "$name" "version" "$requested"
136
137
  else
137
- qmap_set "$manifest" "packages" "$name" "url" "$url"
138
- [ -n "$path" ] && qmap_set "$manifest" "packages" "$name" "path" "$path"
139
- if [ -n "$ref" ]; then
140
- qmap_set "$manifest" "packages" "$name" "ref" "$ref"
141
- elif [ -n "$requested" ]; then
142
- qmap_set "$manifest" "packages" "$name" "version" "$requested"
143
- fi
138
+ die "internal: package '$name' has neither a requested version nor ref"
144
139
  fi
145
140
 
146
141
  lock_upsert "$lock" "$name" "$requested" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
@@ -1,9 +1,9 @@
1
1
  #!/bin/bash
2
2
  # Internal package range update operation.
3
3
  #
4
- # Re-resolve version ranges against the remotes and rewrite the lock. Pinned
5
- # packages (`ref:`) never move here — repin by editing the manifest or
6
- # re-adding. `update <name>` limits the pass to one package.
4
+ # Resolve requested versions/refs against each package's locked source and
5
+ # rewrite the lock. `update <name>` limits the pass to one package; re-adding
6
+ # is the explicit operation for changing a source URL/path.
7
7
  set -euo pipefail
8
8
  source "$CLI_DIR/lib/cli-common.sh"
9
9
 
@@ -35,30 +35,36 @@ while IFS= read -r name; do
35
35
  echo " $name: engine content follows the installed CLI — skipped"
36
36
  continue
37
37
  fi
38
+ current="$(qmap_field "$lock" "packages" "$name" "resolved")"
39
+ locked_requested="$(qmap_field "$lock" "packages" "$name" "requested")"
40
+ url="$(qmap_field "$lock" "packages" "$name" "url")"
41
+ path="$(qmap_field "$lock" "packages" "$name" "path")"
42
+ [ -n "$url" ] || die "$name has no source in intelligence.lock — restore the committed lock or re-add the package"
38
43
  ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
39
- [ -n "$ref" ] && { echo " $name: pinned to ref '$ref' — skipped"; continue; }
40
44
  range="$(qmap_field "$manifest" "packages" "$name" "version")"
41
- # An exact version is a pin, same as ref: — update never moves pins.
42
- if [ -n "$range" ] && semver_is_stable "$range"; then
43
- echo " $name: pinned to $range — edit the manifest to move it"
44
- continue
45
+ [ -n "$ref" ] || [ -n "$range" ] || die "$name has neither version nor ref in the manifest"
46
+
47
+ if [ -n "$ref" ]; then
48
+ tag="$ref"
49
+ requested=""
50
+ else
51
+ picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
52
+ [ -n "$picked" ] || { echo " $name: nothing satisfies '$range' at $url" >&2; continue; }
53
+ read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
54
+ requested="$range"
45
55
  fi
46
- url="$(qmap_field "$manifest" "packages" "$name" "url")"
47
- path="$(qmap_field "$manifest" "packages" "$name" "path")"
48
- if [ -z "$url" ]; then
49
- resolve_package_source "$manifest" "$name"
50
- url="$RES_URL"; path="$RES_PATH"
56
+ if [ "$tag" = "$current" ] && [ "$requested" != "$locked_requested" ]; then
57
+ if [ "$preview" -eq 1 ]; then
58
+ echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (keeps $current)"
59
+ else
60
+ locked_sha="$(qmap_field "$lock" "packages" "$name" "sha")"
61
+ lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$current" "$locked_sha"
62
+ echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (kept $current)"
63
+ fi
64
+ moved=$((moved + 1))
65
+ continue
51
66
  fi
52
- picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
53
- [ -n "$picked" ] || { echo " $name: nothing satisfies '${range:-*}' at $url" >&2; continue; }
54
- read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
55
- current="$(qmap_field "$lock" "packages" "$name" "resolved")"
56
- cur_url="$(qmap_field "$lock" "packages" "$name" "url")"
57
- cur_path="$(qmap_field "$lock" "packages" "$name" "path")"
58
- # Same tag is not enough: a registry may re-point a name's source while
59
- # keeping the version — the move doctor warns about must be actionable
60
- # here, so url/path changes refetch too.
61
- if [ "$tag" = "$current" ] && [ "$url" = "$cur_url" ] && [ "$path" = "$cur_path" ]; then
67
+ if [ "$tag" = "$current" ]; then
62
68
  echo " $name: ${current:-<none>} (up to date)"
63
69
  continue
64
70
  fi
@@ -80,7 +86,7 @@ while IFS= read -r name; do
80
86
  mkdir -p "$(dirname "$IP_ROOT/$rel")"
81
87
  mv "$staging" "$IP_ROOT/$rel"
82
88
  wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
83
- lock_upsert "$lock" "$name" "$range" "$url" "$path" "$tag" "$sha"
89
+ lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$tag" "$sha"
84
90
  echo " $name: ${current:-<none>} -> $tag"
85
91
  moved=$((moved + 1))
86
92
  done < <(qmap_keys "$manifest" "packages")
@@ -23,7 +23,7 @@ manifest="$IP_ROOT/intelligence.yaml"
23
23
  lock="$IP_ROOT/intelligence.lock"
24
24
 
25
25
  # --frozen is the reproducibility contract: the lock must agree with the
26
- # manifest on the package SET and on every requested-source field before a
26
+ # manifest on the package SET and on every requested-intent field before a
27
27
  # single byte is restored. Anything less lets a manifest edit ride on a stale
28
28
  # lock — the exact drift the flag exists to refuse.
29
29
  if [ "$frozen" -eq 1 ]; then
@@ -33,20 +33,12 @@ if [ "$frozen" -eq 1 ]; then
33
33
  || die "locked restore: $name is in the manifest but not in intelligence.lock"
34
34
  m_ver="$(qmap_field "$manifest" "packages" "$name" "version")"
35
35
  m_ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
36
- m_url="$(qmap_field "$manifest" "packages" "$name" "url")"
37
- m_path="$(qmap_field "$manifest" "packages" "$name" "path")"
38
36
  l_req="$(qmap_field "$lock" "packages" "$name" "requested")"
39
37
  l_res="$(qmap_field "$lock" "packages" "$name" "resolved")"
40
- l_url="$(qmap_field "$lock" "packages" "$name" "url")"
41
- l_path="$(qmap_field "$lock" "packages" "$name" "path")"
42
38
  [ -z "$m_ver" ] || [ "$m_ver" = "$l_req" ] \
43
39
  || die "locked restore: $name requests '$m_ver' but the lock recorded '$l_req' — reconcile the manifest and lock"
44
40
  [ -z "$m_ref" ] || [ "$m_ref" = "$l_res" ] \
45
41
  || die "--frozen: $name pins ref '$m_ref' but the lock resolved '$l_res'"
46
- [ -z "$m_url" ] || [ "$m_url" = "$l_url" ] \
47
- || die "--frozen: $name declares url '$m_url' but the lock recorded '$l_url'"
48
- [ -z "$m_path" ] || [ "$m_path" = "$l_path" ] \
49
- || die "--frozen: $name declares path '$m_path' but the lock recorded '$l_path'"
50
42
  done < <(qmap_keys "$manifest" "packages")
51
43
  while IFS= read -r name; do
52
44
  [ -n "$name" ] || continue
@@ -76,14 +68,11 @@ if [ -n "$missing" ]; then
76
68
  sync_pkg_install "$IP_ROOT"
77
69
  continue
78
70
  fi
79
- url="$(qmap_field "$manifest" "packages" "$name" "url")"
80
- path="$(qmap_field "$manifest" "packages" "$name" "path")"
81
71
  ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
82
72
  range="$(qmap_field "$manifest" "packages" "$name" "version")"
83
- if [ -z "$url" ]; then
84
- resolve_package_source "$manifest" "$name"
85
- url="$RES_URL"; path="$RES_PATH"
86
- fi
73
+ resolve_package_source "$manifest" "$name"
74
+ url="$RES_URL"; path="$RES_PATH"
75
+ [ -n "$url" ] || die "no trusted registry declares '$name' and it is absent from intelligence.lock"
87
76
  resolved_tag=""
88
77
  if [ -z "$ref" ]; then
89
78
  picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
@@ -49,7 +49,32 @@ if [ -n "$legacy_stamp" ]; then
49
49
  echo " migrating: sync_version -> schema_version"
50
50
  fi
51
51
 
52
- # v2 migration 1: staged engine content -> the @ainova-systems/sync package.
52
+ # v2 migration 1: package entries contain requested intent only. Early RCs
53
+ # also copied resolved url/path into the manifest. Preserve the existing lock
54
+ # as the trusted source and remove that duplicate representation. A direct
55
+ # unversioned package becomes an explicit HEAD pin so its manifest entry stays
56
+ # meaningful after the source fields are removed.
57
+ source_fields_migrated=0
58
+ while IFS= read -r name; do
59
+ [ -n "$name" ] || continue
60
+ m_url="$(qmap_field "$manifest" "packages" "$name" "url")"
61
+ m_path="$(qmap_field "$manifest" "packages" "$name" "path")"
62
+ [ -n "$m_url" ] || [ -n "$m_path" ] || continue
63
+ m_ver="$(qmap_field "$manifest" "packages" "$name" "version")"
64
+ m_ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
65
+ if [ -z "$m_ver" ] && [ -z "$m_ref" ]; then
66
+ locked_ref="$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$name" "resolved")"
67
+ qmap_set "$manifest" "packages" "$name" "ref" "${locked_ref:-HEAD}"
68
+ fi
69
+ qmap_delete_field "$manifest" "packages" "$name" "url"
70
+ qmap_delete_field "$manifest" "packages" "$name" "path"
71
+ source_fields_migrated=1
72
+ done < <(qmap_keys "$manifest" "packages")
73
+ if [ "$source_fields_migrated" -eq 1 ]; then
74
+ echo " migrating: package url/path -> intelligence.lock"
75
+ fi
76
+
77
+ # v2 migration 2: staged engine content -> the @ainova-systems/sync package.
53
78
  # Pre-package manifests listed `.intelligence/engine/{rules,agents,skills}`
54
79
  # as sources fed by a CLI-staged copy; that copy is now an ordinary package
55
80
  # entry seeded from the bundle. Post-condition: no `.intelligence/engine`
@@ -139,14 +139,12 @@ bundled_engine_version() {
139
139
  }
140
140
 
141
141
  # --- The sync package's manifest/lock plumbing ---------------------------
142
- # sync_pkg_entry <manifest> — write/refresh the package's manifest entry:
143
- # name + exact pin + explicit url/path, so resolution never depends on any
144
- # registry (a project registry shadowing the name cannot brick the engine).
142
+ # sync_pkg_entry <manifest> — write/refresh only the requested exact pin.
143
+ # The built-in source lives in engine-package.yaml and resolved source state
144
+ # belongs exclusively to intelligence.lock.
145
145
  sync_pkg_entry() {
146
146
  local manifest="$1"
147
147
  qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "version" "$(bundled_engine_version)"
148
- qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "url" "$SYNC_PKG_URL"
149
- qmap_set "$manifest" "packages" "$SYNC_PKG_NAME" "path" "$SYNC_PKG_PATH"
150
148
  }
151
149
 
152
150
  # sync_pkg_install <root> — materialize the package into the store and lock
@@ -227,13 +225,19 @@ project_needs_upgrade() {
227
225
  [ -d "$root/.intelligence/engine" ] && return 0
228
226
 
229
227
  while IFS= read -r name; do
230
- [ "$name" = "$SYNC_PKG_NAME" ] || continue
231
- pinned="$(qmap_field "$manifest" "packages" "$name" "version")"
232
- locked="$(qmap_field "$root/intelligence.lock" "packages" "$name" "resolved")"
233
- [ "$pinned" = "$eng" ] || return 0
234
- if [ -f "$root/intelligence.lock" ]; then
235
- [ -n "$locked" ] || return 0
236
- [ "${locked#v}" = "$eng" ] || return 0
228
+ [ -n "$name" ] || continue
229
+ # Early RC manifests mixed requested intent with resolved source
230
+ # details. Source URL/path now live only in the required lockfile.
231
+ [ -n "$(qmap_field "$manifest" "packages" "$name" "url")" ] && return 0
232
+ [ -n "$(qmap_field "$manifest" "packages" "$name" "path")" ] && return 0
233
+ if [ "$name" = "$SYNC_PKG_NAME" ]; then
234
+ pinned="$(qmap_field "$manifest" "packages" "$name" "version")"
235
+ locked="$(qmap_field "$root/intelligence.lock" "packages" "$name" "resolved")"
236
+ [ "$pinned" = "$eng" ] || return 0
237
+ if [ -f "$root/intelligence.lock" ]; then
238
+ [ -n "$locked" ] || return 0
239
+ [ "${locked#v}" = "$eng" ] || return 0
240
+ fi
237
241
  fi
238
242
  done < <(qmap_keys "$manifest" "packages")
239
243
  return 1
@@ -301,6 +301,29 @@ qmap_delete_key() {
301
301
  '
302
302
  }
303
303
 
304
+ # qmap_delete_field <file> <block> <key> <field> — drop one field from a
305
+ # quoted-key entry without disturbing its other fields or surrounding YAML.
306
+ qmap_delete_field() {
307
+ local file="$1" block="$2" key="$3" field="$4"
308
+ [ -f "$file" ] || return 0
309
+ _qmap_stage "$file" -v block="$block" -v key="$key" -v field="$field" '
310
+ { sub(/\r$/, "") }
311
+ $0 ~ "^" block ":[ \t]*$" { inb = 1; print; next }
312
+ inb && /^[^ #]/ { inb = 0; ink = 0 }
313
+ inb && /^ "/ {
314
+ s = substr($0, 4); q = index(s, "\"")
315
+ ink = (q > 0 && substr(s, 1, q - 1) == key)
316
+ print; next
317
+ }
318
+ inb && ink && /^ [A-Za-z_]/ {
319
+ line = $0; sub(/^ /, "", line)
320
+ c = index(line, ":")
321
+ if (c > 0 && substr(line, 1, c - 1) == field) next
322
+ }
323
+ { print }
324
+ '
325
+ }
326
+
304
327
  # --- registries: a trust LIST, not a scope map -----------------------------
305
328
  # The block holds registry repo URLs in trust order. Two shapes are read so
306
329
  # early manifests keep working: the list form (`- "url"`) and the retired
package/engine/ENGINE_SHA CHANGED
@@ -1 +1 @@
1
- f2bfc5e3722bf6fb2f7309db2d248368d5b0cc5e
1
+ af238e67e8a9469edf6d2058e169de52cbeb93af
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.11.0-rc.8",
3
+ "version": "0.11.0-rc.9",
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"
@@ -92,11 +92,11 @@ intelligence package add github:acme/backend-intelligence
92
92
  intelligence package add 'git+https://git.example.com/acme/backend.git@main#package'
93
93
  ```
94
94
 
95
- Registries are an ordered trust list and the only resolver for a package name. There is no built-in catalog and no `@org/name` → GitHub guessing. An explicit `github:` or `git+` spec bypasses registry lookup while remaining recorded and pinned in the manifest and lock.
95
+ Registries are an ordered trust list and the only resolver for a package name. There is no built-in catalog and no `@org/name` → GitHub guessing. An explicit `github:` or `git+` spec bypasses registry lookup. In every case the manifest stores only requested `version` or `ref`; resolved URL/path and SHA live in the lock.
96
96
 
97
97
  Stable Git tags provide package versions. Semver ranges select the highest matching stable tag; a `ref:` pin names a branch or commit and does not move during `intelligence update`. One package name has one version per project.
98
98
 
99
- Commit `intelligence.lock`. It records requested versions, source URLs and paths, resolved refs and commit SHAs. After cloning, `intelligence sync` restores a missing store strictly from that lock before rendering; manifest/lock or SHA drift is refused.
99
+ Commit `intelligence.lock`. It records requested versions, source URLs and paths, resolved refs and commit SHAs. After cloning, `intelligence sync` restores a missing store strictly from that lock before rendering; manifest/lock or SHA drift is refused. Re-run `package add` when deliberately changing a source.
100
100
 
101
101
  `@ainova-systems/sync` is ordinary package content exact-pinned to the bundled engine version. `intelligence init` installs it unless `--bare` is used. Lifecycle preflight keeps that pin and `schema_version` aligned with the installed CLI; package-range updates never move it independently.
102
102