Repository navigation
Let sources name a package's directories by full name or alias - #48
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Undeclared-token behavior is inconsistent, and the minor release contradicts the repository’s compatibility contract.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Introduces package:<name>/<dir> source tokens across parsing, lifecycle operations, migration, documentation, and examples.
Changes:
- Expands and validates package source tokens.
- Canonicalizes tokens during package operations and migration.
- Promotes artifacts to
0.19.0with comprehensive tests and documentation.
| File | Description |
|---|---|
packages/sync/skills/intelligence-update-context/SKILL.md |
Recognizes package-token sources. |
packages/sync/skills/intelligence-review-context/SKILL.md |
Clarifies upstream package findings. |
packages/sync/references/conventions.md |
Documents token syntax and migration. |
packages/sync/references/adapters.md |
Documents parser-expanded adapter inputs. |
examples/with-remote-skills/intelligence.yaml |
Migrates remote-package example. |
examples/platform-with-submodules/intelligence.yaml |
Migrates platform example. |
examples/platform-with-submodules/intelligence.lock |
Updates lock to 0.19.0. |
examples/go-api/intelligence.yaml |
Migrates Go example. |
examples/go-api/intelligence.lock |
Updates lock to 0.19.0. |
examples/go-api-with-pi-and-codex/intelligence.yaml |
Migrates Pi/Codex example. |
examples/go-api-with-pi-and-codex/intelligence.lock |
Updates lock to 0.19.0. |
examples/go-api-with-opencode/intelligence.yaml |
Migrates OpenCode example. |
examples/go-api-with-opencode/intelligence.lock |
Updates lock to 0.19.0. |
examples/go-api-with-antigravity/intelligence.yaml |
Migrates Antigravity example. |
examples/go-api-with-antigravity/intelligence.lock |
Updates lock to 0.19.0. |
examples/dotnet-api-with-react-frontend/intelligence.yaml |
Migrates .NET/React example. |
examples/dotnet-api-with-react-frontend/intelligence.lock |
Updates lock to 0.19.0. |
examples/cli-project/intelligence.yaml |
Migrates multi-package example. |
engine/VERSION |
Promotes engine to 0.19.0. |
engine/sync.sh |
Warns about unresolved tokens. |
engine/lib/common.sh |
Implements token parsing and expansion. |
engine/adapters/_template.sh |
Documents expanded source lists. |
docs/cli.md |
Documents public token behavior. |
decisions/0015-name-package-sources-by-package.md |
Records the architecture decision. |
cli/tests/unit-manifest.sh |
Tests canonical spelling and wiring. |
cli/tests/unit-engine.sh |
Tests parsing and classification. |
cli/tests/e2e-sources.sh |
Tests source commands and diagnostics. |
cli/tests/e2e-packages.sh |
Tests collisions and stable ordering. |
cli/tests/e2e-lifecycle.sh |
Tests migration and output identity. |
cli/lib/registry.sh |
Wires packages using tokens. |
cli/lib/manifest.sh |
Implements canonical token rewriting. |
cli/lib/cli-common.sh |
Integrates expanded store checks. |
cli/internal/package-update.sh |
Preserves source ordering during updates. |
cli/internal/package-remove.sh |
Unwires packages by name. |
cli/internal/package-add.sh |
Handles token collisions during add. |
cli/internal/convert-legacy.sh |
Converts legacy paths to tokens. |
cli/internal/check.sh |
Diagnoses invalid package tokens. |
cli/internal/align-project.sh |
Adds lifecycle token migration. |
cli/commands/source.sh |
Handles tokens in source operations. |
cli/commands/init.sh |
Updates generated manifest guidance. |
CHANGELOG.md |
Records 0.19.0 behavior and breakage. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Verification - PASShead: 5bb148a - steps: PR §Verification — checklist (5 items) + numbered steps AC1-AC7; qa_checks: none matched (no docs/qa-checks.md in this repo) - env: local (
Negative probes (beyond the declared steps, per passing step 10/11's boundary)
All three negative probes behaved correctly; no defects found. No |
Code review - CLEANhead: 5bb148a - base: main - files 41, +1146/-200 Critical(none) Warning(none) Suggestion
Claims axis
Verification performed
Summary
|
Verification - PASShead: c1bbbc8 - steps: PR §Verification — checklist (5 items) + numbered steps AC1-AC7; qa_checks: none matched (no docs/qa-checks.md in this repo) - env: local ( Re-verified against the new head (prior reports on this PR name
Negative probes (beyond the declared steps, per the boundary each step names)
FailuresNone. |
Code review - BLOCKhead: c1bbbc8 - base: main - files 41, +1161/-200 Critical
Warning(none) Suggestion(none — the prior review's one suggestion on this PR, the thin acceptance-level trace for the Claims axis
Verification performed
Summary
|
Outcome - MANUALhead: c1bbbc8 - rounds: 2 of 3 - CI green -
|
Verification - PASShead: d76db0c - steps: PR §Verification — checklist (5 items, 1 marked n/a) + numbered steps 1-10; qa_checks: none matched (no docs/qa-checks.md in this repo) - env: local (
Negative probes (beyond the declared steps, per the boundaries steps 1(AC)/6(AC)/8(AC) name)
All probes behaved correctly; no defects found. No |
Code review - CLEANhead: d76db0c - base: main - files 35, +1635/-153 Critical(none) Warning(none) Suggestion
Claims axisPR body and code agree. Spot-checked: Verification corroborationRan independently of the PR's own Summary
|
…tput entry in 0.19.0
Code review - PASS-WITH-WARNINGShead: c25f1e7 - base: main - files 35, +1635/-153 Critical(none) Warning
Suggestion
Claims axis
Verification performed
Summary
|
Verification - PASShead: c25f1e7 - steps: PR Verification section - checklist (5 items) + numbered steps AC1-AC10; qa_checks: none matched (no docs/qa-checks.md in this repo) - env: local (bash cli/intelligence on the worktree, built from head; no PR preview/deployment exists - confirmed 0 deployments via the GitHub API)
|


Summary
sources:could name an installed package's content only by its store path,.intelligence/packages/@ainova-systems/sync/rules. A person can now name it by the package instead, in two more spellings, while the store path stays valid and stays what the CLI writes:@scope/name/<dir>— folder<dir>of the packagepackages:declares as@scope/name. It is a reference only when its first two segments equal a declared package name; otherwise it is an ordinary path, exactly as before.<alias>:<dir>— the same through a per-package alias, stored asalias: "<alias>"in that package'spackages:entry and written only bypackage add --aliasand the newpackage alias <@scope/name> <alias> | --remove.The shape of the work:
IS_PKG_REF_AWKinengine/lib/common.sh) reads the names and aliases inpackages:and expands a reference to its store path inside the awk passread_yaml_list/load_yaml_listsalready make — no extra process, and every reader sees the store path, so output is byte-identical. A reference that names nothing (unknown or shared alias,<dir>that is empty or leaves the package) is left out of the list and named in asyncWARNING:line.init, add, update, restore, alignment, engine-content install) still writes store paths but leaves a directory a section already names in any spelling where it stands;package removeremoves the package's entries in every spelling and its alias;updatedrops only the sections a new version lost and keeps the rest in place (it used to move the package first). Alias validation and uniqueness are checked before anything is fetched or written; removing or replacing an aliassources:uses is refused, naming the entries.status --checkreports unresolved references, malformed or shared aliases, and one package directory listed under two spellings, and now judges an@...path like any project directory.source add|removerefuse references namingintelligence packageand accept any spelling as an anchor.package listshows aliases.- "x" # casx # cinsources:,ignore:andsubmodules:(onmaintoo), so a commented source was skipped in silence andstatus --checkrefused the#. One function,yaml_iteminengine/lib/common.sh, now reads a list entry for the engine and for the CLI'ssources:editors alike: a comment starts at a#after whitespace outside quotes, a#inside quotes stays part of the value, and whitespace outside the quotes is dropped. The editors used to cut#…after removing quotes, so they and the engine disagreed on"a #b"; they now agree (decision 0009, point 7).## [0.19.0]### Addedand### Fixed; no### Breakingitem from this PR. Decision 0019 records the design and what was rejected (shortpackage:names, scope-only form, a prefix, implicit wiring, migration,1.0.0). Decision 0005 is unchanged frommain;intelligence/rules/cli.mdchanges only its "Who owns what" sentence, which now states that the engine reads paths and package references fromsources:and the names and aliases ofpackages:.Type of change
Public CLI check
init,sync,update,upgrade,package,source,adapter,status,registry—aliasis apackagesubcommandsources:intelligence syncVerification
bash cli/tests/verify.shreportsverify okand skipped nothing this change needed — scopeall(lint engine, lint cli, all 18 suites), run in WSL on the final treeintelligence status --checksucceeds in the relevant fixture/projectintelligence syncproduces no unexpected diffRun each step in a POSIX shell (Git Bash, Linux or macOS) with
CIunset.<CLI>=bash /d/Repositories/ainova/intelligence-package-source-token/cli/intelligence(any checkout of this branch:bash <checkout>/cli/intelligence).git initdirectory:<CLI> init --targets claudeexits 0; eachsources:section begins with.intelligence/packages/@ainova-systems/sync/<section>followed byintelligence/<section>, exactly asmainwrites it;.claude/skills/intelligence-sync/SKILL.mdexists;<CLI> status --checkexits 0.find .claude -type f | sort | xargs sha256sumoutside the project; runsed -i 's#"\.intelligence/packages/@ainova-systems/sync/#"@ainova-systems/sync/#' intelligence.yaml;<CLI> syncexits 0; the saved hashes still match (sha256sum -c); thesources:block is exactly as edited (no store path written back);<CLI> status --checkexits 0..claudehashes saved:<CLI> package alias @ainova-systems/sync syncexits 0, the"@ainova-systems/sync"entry underpackages:gainsalias: "sync", andsources:is untouched; thensed -i 's#"\.intelligence/packages/@ainova-systems/sync/#"sync:#' intelligence.yaml;<CLI> syncexits 0; the saved hashes still match;sed -n '/^sources:/,/^[a-z]/p' intelligence.yaml | grep -c @ainova-systemsprints0;<CLI> status --checkexits 0.sources:block;rm -rf .intelligence/packages, then<CLI> syncexits 0 and.claude/skills/intelligence-sync/SKILL.mdexists; then setschema_version: "0.18.1"and run<CLI> sync: exits 0 andschema_versionequalsengine/VERSION. After both runs thesources:block is byte-identical to the saved one.rules/acme.md:<CLI> package add "git+file://<repo>" --name @acme/tools --alias syncexits non-zero naming@ainova-systems/syncas the holder ofsync, leavingintelligence.yamlandintelligence.lockunchanged;--alias 'a/b'is refused the same way;<CLI> package add "git+file://<repo>" --name @acme/tools --alias acmeexits 0, the"@acme/tools"entry hasalias: "acme", and.claude/rules/acme.mdexists.- "nope:rules"hand-added tosources.rules:<CLI> syncexits 0 and prints aWARNING:line namingnope:rules;<CLI> status --checkexits non-zero namingnope:rules. With that line replaced by- "@acme/missing/rules"(no such declared package),<CLI> status --checktreats it as an ordinary project path: exit 0, reported as not created yet..intelligence/packages/@acme/tools/rulesentry replaced byacme:rules:<CLI> package remove @acme/toolsexits 0; noacme:entry, no@acme/toolsentry and noalias: "acme"remain;.claude/rules/acme.mdis gone;<CLI> status --checkexits 0.sourcecommand — from AC3's project withdocs/rulescreated:<CLI> source add rules docs/rules --after sync:rulesyields the ordersync:rules, docs/rules, intelligence/rules;<CLI> source add rules @ainova-systems/sync/rulesand<CLI> source remove rules sync:rulesare each refused, namingintelligence package.## [0.18.1]inCHANGELOG.mdis byte-identical toorigin/main;## [0.19.0]holds Let sources name a package's directories by full name or alias #48's lines under### Added(fixes under### Fixed) and no### Breakingitem from Let sources name a package's directories by full name or alias #48;docs/cli.mddescribes the three spellings, the alias command, and that a CLI ≤ 0.18 reads a hand-written reference as a missing path;decisions/0019-*records the design;decisions/0005-*is byte-identical toorigin/main;intelligence/rules/cli.mddiffers fromorigin/mainonly in the "Who owns what" sentence stating what the engine reads.sources.rulesline- "intelligence/rules"to- "intelligence/rules" # project rulesand createintelligence/rules/marker.mdholding# MarkerandCOMMENT_MARKER;<CLI> syncexits 0 and.claude/rules/marker.mdexists;<CLI> status --checkexits 0;<CLI> source listshows the entry asintelligence/rules.Automated coverage: steps 1–8 and 10 run in order, one heading per criterion, in
cli/tests/e2e-sources.sh("… (ACn)"), with portable stand-ins forsha256sumandsed -iso the suite runs on macOS. The parser's states, expansion, cache and alias reading, and the trailing-comment regression forsources:,ignore:andsubmodules:(it fails onorigin/main), are incli/tests/unit-engine.sh; the editors and alias helpers (sources_remove_package,package_source_listed,sources_find_entry, alias validation, uniqueness and in-use refusal, and the editors reading a commented or#-holding entry exactly as the engine does) incli/tests/unit-manifest.sh;updatekeeping entries in place, dropping a lost section spelled by alias, and a restore leaving a full-name entry as written incli/tests/e2e-packages.sh. Step 9 is prose and release state, checked withgit diff origin/main -- decisions/0005-*(empty),git diff origin/main -- intelligence/rules/cli.md(the one sentence) and a diff of the## [0.18.1]section.Versioned artifacts
CHANGELOG.mdversion; there is no[Unreleased]section or duplicated release date —0.19.0, pendingengine/VERSIONrelease state was checked: append while pending, or select the next SemVer after publication —v0.19.0has no published GitHub Release, so the lines join the existing sectionschema_versionand exact@ainova-systems/syncpin changed with it — n/a: no new version selectedNotes for reviewers
@ainova-systems/syncand a local@acme/tools(rules, agents, skills), eight targets (agents,claude,cursor,codex,copilot,opencode,antigravity,pi), 98 output files.main's CLI on store paths, and this branch on store paths, with full names, with aliases, and with a trailing# commenton everysources:entry (sync --forceeach) produce identical hashes for every output file.status --checkthen reports the directory as listed twice. References are opt-in, so nothing changes for a manifest nobody edits — the docs, the CHANGELOG line and decision 0019 say so.package removenow also removes a hand-written store path to a non-standard directory of the package, which it used to leave dangling.status --checkused to skip every@...entry (a legacy remote-token remnant); it now judges an unmatched one as an ordinary path, which criterion 6 requires.paths:list (engine/adapters/copilot.sh,pi.sh) and in legacy conversion's line rewriter, which splits a comment even inside quotes. None of them is served byread_yaml_list, so they are outside this fix. The CLI editors also accept an item line at any indentation (^[ \t]*-) where the engine reads^ -only; the two differ only on YAML that is invalid anyway.