Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,9 @@ jobs:
# Every text pass in the engine (LF normalization, token expansion,
# frontmatter quoting) reads a file and writes it back. Run one of
# those through a symlink and the link's TARGET — a host file outside
# the repo — is copied into the generated skill. `cp -R` preserves
# symlinks precisely so that cannot happen; assert it stays true.
# the repo — is copied into the generated skill. A link that resolves
# outside its skills source is never read through: its skill is left
# out of every output (decision 0017); assert it stays true.
PROJ=$RUNNER_TEMP/symskill
SECRET=$RUNNER_TEMP/secret
mkdir -p "$PROJ/intelligence"/{rules,agents,skills/evil-skill} "$SECRET"
Expand Down Expand Up @@ -562,6 +563,9 @@ jobs:
grep -rl "TOP-SECRET-HOST-CONTENT" .claude .agents AGENTS.md
exit 1
fi
test -L .claude/skills/evil-skill/SKILL.md \
|| { echo "FAIL: symlinked SKILL.md was replaced by a real file"; exit 1; }
echo "OK: symlinked SKILL.md left as a symlink; no host content in outputs"
for tree in .claude/skills .agents/skills; do
if [ -e "$tree/evil-skill" ] || [ -L "$tree/evil-skill" ]; then
echo "FAIL: a skill whose SKILL.md escapes its source reached $tree"; exit 1
fi
done
echo "OK: escaping SKILL.md left out of every output; no host content in outputs"
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Legacy Intelligence Sync history remains in its [archive](https://github.com/ain

- An unchanged `sync` starts far fewer processes: one walk and one hash batch fingerprint the CLI, sources and generated files; the package descriptor, manifest and lock are read once; and `sync` runs in the dispatcher's own bash. On Windows/Git Bash a cache hit on this repository's project dropped from a median 3.9 to 0.74 seconds with the same output. Cache records from earlier versions are ignored, so the first sync after upgrading renders once.

- Every warning sync and the CLI print is a `WARNING:` line, so `sync --compact`, its unchanged-run replay and `init` keep it — the frontmatter lint's "will be REJECTED at load time" included (#51).
- A symlink in a skills source whose target stays inside that source is rendered as regular files in every skill tree, with quoting, effort and the Codex invocation policy applied; a link that escapes its source, dangles or loops is left out of every output with one `WARNING:` naming the skill and the link. Claude Code, Cursor and Copilot no longer receive raw links, and a skills source directory that is itself a symlink is read everywhere (decision 0017, #44, #47).

## [0.19.0]

### Added
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ case "$sub" in
echo " index.yaml reachable — $n package(s):"
qmap_keys "$index" "packages" | sed 's/^/ /'
elif [ "$force" -eq 1 ]; then
echo " WARN: no index.yaml at $url — recording anyway (--force)." >&2
echo "WARNING: no index.yaml at $url — recording anyway (--force)." >&2
else
echo "ERROR: no index.yaml found at $url — not adding it." >&2
echo " A registry is a git repo with index.yaml at its root, mapping names to sources:" >&2
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/source.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ case "$action" in
echo "added: $dir"
fi
if [ ! -d "$IP_ROOT/$dir" ]; then
echo " WARN: $dir does not exist yet — sync skips a missing source and renders it once the directory appears." >&2
echo "WARNING: $dir does not exist yet — sync skips a missing source and renders it once the directory appears." >&2
fi
print_section "$manifest" "$IP_ROOT" "$section"
echo "Run 'intelligence sync' to render it."
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/convert-legacy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
# Containment before deletion: a mirror value is config input, and rm -rf
# must never follow it out of the repository.
case "$mirror" in
""|/*|*..*) [ -n "$mirror" ] && echo " WARN: mirror '$mirror' not repo-contained — left in place" >&2; continue ;;
""|/*|*..*) [ -n "$mirror" ] && echo "WARNING: mirror '$mirror' not repo-contained — left in place" >&2; continue ;;
esac
if [ -d "$root/$mirror" ]; then
rm -rf "${root:?}/$mirror"
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/package-search.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ if [ -n "$manifest" ]; then
[ -n "$url" ] || continue
index="$(_fetch_index "${url#git+}")"
if [ -z "$index" ]; then
echo " WARN: registry unreachable or missing index.yaml: $url" >&2
echo "WARNING: registry unreachable or missing index.yaml: $url" >&2
continue
fi
emit_index "$index" "$url"
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/package-update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ while IFS= read -r name; do
if [ "$probe_rc" -ne 0 ]; then
# No answer is not "no change": reporting up to date here would
# freeze the pin exactly as the ref-name comparison used to.
echo " WARN: $name: cannot reach $url — ref '$ref' was not checked" >&2
echo "WARNING: $name: cannot reach $url — ref '$ref' was not checked" >&2
echo " $name: $ref (not checked — remote unreachable)"
continue
fi
Expand All @@ -104,7 +104,7 @@ while IFS= read -r name; do
echo " $name: $(short_sha "$ref") (pinned commit)"
;;
*)
echo " WARN: $name pins ref '$ref', which $url no longer advertises" >&2
echo "WARNING: $name pins ref '$ref', which $url no longer advertises" >&2
echo " $name: $ref (unresolvable — gone upstream)"
;;
esac
Expand Down
2 changes: 1 addition & 1 deletion cli/lib/registry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ resolve_package_source() {
[ -n "$reg_url" ] || continue
index="$(_fetch_index "${reg_url#git+}")"
if [ -z "$index" ]; then
echo " WARN: registry unreachable or missing index.yaml, skipped: $reg_url" >&2
echo "WARNING: registry unreachable or missing index.yaml, skipped: $reg_url" >&2
continue
fi
RES_URL="$(qmap_field "$index" "packages" "$name" "url")"
Expand Down
8 changes: 4 additions & 4 deletions cli/lib/sync-cache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -447,14 +447,14 @@ sync_cache_publish() {
mv -f "$seal_staged" "$SC_DIRECTORY/seal" || rm -f "$seal_staged"
}

# Keep actionable diagnostics, including the multi-line unsynced-directory
# report that compact mode intentionally omits. Never replay renderer progress.
# Keep actionable diagnostics in the same form compact mode keeps; never replay
# renderer progress.
sync_cache_report() {
awk '
/^(WARNING:| WARN:)/ { warning=1; print; next }
/^WARNING:/ { warning=1; print; next }
warning && /^ / { print; next }
{ warning=0 }
/^(CONTEXT:|IS_STATUS=ok|=== Done:|=== WARNING:| NOT SYNCED:| Wire one in:)/ { print }
/^(CONTEXT:|IS_STATUS=ok|=== Done:)/ { print }
' "$1"
}

Expand Down
27 changes: 25 additions & 2 deletions cli/tests/e2e-compat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# Hermetic e2e for the schema-compatibility gate: a CLI OLDER than the project.
# A project stamped a newer minor or patch within the same major syncs with one
# warning and is never restamped or re-pinned downward; a newer major is refused
# with the `ahead-of-engine` status and exit 4. No network: the engine-content
# store is seeded from this tree.
# with the `ahead-of-engine` status and exit 4. Compact sync keeps every warning
# within its line contract. No network: the engine-content store is seeded from
# this tree.
set -euo pipefail
unset CI
REPO="${1:-$(cd "$(dirname "$0")/../.." && pwd)}"
Expand Down Expand Up @@ -245,6 +246,28 @@ else
echo " (engine $ENGINE_VER has no lower version to stamp; skipped)"
fi

echo "== 7. compact output keeps every warning, the frontmatter checks included =="
# A description over 1024 characters gets the agent rejected at load time. The
# first sync `init` runs is compact, so a warning compact output drops is one
# nobody reads: a full render and the unchanged run that replays it both carry it.
P6="$OUT/lint-warning"
make_project "$P6" "$ENGINE_VER"
mkdir -p "$P6/intelligence/agents"
awk '{ print } $0 == " agents:" { print " - \"intelligence/agents\"" }' "$P6/intelligence.yaml" > "$OUT/p6.yaml"
mv "$OUT/p6.yaml" "$P6/intelligence.yaml"
long_description="$(printf '%01100d' 0 | tr 0 x)"
printf -- '---\nname: verbose\ndescription: "%s"\n---\n\n# Verbose\n' "$long_description" \
> "$P6/intelligence/agents/verbose.md"
REJECTED="description is 1100 chars — over the 1024-char limit; the skill/agent will be REJECTED at load time"
xok "$REJECTED" "$P6" sync --compact --force
compact_contract_ok "$OUTPUT" || { echo "FAIL: forced compact output broke its contract"; printf '%s\n' "$OUTPUT"; fail=1; }
printf '%s\n' "$OUTPUT" | grep -q "^WARNING: .*verbose.md:3 $REJECTED" \
|| { echo "FAIL: the rejection warning is not a WARNING: line"; printf '%s\n' "$OUTPUT"; fail=1; }
xok "$REJECTED" "$P6" sync --compact
compact_contract_ok "$OUTPUT" || { echo "FAIL: replayed compact output broke its contract"; printf '%s\n' "$OUTPUT"; fail=1; }
xok "Unchanged: generated files are up to date." "$P6" sync
has "$REJECTED"

if [ "$fail" -eq 0 ]; then
echo "e2e-compat: OK"
else
Expand Down
Loading
Loading