Skip to content

Made an unchanged sync start far fewer processes and added sync --check - #54

Merged
dzykovic merged 15 commits into
mainfrom
feature/fast-sync-check
Oct 7, 2026
Merged

dzykovic merged 15 commits into
mainfrom
feature/fast-sync-check

Conversation

@dzykovic

@dzykovic dzykovic commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Save hooks, editor tasks and CI need one cheap answer: would intelligence sync change a generated file? The only way to ask was to run sync, and even its cheapest path, an unchanged run answered from the cache, took 3.9–4.9 s on Windows/Git Bash. Nearly all of that went on starting processes, at 40–120 ms each, not on hashing.

  • intelligence sync [adapter] --check changes no generated file and prints one IS_STATUS line. Exit codes: 0 when generated files are up to date; 2 (new status out-of-date) when sync would change them or the project needs alignment, which the check never applies; the failure's own code otherwise. When the record from the last sync or check was made by the same tooling, it answers straight from that record, without rendering, and says which side changed. Without such a record, and always with --check --force, it renders inside the engine's snapshot transaction, compares, and restores every path. A clean answer is recorded, so the next check is fast.

  • Unchanged sync is faster, with byte-identical output:

    • one find | awk | git hash-object pipeline replaces two fingerprint runs;
    • manifest, lock and package descriptor are parsed once;
    • adapter contracts are read in one subshell;
    • the dispatcher sources sync instead of starting another bash.

    Median on this repository's own project: 3.90 s on main, 0.74 s on this branch. The check answers from its record in 0.73 s, or 0.58 s when sources changed.

Merging #48 (package-named sources, decision 0019) extended the single-pass manifest readers: package references expand the way read_yaml_list expands them, so the cache fingerprints such sources at their store path. Decision 0018 item 3 and both test suites cover it.

Design and rejected alternatives: decision 0018. The measured cache follow-up is appended to 0010, and 0002 gains the flag.

Type of change

  • CLI lifecycle or package behavior
  • Sync engine or adapter
  • Rule, agent, or skill content (packages/sync/references/conventions.md: the command line and the out-of-date status row)
  • Documentation
  • Refactor with no behavior change

Public CLI check

  • Uses only the established groups: init, sync, update, upgrade, package, source, adapter, status, registry (a flag on sync)
  • Keeps preview/apply behavior explicit for planned writes (the check writes no tracked or generated file; it may restore the ignored package store and refresh the ignored cache record)
  • Covers automatic project alignment and CI refusal when project schema/content can change (the check never aligns; pending alignment is out-of-date naming intelligence init --apply, with or without CI)
  • Keeps fresh-clone package restoration behind intelligence sync (the check restores a missing or mis-recorded store through the same locked path, because the comparison needs it)

Verification

  • bash cli/tests/verify.sh reports verify ok and skipped nothing this change needed (all, through WSL, on this head)
  • intelligence status --check succeeds in the relevant fixture/project (this repository's root project, on this head)
  • A second intelligence sync produces no unexpected diff (sync then sync --check exits 0 and git status stays empty)
  • Adapter changes were checked against the real tool format and preserve hand-authored sibling files (adapters change only how they source the shared library; rendered output is byte-identical to main, step 10)
  • User-facing docs and examples use the current public command groups

Steps. Shared start state: Windows 11, Git Bash, CI unset. Clone the PR head (git clone --branch feature/fast-sync-check https://github.com/ainova-systems/intelligence.git cli-src) and main (git clone --branch main https://github.com/ainova-systems/intelligence.git cli-main); CLI="bash $PWD/cli-src/cli/intelligence". Scratch project: mkdir proj && git -C cli-src archive HEAD | tar -x -C proj && cd proj && git init -q && git add -A && git commit -qm base && $CLI sync (exit 0; the first run fetches @ainova-systems/core from GitHub). "Reset" = git checkout -- . && git clean -fdq && $CLI sync.

  1. Up to date and fast: $CLI sync --check; echo $? prints one line saying generated files are up to date and 0; git status --porcelain stays empty. Five runs of time $CLI sync --check >/dev/null: median real ≤ 1.0 s.
  2. Same-size source edit with the old mtime restored: f=intelligence/rules/product.md; t=$(stat -c %Y "$f"); sed -i 's/^# Intelligence product repository$/# Intelligencf product repository/' "$f"; touch -d "@$t" "$f". $CLI sync --check exits 2 and says sources changed; five runs median ≤ 1.0 s; git status --porcelain lists only intelligence/rules/product.md.
  3. Hand-edited generated file: reset, echo '<!-- hand edit -->' >> AGENTS.md. $CLI sync --check exits 2 and says generated files changed; tail -1 AGENTS.md is still the hand edit and git status --porcelain lists only AGENTS.md. Then $CLI sync leaves git status --porcelain empty and $CLI sync --check exits 0.
  4. No cache, nothing changed: reset, rm -rf .intelligence/sync-cache. $CLI sync --check exits 0 after rendering; git status --porcelain empty. A second $CLI sync --check exits 0 with a five-run median ≤ 1.0 s.
  5. No cache, source changed: reset, rm -rf .intelligence/sync-cache, echo extra >> intelligence/rules/product.md. $CLI sync --check exits 2; git status --porcelain lists only intelligence/rules/product.md.
  6. Missing store: reset, rm -rf .intelligence. $CLI sync --check restores the package store from intelligence.lock and exits 0; git status --porcelain empty.
  7. Alignment pending: reset, sed -i 's/^schema_version: .*/schema_version: "0.17.3"/' intelligence.yaml. $CLI sync --check exits 2 and names intelligence init; git diff --stat shows only intelligence.yaml with that one line changed. CI=true $CLI sync --check behaves the same.
  8. Errors are distinct: reset, sed -i 's/^lockfile_version: 1$/lockfile_version: 99/' intelligence.lock; $CLI sync --check exits 1 with the lock error. In an empty temporary directory $CLI sync --check exits 1.
  9. Strict mode: on a reset project $CLI sync --check --force renders and exits 0 with git status --porcelain empty; with the criterion-2 edit it exits 2.
  10. Ordinary sync unchanged and faster: in a reset project run bash ../cli-main/cli/intelligence sync --force && git add -A && git commit -qm main-render, then $CLI sync --force: git status --porcelain empty. On two copies of the scratch project, one synced by each CLI, five alternating cache-hit sync runs: the branch's median real is lower than main's.

Automated coverage

  • e2e-sync-check (new, in SUITES):
    • exit codes 0, 2 and 1;
    • fast answers in both directions, proved by a marker the engine would have written;
    • the render path restoring bytes, links and executable bits, then recording a clean answer;
    • --force, adapter filters, and old-format or damaged records;
    • pending alignment, with and without CI=true;
    • store restore and lock errors.
  • e2e-sync-cache adds file names the fingerprint cannot carry, which must fall back to ordinary sync.
  • unit-manifest: preloaded answers equal the ordinary reader, a write clears the preload, and partial output is rejected.
  • unit-engine: the combined manifest pass equals the single readers.
  • bash cli/tests/verify.sh all passes on this head through WSL. Natively under Git Bash (INTELLIGENCE_VERIFY_NATIVE=1), e2e-sync-check (113 checks) and e2e-sync-cache (245 checks) passed on e0a7523 (e2e-sync-cache dropped from ~635 s to ~465 s); that native result predates the merges of Made status --check decide .gitignore presence with the writer's CR-blind test #55 and Added a tool-neutral effort field and stopped deriving Codex reasoning effort from the tier #46 and the link case added to e2e-sync-check, which this host cannot run natively because Git Bash here cannot create symlinks.

Versioned artifacts

  • The PR records its change under one concrete CHANGELOG.md version; there is no [Unreleased] section or duplicated release date
  • The current engine/VERSION release state was checked: append while pending, or select the next SemVer after publication (v0.19.0 is published without this change, so it selects 0.20.0, a minor for the new flag, in a new ## [0.20.0] section; everything from ## [0.19.0] down is byte-identical to main)
  • If a new version was selected, every example's schema_version and exact @ainova-systems/sync pin changed with it (with their lock rows; the root project was aligned by init --apply)
  • No stable release is implied or published without owner approval

Notes for reviewers

  • Postponed by the owner: the review at 2b80adb found that the sync --check render path compares through symlinks. That fix, plus the review's warnings and suggestions, is tracked in sync --check follows symlinks when comparing rendered output with its snapshot #60.

  • Fast answers can over-report. A 2 answered from the record compares content, not rendered output, so an edit that renders identically, such as a comment in intelligence.yaml, reports "sources changed". --check --force gives the exact answer. This was a deliberate trade for speed.

  • When the record does not answer. PATH, locale, umask, the adapter filter and CLI/engine code are part of the tooling fingerprint. A check from an environment other than the last sync's renders once, about 8 s on Windows, and records its own answer. An upgrade therefore renders instead of claiming that sync is needed.

  • Cache format. The record moves to intelligence-sync-cache-v2: tooling, inputs and outputs as separate fingerprints, sealed by its own hash. Earlier records are ignored, so the first sync after upgrading renders once.

  • Exit code 2. Under --check, 2 means only "sync needed". config-missing, which shares code 2, cannot reach a check, and any other failure that would end in 2 is reported as 1.

  • The 1-second target is met on a quiet host but has little margin. With other sessions loading the machine, the record answer measured about 1.2 s, while main's cache hit took 6 s.

  • Not run locally: macOS bash 3.2. CI's cli-e2e covers it. Under mawk, e2e-sync-cache fails one test identically on main; that is pre-existing and not touched here.

Copilot AI balanced review requested due to automatic review settings October 7, 2026 13:10
@dzykovic dzykovic added the ai:processing An agent is working on this pull request label Oct 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Executable-mode changes can be missed, and several check failures violate the documented single-status-line API.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds a read-only sync --check mode and substantially reduces process startup overhead on unchanged syncs.

Changes:

  • Adds cached and strict render-based sync checks.
  • Consolidates manifest parsing, adapter contracts, and fingerprinting.
  • Updates tests, documentation, decisions, and versioned examples to 0.19.0.
File Description
README.md Documents sync --check.
packages/​sync/​references/​conventions.md Defines check semantics and status.
intelligence/​rules/​engine.md Documents engine check transaction.
intelligence/​rules/​cli.md Records cache performance conventions.
intelligence.yaml Updates schema and package pin.
intelligence.lock Updates engine and package lock.
examples/​with-remote-skills/​intelligence.yaml Updates version pins.
examples/​platform-with-submodules/​intelligence.yaml Updates version pins.
examples/​platform-with-submodules/​intelligence.lock Updates lock version.
examples/​go-api/​intelligence.yaml Updates version pins.
examples/​go-api/​intelligence.lock Updates lock version.
examples/​go-api-with-pi-and-codex/​intelligence.yaml Updates version pins.
examples/​go-api-with-pi-and-codex/​intelligence.lock Updates lock version.
examples/​go-api-with-opencode/​intelligence.yaml Updates version pins.
examples/​go-api-with-opencode/​intelligence.lock Updates lock version.
examples/​go-api-with-antigravity/​intelligence.yaml Updates version pins.
examples/​go-api-with-antigravity/​intelligence.lock Updates lock version.
examples/​dotnet-api-with-react-frontend/​intelligence.yaml Updates version pins.
examples/​dotnet-api-with-react-frontend/​intelligence.lock Updates lock version.
examples/​cli-project/​intelligence.yaml Updates version pins.
engine/​VERSION Bumps engine to 0.19.0.
engine/​sync.sh Adds snapshot comparison and restoration.
engine/​lib/​contract.sh Adds out-of-date return contract.
engine/​lib/​common.sh Adds combined manifest parsing.
engine/​lib/​adapter-contract.sh Batches built-in contract reads.
engine/​adapters/​pi.sh Avoids redundant common-library loading.
engine/​adapters/​opencode.sh Avoids redundant common-library loading.
engine/​adapters/​cursor.sh Avoids redundant common-library loading.
engine/​adapters/​copilot.sh Avoids redundant common-library loading.
engine/​adapters/​codex.sh Avoids redundant common-library loading.
engine/​adapters/​claude.sh Avoids redundant common-library loading.
engine/​adapters/​antigravity.sh Avoids redundant common-library loading.
engine/​adapters/​agents.sh Optimizes loading and output lookup.
docs/​cli.md Documents check behavior and environment contract.
decisions/​0015-check-whether-sync-is-needed.md Records the check design.
decisions/​0010-skip-unchanged-local-sync.md Records fingerprint optimization results.
decisions/​0002-consolidate-intelligence-cli-lifecycle.md Adds the lifecycle amendment.
cli/​tests/​verify.sh Registers the new check suite.
cli/​tests/​unit-manifest.sh Tests qmap preloading.
cli/​tests/​unit-engine.sh Tests combined manifest views.
cli/​tests/​e2e-sync-check.sh Covers check behavior end to end.
cli/​tests/​e2e-sync-cache.sh Extends cache fallback coverage.
cli/​lib/​sync-lock.sh Optimizes locking and adds scratch paths.
cli/​lib/​sync-cache.sh Implements v2 fingerprints and checks.
cli/​lib/​qmap.awk Supports batched document reads.
cli/​lib/​manifest.sh Adds qmap memoization and preloading.
cli/​lib/​lockfile.sh Uses preloaded lock reads.
cli/​lib/​cli-common.sh Consolidates lifecycle preflight reads.
cli/​intelligence Sources sync in the dispatcher process.
cli/​commands/​sync.sh Adds check option and orchestration.
CHANGELOG.md Records the 0.19.0 changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cli/commands/sync.sh
Comment thread cli/lib/sync-cache.sh
Comment thread engine/sync.sh
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Code review - CLEAN

head: 2c803bc - base: main - files 50, +2118/-327

Critical

(none)

Warning

(none)

Suggestion

  • engine/sync.sh:691-694 - when compare_sync_snapshot returns rc=2 (the comparison itself failed, e.g. a find/diff error), sync --check prints only a generic "could not compare" message with no path or step named. A rare failure here is harder to triage in CI than it needs to be; naming the offending snapshot index/path on that line would cost nothing and save a bisection the next time it fires.

Claims axis (PR body vs. diff)

  • Every behavior the body claims is backed by code: --check exit codes/IS_STATUS contract (cli/commands/sync.sh:88-130, cli/lib/sync-cache.sh:512-569, engine/lib/contract.sh:84-91), the record-vs-render split (cli/lib/sync-cache.sh:521-569), the single find|awk|git hash-object fingerprint and batched qmap/manifest reads (cli/lib/sync-cache.sh:110-230, cli/lib/cli-common.sh:510-565, cli/lib/manifest.sh:184-314, cli/lib/qmap.awk), and the dispatcher sourcing sync instead of forking bash (cli/intelligence:117-122).
  • Decision 0018 is new and accepted; 0010 and 0002 carry append-only amendments consistent with dev-context-engineering's rule that a convention change updates its record in the same commit.
  • engine/lib/contract.sh:91 and engine/sync.sh confirm the engine itself never emits out-of-date (grepped) - matches the body's and decision 0018's claim that only the CLI check reuses status 2 that way.
  • Versioned artifacts: v0.18.1 is published (verified via gh release view v0.18.1), so selecting 0.19.0 is correct; engine/VERSION, CHANGELOG.md (## [0.19.0] added, ## [0.18.1] left untouched, no [Unreleased], no date), every example's schema_version and @ainova-systems/sync pin, and the root project's own manifest/lock all moved together - checked across all 8 examples plus root, no stragglers.
  • No deployment-notes gap: the "Notes for reviewers" section already names the one migration-like change (cache format v2, old records ignored, reversible per decision 0018's "Rejected"/consequences section).
  • No undeclared scope found: the diff's areas (cli lib, engine lib/adapters, tests, decisions, docs, examples, changelog) all map to sections the body already describes.
  • CI is green on this head (cli-e2e macos/ubuntu, cli-shellcheck, shellcheck, repo-purity, guardrails, npm-pack-smoke x3, smoke x12 - all pass), consistent with the body's verification claims; nothing in the diff contradicts a passing step.

Sibling drift

  • New adapter changes (engine/adapters/*.sh) are mechanical and uniform across all 8 adapter files (guard a source of common.sh behind declare -F finalize_output_file) - no adapter departs from its siblings.
  • cli/tests/e2e-sync-check.sh follows the fixture/instrumentation conventions already used by cli/tests/e2e-sync-cache.sh (hermetic TMP, file://-style isolation from the real checkout, same check/fail helpers) - no drift.

Summary

  • files: 50, +2118/-327; critical: 0; warning: 0; suggestion: 1
  • verdict: CLEAN

@dzykovic dzykovic added the ai:reviewed Diff reviewed on the reported head label Oct 7, 2026
@dzykovic dzykovic removed the ai:reviewed Diff reviewed on the reported head label Oct 7, 2026
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Code review - CLEAN

head: 0bcc635 - base: main - files 34, +2095/-287

Critical

(none)

Warning

(none)

Suggestion

  • engine/sync.sh:691-694 - when compare_sync_snapshot returns rc=2 (the comparison itself failed - a cmp/diff/find error), sync --check prints only a generic "could not compare" message with no index/path named. This branch is rare and hard to trigger, but when it does fire in CI it gives the next debugger nothing to start from; naming the offending snapshot index or path on that line would cost one more variable and save a bisection.

Claims axis (PR body vs. diff)

  • Every behavior the body claims is backed by code: the --check exit-code/IS_STATUS contract (cli/commands/sync.sh:120-162, cli/lib/sync-cache.sh:525-581, engine/lib/contract.sh:84-91), the record-vs-render split and "answers without rendering, names what changed" behavior (cli/lib/sync-cache.sh:538-563), the single find|awk|git hash-object fingerprint pipeline and batched qmap/manifest/contract reads (cli/lib/sync-cache.sh:154-396, cli/lib/manifest.sh:157-269, engine/lib/common.sh:1973-2060, engine/lib/adapter-contract.sh:108-140), and the dispatcher sourcing sync instead of forking bash (cli/intelligence:114-121).
  • Decision 0018 is new, dated, accepted, and traces every numbered item to code; 0010 and 0002 carry append-only amendments, matching dev-context-engineering's rule that a convention change updates its record in the same commit.
  • Verified engine/lib/contract.sh:88-89 (IS_RC_OUT_OF_DATE=2, documented as CLI-only) and confirmed the engine itself never emits out-of-date (grepped engine/) - matches the body's and decision 0018's claim.
  • Versioned artifacts: v0.18.1 is published (gh release view v0.18.1), so selecting 0.19.0 for this PR's lines is correct per release.md; engine/VERSION (0.19.0), CHANGELOG.md (## [0.19.0] carries this PR's Added/Changed lines, ## [0.18.1] untouched, no [Unreleased], no date), every example's schema_version/@ainova-systems/sync pin, and this repository's own root manifest/lock all already sit at 0.19.0 - the body's claim that Made status --check decide .gitignore presence with the writer's CR-blind test #55 moved them and they merge identically checks out (no example or lockstep file appears in this diff, correctly).
  • AGENTS.md is untouched, correctly: cli.md/engine.md are path-scoped rules, and per engine.md's own routing rule only always-on/global rules get inlined into AGENTS.md - no drift between the changed intelligence/rules/{cli,engine}.md and the one generated artifact this repository tracks (every other tool directory, .claude/ included, is gitignored here).
  • No deployment-notes gap: "Notes for reviewers" already names the one migration-like change (cache format v2, old records ignored and harmless, explicitly reversible).
  • No undeclared scope: every changed area (cli lib, engine lib/adapters, new/extended test suites, decisions, docs, rules, changelog) maps to a section the body already describes.
  • CI is green on this exact head (21/21 checks, including cli-e2e on ubuntu-latest and macos-latest) - nothing in the diff contradicts the body's verification claims.

Sibling drift

  • The eight engine/adapters/*.sh edits are mechanical and identical in shape (guard the common.sh source behind declare -F finalize_output_file) - no adapter departs from its siblings, and cli-common.sh already sources engine/lib/common.sh before any adapter runs, so the guard is live on every call path that matters.
  • cli/tests/e2e-sync-check.sh follows the hermetic fixture/instrumentation conventions already used by cli/tests/e2e-sync-cache.sh (isolated TMP outside the checkout, a bash wrapper for render instrumentation, check/fail helpers) - no drift from its closest sibling suite.

Summary

  • files: 34, +2095/-287; critical: 0; warning: 0; suggestion: 1
  • verdict: CLEAN

@dzykovic dzykovic added the ai:reviewed Diff reviewed on the reported head label Oct 7, 2026
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Verification - PASS

head: 0bcc635 - steps: sha1:52f816a43d90 (Verification section: checklist + Steps 1-10; docs/qa-checks.md absent, no standing checks matched) - env: local (fresh clones of the PR head and main from GitHub into a scratch dir; no preview/app_url exists for this CLI project)

Setup per the PR shared start state (Windows 11, Git Bash, CI unset): cloned feature/fast-sync-check as cli-src (HEAD 0bcc635 confirmed before and after the run) and main as cli-main; built the scratch project via git -C cli-src archive HEAD | tar -x -C proj, then git init, add, commit base, then $CLI sync - exit 0, package store restored, git status --porcelain empty.

# Step Source Verdict Observed
C1 bash cli/tests/verify.sh reports verify ok, skips nothing needed (all, WSL, this head) PR checklist pass Ran bash cli/tests/verify.sh all in cli-src under WSL: all gates (lint: engine, lint: cli) and all 19 suites passed, ending === verify ok ===, exit 0, real 2m54.767s. No suite reported skipped.
C2 intelligence status --check succeeds in the relevant fixture/project (this repository root project, on this head) PR checklist pass Read "this repository root project" as cli-src itself (the repo self-hosts its own Intelligence project). A fresh clone gitignored store is not present, so ran sync once to restore it (exit 0, no tracked diff), then status --check printed all good., exit 0.
C3 A second intelligence sync produces no unexpected diff (sync then sync --check exits 0, git status stays empty) PR checklist pass In cli-src: sync exit 0; sync --check gave IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0; git status --porcelain empty.
C4 Adapter changes preserve hand-authored sibling files; rendered output byte-identical to main (step 10) PR checklist pass Evidence is Step 10 below: main render left nothing to commit against the already branch-synced tree, and the branch own re-render afterward left git status --porcelain empty.
C5 User-facing docs and examples use current public command groups PR checklist pass Grepped docs/cli.md, README.md, CONTRIBUTING.md, examples/*/README.md for intelligence <word>; every real invocation is one of init/sync/update/upgrade/package/source/adapter/status/registry. intelligence help (README.md:100) is the dispatcher built-in usage alias, not a lifecycle group.
1 Up to date and fast: sync --check prints up-to-date + 0; git status --porcelain empty; 5-run median <= 1.0s PR step pass IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0; status empty. 5 runs real: 0.981s, 0.765s, 0.817s, 0.745s, 0.874s - median 0.817s.
2 Same-size source edit, old mtime restored: exits 2, says sources changed; 5-run median <= 1.0s; status lists only the file PR step pass IS_STATUS=out-of-date IS_DETAIL=sources changed since the last sync; run 'intelligence sync', exit 2; status showed only intelligence/rules/product.md. 5 runs real: 0.695s, 0.694s, 0.853s, 1.362s, 0.770s - median 0.770s (one run spiked under host variance; median still met).
3 Hand-edited generated file: exits 2, says generated files changed, edit survives, status lists only the file; then sync clears it and sync --check exits 0 PR step pass IS_STATUS=out-of-date IS_DETAIL=generated files changed since the last sync; run 'intelligence sync', exit 2; tail of AGENTS.md still the hand edit; status showed only AGENTS.md. After sync: status empty, sync --check gave IS_STATUS=ok, exit 0.
4 No cache, nothing changed: sync --check exits 0 after rendering, status empty; second check 5-run median <= 1.0s PR step pass After removing .intelligence/sync-cache: IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0 (cold render, 11.7s, untimed by the step); status empty. 5 cache-hit runs real: 0.799s, 0.862s, 0.654s, 0.828s, 0.702s - median 0.799s.
5 No cache, source changed: sync --check exits 2, status lists only the file PR step pass IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files; run 'intelligence sync', exit 2; status showed only intelligence/rules/product.md.
6 Missing store: sync --check restores the package store and exits 0, status empty PR step pass After removing .intelligence entirely: output showed restoring package store from intelligence.lock for both @ainova-systems/core and @ainova-systems/sync; IS_STATUS=ok ... (rendered to compare), exit 0; status empty.
7 Alignment pending: sync --check exits 2, names intelligence init; git diff --stat shows only intelligence.yaml, one line; CI=true behaves the same PR step pass IS_STATUS=out-of-date IS_DETAIL=the project needs alignment (stamp 0.17.3, engine 0.19.0); run 'intelligence init --apply' locally, review and commit the diff, exit 2; git diff --stat showed intelligence.yaml with 1 insertion/1 deletion. CI=true sync --check printed the identical line, exit 2.
8 Errors are distinct: bad lock gives exit 1 with the lock error; empty temp dir gives exit 1 PR step pass Bad lockfile_version: 99 gave an unsupported-version message plus ERROR: invalid intelligence.lock - recovery: ..., exit 1. In an empty dir: ERROR: no intelligence project found here - run 'intelligence init', exit 1.
9 Strict mode: sync --check --force on a reset project renders, exits 0, status empty; with the criterion-2 edit it exits 2 PR step pass Clean project: IS_STATUS=ok ... (rendered to compare), exit 0, status empty. With the same-size edit reapplied: IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files..., exit 2.
10 Ordinary sync unchanged and faster: main render + commit + branch sync --force leaves status empty; two copies, 5 alternating cache-hit sync runs, branch median below main PR step pass bash ../cli-main/.../intelligence sync --force && git add -A && git commit -qm main-render - the only non-zero point in the chain was git commit reporting nothing to commit, working tree clean (main render was already byte-identical to the branch-synced tree); $CLI sync --force (branch) gave exit 0, git status --porcelain empty throughout - confirms byte-identical rendered output. On two independently-synced fresh copies, 5 alternating cache-hit sync runs each: main real 4.173s, 4.898s, 6.865s, 5.544s, 5.655s -> median 5.544s; branch real 0.625s, 1.160s, 1.227s, 1.180s, 0.852s -> median 1.160s. Branch median is lower, as required.

Negative probes (skill step 5; not PR-declared, carry no separate verdict)

  • sync --check --this-flag-does-not-exist -> ERROR: unknown option '--this-flag-does-not-exist', exit 1 (no false ok).
  • sync nonexistent-adapter --check -> ERROR: Adapter 'nonexistent-adapter' not found. Available: agents antigravity claude codex copilot cursor opencode pi; ERROR: sync failed; all adapter-owned paths were restored to their pre-sync state., exit 1 (no partial write on an invalid filter).
  • .intelligence/sync-cache made read-only (chmod 500), then sync --check: still answered correctly, IS_STATUS=ok ... (rendered to compare), exit 0, status empty - the check degrades to a correct render instead of failing when it cannot persist a fresh record.

No defects found by any of the three probes above.

@dzykovic dzykovic added the ai:verified Declared behavior verified on the reported head label Oct 7, 2026
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Outcome - MANUAL

head: 0bcc635

CI is green with 21/21 checks, and ai:verified and ai:reviewed are both fresh at this head. All three review threads are answered and resolved. The run stops here because the default max_pr_rounds of 3 is spent and the items below are left:

  • docs/cli.md, "Checking whether sync is needed": the lead sentence "It prints one status line, IS_STATUS=<code> IS_DETAIL=<reason>, and exits:" reads as a status line on every exit. In practice a failure other than ahead-of-engine prints nothing on stdout: sync --check in an empty directory exits 1 with empty stdout. This matches decision 0018 §1 and run_check, which send only the verdict to stdout; the docs do not. The fix is one sentence, for example "An answer is one status line, IS_STATUS=<code> IS_DETAIL=<reason>; a failure prints its diagnostics on stderr and keeps sync's status." It would be a fourth fix round. Decision needed: authorize that round, or accept the current wording.
  • Verification step 10: the chain bash ../cli-main/cli/intelligence sync --force && git add -A && git commit -qm main-render exits 1 when main's render already equals the branch-synced tree, because there is nothing to commit. The verifier recorded this verbatim and judged the step passed on the identical render. Decision needed: reword the step, for example git commit --allow-empty -qm main-render, or keep it.

@dzykovic dzykovic added ai:manual Needs an owner decision and removed ai:processing An agent is working on this pull request labels Oct 7, 2026
@dzykovic dzykovic added ai:processing An agent is working on this pull request and removed ai:manual Needs an owner decision ai:verified Declared behavior verified on the reported head ai:reviewed Diff reviewed on the reported head labels Oct 7, 2026
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Verification - FAIL

head: 2b80adb - steps: sha1:52f816a43d90 (Verification section: checklist + Steps 1-10, unchanged since the 0bcc635 report; docs/qa-checks.md absent at head, no standing checks matched) - env: local (fresh clones of the PR head and of main 3adf722 from GitHub into a scratch dir; no preview or app_url exists for this CLI project)

Setup per the PR's shared start state (Windows 11, Git Bash 5.3.9, CI unset): cloned feature/fast-sync-check as cli-src (HEAD 2b80adb, confirmed before and after the run, PR head unchanged) and main as cli-main; built proj with git -C cli-src archive HEAD | tar -x -C proj, git init, add, commit base, then $CLI sync: package store restored, exit 0, git status --porcelain empty. Every "reset" below ran git checkout -- . && git clean -fdq && $CLI sync and exited 0 with an empty status.

# Step Source Verdict Observed
C1 bash cli/tests/verify.sh reports verify ok and skipped nothing this change needed (all, through WSL, on this head) PR checklist pass In cli-src at 2b80adb: bash cli/tests/verify.sh all printed === WSL: 'all' on a copy of this tree ... ===, ok: lint: engine, ok: lint: cli, ran all 19 suites (sync-check: 134 checks passed, sync-cache: 254 checks passed, ...), ok: tests: CLI suites, === verify ok ===, exit 0, real 2m31.735s. No gate or suite was skipped. The in-suite notes come with running under WSL: SKIP Windows junction case: non-Windows host (e2e-sync-cache) and NOTE: node is not on PATH — launcher verification not exercised (unit-upgrade).
C2 intelligence status --check succeeds in the relevant fixture/project (this repository's root project, on this head) PR checklist pass In cli-src (fresh clone, no store): $CLI sync restored the store from the lock, exit 0, no tracked diff. Then $CLI status --check showed every line as ✓ (schema_version 0.19.0 matches the engine; sync v0.19.0 and core v0.9.3; adapters agents, codex, claude and antigravity at contract v1) and ended with all good., exit 0.
C3 A second intelligence sync produces no unexpected diff (sync then sync --check exits 0 and git status stays empty) PR checklist pass In cli-src: sync exit 0, git status --porcelain empty; sync --check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0; status still empty.
C4 Adapter changes preserve hand-authored sibling files; rendered output byte-identical to main (step 10) PR checklist pass Judged on step 10's evidence, as the item says to: main's sync --force over the branch-synced tree left nothing to commit, and the branch's sync --force after it left git status --porcelain empty.
C5 User-facing docs and examples use the current public command groups PR checklist pass Grepped README.md, CONTRIBUTING.md, docs/ and examples/ for intelligence <word>. The invocations are only package (14), sync (13), init (13), update (10), adapter (10), upgrade (7), status (7), registry (6) and source (4), plus intelligence help (README.md:100), which is the dispatcher's usage alias. The other five matches are prose ("intelligence from/in/at/layer/pulled"). docs/cli.md:89 documents intelligence sync [adapter] [--compact] [--force] [--check].
1 Up to date and fast: sync --check prints one up-to-date line and 0; git status --porcelain empty; five-run median real <= 1.0 s PR step fail IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0; status empty before and after the timings. Five runs: real 1.253s, 0.929s, 1.137s, 1.057s, 1.584s, so the median is 1.137s, above the 1.0 s bound.
2 Same-size source edit with the old mtime restored: exits 2 and says sources changed; five-run median <= 1.0 s; status lists only intelligence/rules/product.md PR step pass mtime was 1791387676 before and after the edit. IS_STATUS=out-of-date IS_DETAIL=sources changed since the last sync; run 'intelligence sync', exit 2. Five runs: real 0.914s, 0.766s, 0.861s, 0.785s, 0.772s, median 0.785s. Status showed only M intelligence/rules/product.md.
3 Hand-edited generated file: exits 2 and says generated files changed; edit survives; status lists only AGENTS.md; then sync empties status and sync --check exits 0 PR step pass After reset: IS_STATUS=out-of-date IS_DETAIL=generated files changed since the last sync; run 'intelligence sync', exit 2; tail -1 AGENTS.md was <!-- hand edit -->; status showed only M AGENTS.md. Then sync exit 0 with status empty, and sync --check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0.
4 No cache, nothing changed: sync --check exits 0 after rendering; status empty; a second check exits 0 with five-run median <= 1.0 s PR step pass After reset and removing .intelligence/sync-cache: IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0 (real 15.764s for the render, which the step does not time); status empty. The second check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0. Five runs: real 0.922s, 0.889s, 0.851s, 0.801s, 0.776s, median 0.851s; status empty.
5 No cache, source changed: sync --check exits 2; status lists only intelligence/rules/product.md PR step pass IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files; run 'intelligence sync', exit 2; status showed only M intelligence/rules/product.md.
6 Missing store: sync --check restores the package store from intelligence.lock and exits 0; status empty PR step pass After reset and rm -rf .intelligence: restoring package store from intelligence.lock, = @ainova-systems/core@v0.9.3, WARNING: bundled sync content restored without commit verification (development bundle or lock has no SHA), = @ainova-systems/sync@v0.19.0, then IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0. The store holds core and sync; status empty.
7 Alignment pending: sync --check exits 2 and names intelligence init; git diff --stat shows only intelligence.yaml with one line changed; CI=true behaves the same PR step pass IS_STATUS=out-of-date IS_DETAIL=the project needs alignment (stamp 0.17.3, engine 0.19.0); run 'intelligence init --apply' locally, review and commit the diff, exit 2. git diff --stat showed only intelligence.yaml: 1 file changed, 1 insertion(+), 1 deletion(-) (schema_version: "0.19.0" to "0.17.3"). CI=true $CLI sync --check printed the identical line, exit 2, with the same diff stat.
8 Errors are distinct: a bad lock exits 1 with the lock error; an empty temporary directory exits 1 PR step pass .../proj/intelligence.lock has unsupported or missing lockfile_version '99' — expected 1 and ERROR: invalid intelligence.lock — recovery: https://github.com/ainova-systems/intelligence/blob/main/docs/cli.md#recovering-a-lock, exit 1. In an empty scratch directory: ERROR: no intelligence project found here - run 'intelligence init', exit 1.
9 Strict mode: sync --check --force on a reset project renders and exits 0 with status empty; with the criterion-2 edit it exits 2 PR step pass After reset: IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0, status empty. With the same-size edit and its mtime restored: IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files; run 'intelligence sync', exit 2.
10 Ordinary sync unchanged and faster: main render, git add -A, git commit -qm main-render, then $CLI sync --force leaves status empty; two copies, five alternating cache-hit sync runs, branch median below main PR step pass After reset, bash ../cli-main/cli/intelligence sync --force (main 3adf722) ended with === Sync complete ===, and git add -A ran. git commit -qm main-render exited 1 with nothing to commit, working tree clean, because main's render already equals the committed files; the owner accepted this as part of the step. $CLI sync --force exit 0; git status --porcelain empty. Two cp -a copies of the reset project, each primed by its own CLI, then five alternating cache-hit sync runs (every log shows Unchanged: generated files are up to date.). main: 5.738s, 4.694s, 4.854s, 5.792s, 5.144s, median 5.144s. branch: 0.835s, 0.729s, 0.730s, 1.056s, 1.348s, median 0.835s. The branch median is lower. Both copies were left with an empty status.

Failures

  • Step 1 - expected a five-run median real <= 1.0 s for sync --check on an up-to-date project; observed 1.137 s (runs 1.253 / 0.929 / 1.137 / 1.057 / 1.584 s). The answer itself was correct: IS_STATUS=ok, exit 0. Repro: shared start state, then in proj run for i in 1 2 3 4 5; do time $CLI sync --check >/dev/null; done. Suspect: no code location found. The host was loaded, sampled right after step 1: CPU 48% average, 9 other Claude processes and 22 bash processes. The same record-answer path measured medians of 0.785 s (step 2) and 0.851 s (step 4) minutes later, and the PR's own notes say the 1-second target has little margin under load. A rerun on a quieter host decides this; this report does not.

Negative probes (skill step 5) were not run in this pass, because the run was scoped to the declared steps.

@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Code review - BLOCK

head: 2b80adb - base: main - files 34, +2095/-287

Critical

  • engine/sync.sh:294 - compare_sync_snapshot compares directories with diff -r, which follows links. Only the snapshotted path itself is checked with -L (:288), so a link inside an owned or managed directory is compared by its target's bytes. A relative link is also resolved from the snapshot copy under $SYNC_TX_DIR/data/<index>/, not from the repository. Reproduced on this head in WSL with GNU diffutils 3.10, using a skill asset under intelligence/skills/demo/assets/:

    • With a dangling link, or a relative link that leaves the snapshotted directory (../../../../shared.txt), sync exits 0. sync --check and sync --check --force exit 1 on every run with "could not compare the rendered output with its snapshot".
    • When a source asset changes from a link to a regular file with the same bytes, sync --check exits 0 with "generated files are up to date (rendered to compare)". sync then turns .claude/skills/demo/assets/abs from a link into a regular file. A link retargeted to a file with identical bytes is judged "same" in the same way.

    This breaks decision 0018 item 4, "compares every snapshotted path with what the render left (names, entry kinds, bytes, executable bits)" (decisions/0018-check-whether-sync-is-needed.md:55-56). It also breaks docs/cli.md:209, where an error keeps sync's own status. Links inside skills are supported input (engine/lib/common.sh:454), and the fingerprint sends every project that has one down this render path. The link case in cli/tests/e2e-sync-check.sh:290-313 passes only because its absolute target resolves from both places.

Warning

  • cli/lib/sync-cache.sh:149 - the outputs fingerprint walks whole owned and managed paths, including the sibling files that sync keeps on purpose (engine/lib/common.sh:643-644). Reproduced: after a hand-written .agents/skills/README.md is edited, sync --check exits 2 with "generated files changed since the last sync; run 'intelligence sync'". --check --force exits 0, and sync leaves the file unchanged. docs/cli.md:226 says only the source side can over-report.
  • engine/sync.sh:696 - on the render path the check restores every snapshotted path with rm -rf and cp -a (:232-242). Any write to those paths during the render is reverted, such as saving a kept sibling in .agents/skills/ while a save hook's check renders, which takes about 8 s on Windows. sync itself prunes only skill subdirectories there. docs/cli.md:202 says the check changes no generated file, and decision 0018 does not record this window.
  • cli/lib/sync-cache.sh:490 - ordinary sync starts the engine without clearing IS_SYNC_CHECK, and the engine acts on any inherited value (engine/sync.sh:685). Reproduced with IS_SYNC_CHECK exported: sync after a rule edit prints "=== Sync complete ===", keeps the old output and records it as current. The next plain sync prints "Unchanged" and sync --check exits 0, until sync --force. export_engine_env sets every other engine variable explicitly.
  • PR body, "Notes for reviewers" - "Under mawk, e2e-sync-cache fails one test identically on main" records a defect only in the PR body. It has no fix and no issue (gh issue list --search mawk is empty). This breaks dev-context-engineering: a defect found by a change is fixed in it or recorded before it ships, never left only in a PR body.

Suggestion

  • cli/intelligence:16-17 - the new fast path keeps a CLI_DIR that ends in /. bash /abs/cli//intelligence version fails on this head with .../cli/engine/VERSION: No such file or directory and works on main. No pattern matches /abs/cli/, so CLI_ROOT="${CLI_DIR%/*}" names cli itself. PATH lookup and the npm launcher never produce the doubled slash. Adding */ to the pattern restores main's behaviour.
  • cli/lib/manifest.sh:335, cli/lib/lockfile.sh:98 - the writers clear only the qmap memo, not the manifest view (IS_YL_*, IS_TGT_*, IS_YF_*). No in-process writer runs after the preload today. Calling project_reads_reset from the writers would keep it that way without relying on each caller.
  • cli/lib/sync-cache.sh:562-565 - a check from another environment renders and overwrites the record. A save hook and a terminal with different PATHs therefore invalidate each other's record on every switch, and neither gets the fast path. This is worth a sentence in docs/cli.md, or a record kept per tooling fingerprint.
  • cli/lib/manifest.sh:246, cli/lib/lockfile.sh:15 - qmap_preload and lock_validate are now called only from cli/tests/unit-manifest.sh. _qmap_read_var (cli/lib/manifest.sh:189-199) repeats the replay rules of _qmap_memo_replay (:232).
  • cli/lib/cli-common.sh:577 - _yaml_list_var reads the engine's IS_YL_<section>_FILE/_VAL cache by its key format, so that format is now defined in two files.

Claims axis

  • Every other behaviour the body claims has code in the diff that delivers it: the exit-code mapping, the answers from the record, the render-and-restore path, the single fingerprint pipeline, the one-pass manifest and lock reads, the batched contracts and the sourced sync. Decision 0018 is new, and 0002 and 0010 gain append-only amendments.
  • The merge of Ignored generated Copilot output by default and added targets.copilot.commit_output to keep it tracked #50 is semantically clean. The batched contract reader passes no manifest, which is the ownership-only call that Ignored generated Copilot output by default and added targets.copilot.commit_output to keep it tracked #50's adapter-contract.sh header prescribes, and _adapter_contract_emit accepts unignore.
  • v0.18.1 is published and v0.19.0 is not, so both lines go under the pending ## [0.19.0], with no [Unreleased] section.
  • The diff contains no migration, environment or secret change. "Notes for reviewers" names the cache-format move.
  • Not re-raised, by owner decision: the docs/cli.md status-line sentence and the Verification steps.
  • Gates: CI is green at this head. bash cli/tests/verify.sh all on a copy of this head in WSL printed verify ok, with 134 checks in e2e-sync-check.

@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Owner decision - critical review finding postponed

The critical finding from the review at 2b80adb is postponed to #60 by the owner, and #54 ships without that fix: the sync --check render path compares through symlinks. #60 also records the review's warnings and suggestions.

@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Verification - PASS

head: 0aece34 - steps: sha1:52f816a43d90 (Verification section: checklist + Steps 1-10, unchanged since the 0bcc635 and 2b80adb reports; docs/qa-checks.md absent at head, no standing checks matched) - env: local (fresh clones of the PR head and of main b1113b0 from GitHub into a scratch dir; no preview or app_url exists for this CLI project)

Setup per the PR's shared start state (Windows 11, Git Bash 5.3.9, CI unset): cloned feature/fast-sync-check as cli-src and main as cli-main. cli-src HEAD was 0aece34 before and after the run, and the PR head did not move. Built proj with git -C cli-src archive HEAD | tar -x -C proj, git init, add, commit base, then $CLI sync: the package store was restored (= @ainova-systems/core@v0.9.3, = @ainova-systems/sync@v0.20.0), exit 0, git status --porcelain empty. Every "reset" below ran git checkout -- . && git clean -fdq && $CLI sync, and each one exited 0 with an empty status.

# Step Source Verdict Observed
C1 bash cli/tests/verify.sh reports verify ok and skipped nothing this change needed (all, through WSL, on this head) PR checklist pass In cli-src at 0aece34, bash cli/tests/verify.sh all printed === WSL: 'all' on a copy of this tree ... ===, ok: lint: engine and ok: lint: cli. It ran all 19 suites (sync-check: 161 checks passed, sync-cache: 254 checks passed, concurrency: 21, sync-lock: 46, store-sha: 34, unit-verify: all checks passed, ...), then printed ok: tests: CLI suites and === verify ok ===. Exit 0, real 2m59.331s. No gate or suite was skipped. The in-suite notes come from running under WSL: SKIP Windows junction case: non-Windows host (e2e-sync-cache), NOTE: node is not on PATH — launcher verification not exercised (unit-upgrade) and source checkout/registry check skipped; engine 0.20.0 (e2e-packages).
C2 intelligence status --check succeeds in the relevant fixture/project (this repository's root project, on this head) PR checklist pass In cli-src, a fresh clone with no store, $CLI sync restored the store from the lock with exit 0 and no tracked diff. Then $CLI status --check printed ✓ on every line: schema_version 0.20.0 matches the engine; sync v0.20.0 and core v0.9.3; adapters agents, codex, claude and antigravity at contract v1. It ended with all good., exit 0.
C3 A second intelligence sync produces no unexpected diff (sync then sync --check exits 0 and git status stays empty) PR checklist pass In cli-src, sync printed === Unchanged: no files needed updating ===, exit 0, with git status --porcelain empty. sync --check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0, and the status stayed empty.
C4 Adapter changes preserve hand-authored sibling files; rendered output byte-identical to main (step 10) PR checklist pass Judged on step 10's evidence, as the item says. main's sync --force over the branch-synced tree left nothing to commit, and the branch's sync --force after it left git status --porcelain empty.
C5 User-facing docs and examples use the current public command groups PR checklist pass Grepped README.md, CONTRIBUTING.md, docs/ and examples/ for intelligence <word>. The only invocations are package (17), init (14), sync (13), update (10), adapter (10), upgrade (7), status (7), registry (6) and source (4), plus intelligence help (README.md:100), the dispatcher's usage alias (cli/intelligence:48). The remaining matches are prose ("intelligence from/in/at/layer/pulled"). docs/cli.md:89 documents intelligence sync [adapter] [--compact] [--force] [--check].
1 Up to date and fast: sync --check prints one up-to-date line and 0; git status --porcelain empty; five-run median real <= 1.0 s PR step pass IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0. The status was empty before and after the timings. Five runs: real 0.554s, 0.573s, 0.623s, 0.636s, 0.752s, median 0.623s.
2 Same-size source edit with the old mtime restored: exits 2 and says sources changed; five-run median <= 1.0 s; status lists only intelligence/rules/product.md PR step pass The heading line became # Intelligencf product repository. Size was 3079 and mtime 1791389992 both before and after. Output: IS_STATUS=out-of-date IS_DETAIL=sources changed since the last sync; run 'intelligence sync', exit 2. Five runs: real 0.577s, 0.680s, 0.558s, 0.650s, 0.552s, median 0.577s. Status showed only M intelligence/rules/product.md.
3 Hand-edited generated file: exits 2 and says generated files changed; edit survives; status lists only AGENTS.md; then sync empties status and sync --check exits 0 PR step pass After reset: IS_STATUS=out-of-date IS_DETAIL=generated files changed since the last sync; run 'intelligence sync', exit 2. tail -1 AGENTS.md was <!-- hand edit -->, and status showed only M AGENTS.md. Then sync exited 0 with an empty status, and sync --check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0.
4 No cache, nothing changed: sync --check exits 0 after rendering; status empty; a second check exits 0 with five-run median <= 1.0 s PR step pass After reset and removing .intelligence/sync-cache (confirmed absent): IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0. That render took real 10.571s, which the step does not time. Status empty. The second check printed IS_STATUS=ok IS_DETAIL=generated files are up to date, exit 0. Five runs: real 0.874s, 1.368s, 0.803s, 0.780s, 0.718s, median 0.803s. Status empty.
5 No cache, source changed: sync --check exits 2; status lists only intelligence/rules/product.md PR step pass IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files; run 'intelligence sync', exit 2. Status showed only M intelligence/rules/product.md.
6 Missing store: sync --check restores the package store from intelligence.lock and exits 0; status empty PR step pass After reset and removing .intelligence (confirmed absent), the output was restoring package store from intelligence.lock, = @ainova-systems/core@v0.9.3, WARNING: bundled sync content restored without commit verification (development bundle or lock has no SHA) and = @ainova-systems/sync@v0.20.0. It ended with IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0. .intelligence/packages/@ainova-systems holds core and sync. Status empty.
7 Alignment pending: sync --check exits 2 and names intelligence init; git diff --stat shows only intelligence.yaml with one line changed; CI=true behaves the same PR step pass IS_STATUS=out-of-date IS_DETAIL=the project needs alignment (stamp 0.17.3, engine 0.20.0); run 'intelligence init --apply' locally, review and commit the diff, exit 2. git diff --stat showed only intelligence.yaml | 2 +-, 1 file changed, 1 insertion(+), 1 deletion(-), changing schema_version: "0.20.0" to "0.17.3". CI=true $CLI sync --check printed the identical line, exit 2, with the same diff.
8 Errors are distinct: a bad lock exits 1 with the lock error; an empty temporary directory exits 1 PR step pass After reset, with lockfile_version: 99 on line 2 of intelligence.lock: .../proj/intelligence.lock has unsupported or missing lockfile_version '99' — expected 1 and ERROR: invalid intelligence.lock — recovery: https://github.com/ainova-systems/intelligence/blob/main/docs/cli.md#recovering-a-lock, exit 1. In a new empty scratch directory (0 entries): ERROR: no intelligence project found here - run 'intelligence init', exit 1.
9 Strict mode: sync --check --force on a reset project renders and exits 0 with status empty; with the criterion-2 edit it exits 2 PR step pass After reset: IS_STATUS=ok IS_DETAIL=generated files are up to date (rendered to compare), exit 0, status empty. With the same-size edit, mtime restored (1791390281 before and after): IS_STATUS=out-of-date IS_DETAIL=rendering would change generated files; run 'intelligence sync', exit 2.
10 Ordinary sync unchanged and faster: main render, git add -A, git commit -qm main-render, then $CLI sync --force leaves status empty; two copies, five alternating cache-hit sync runs, branch median below main PR step pass After reset, bash ../cli-main/cli/intelligence sync --force (main b1113b0, engine 0.19.0) printed WARNING: project schema 0.20.0 is newer than this CLI's engine 0.19.0 .... It ended with IS_STATUS=ok IS_DETAIL=synced=4 and === Sync complete ===, exit 0. git add -A exited 0. git commit -qm main-render exited 1 with nothing to commit, working tree clean, because main's render already equals the committed files; the owner accepted this as part of the step. $CLI sync --force exited 0, and git status --porcelain was empty. Next, two cp -a copies of the reset project were each primed by their own CLI (both exit 0), followed by five alternating cache-hit sync runs. Every log shows Unchanged: generated files are up to date. main: 4.871s, 3.830s, 5.278s, 4.847s, 4.662s, median 4.847s. branch: 0.705s, 0.788s, 0.856s, 1.001s, 0.887s, median 0.856s. The branch median is lower. Both copies were left with an empty status.

Failures

None.

The caller scoped this run to the declared steps, so it did not run the skill's step-5 negative probes.

@dzykovic dzykovic added the ai:verified Declared behavior verified on the reported head label Oct 7, 2026
@dzykovic

dzykovic commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Code review - PASS-WITH-WARNINGS

head: 0aece34 - base: main - files 51, +2240/-336

Critical

None.

Warning

  • cli/commands/sync.sh:116 - the check's store restore can rewrite the tracked intelligence.yaml. restore.sh calls wire_package_sources for every package it restores (cli/internal/restore.sh:120), and that re-adds any directory the package provides but the manifest omits. Reproduced on this head:

    • commit a project with .intelligence/packages/@ainova-systems/sync/agents removed from sources.agents;
    • rm -rf .intelligence, then sync --check;
    • the check exits 2, and git status shows M intelligence.yaml with the agents entry re-added.

    This contradicts the body's Public CLI check line, "the check writes no tracked or generated file; it may restore the ignored package store and refresh the ignored cache record". It also contradicts decision 0018 item 2 (decisions/0018-check-whether-sync-is-needed.md:36-40). The verdict itself matches what sync would do; the leftover tracked edit is the defect.

  • cli/lib/sync-cache.sh:133 - a source written with a trailing slash (- "intelligence/rules/") becomes a start point ending in /. find prints its children as <start>base.md, which the listing stage cannot place (:207), so every fingerprint fails and the cache is off for that project. Reproduced: on main the second sync prints "Unchanged"; on this head every sync renders, and every sync --check takes the render path. The output is correct but slow. Stripping trailing slashes where the start points are built restores main's behaviour; the nesting test at :266 has the same blind spot.

  • cli/lib/sync-cache.sh:546, :555 - on success the check discards every engine diagnostic. The render log reaches stderr only when the status is non-zero, and the record path replays nothing. Reproduced with - "zz:rules" (an alias no package declares):

    docs/cli.md:213-214 says "restore progress and every diagnostic go to stderr". The record already keeps the WARNING: lines (sync_cache_report), so the check could replay them. Otherwise the sentence should be narrowed.

  • PR body, "Summary" and "Automated coverage" - the merge of Let sources name a package's directories by full name or alias #48 put package references into this change, and the body never mentions them:

    • _IS_AWK_LISTS now carries the reference parser, with ! rows feeding IS_YL_UNRESOLVED (engine/lib/common.sh:2223-2228, :2344);
    • decision 0018 item 3 now names references (decisions/0018-check-whether-sync-is-needed.md:46-48);
    • e2e-sync-check gained the full-name and alias fingerprint cases (cli/tests/e2e-sync-check.sh:416-467);
    • unit-engine gained the combined-pass reference case (cli/tests/unit-engine.sh:377-396).

    This is undeclared scope; a reviewer can learn it only from the merge commit.

Suggestion

  • engine/sync.sh:245-250 - restore_sync_snapshot removes an owned path that was absent before the render, but not the parent directories the render created for it. Reproduced: with .claude/ absent, sync --check --force exits 2 and leaves an empty .claude/ behind. Git does not see it, but the check is meant to leave the tree as found.
  • engine/lib/common.sh:2108-2110 - the comment says the list cache "is only ever populated by load_yaml_list ... so a CLI process that edits the manifest keeps reading the file directly". load_manifest_view now fills that cache in the CLI process. This belongs with the manifest-view item already in sync --check follows symlinks when comparing rendered output with its snapshot #60.
  • cli/lib/sync-cache.sh:316-328 - the three find | [sort] | awk | [cygpath] | git hash-object pipelines differ only in the optional sort and cygpath stages and in SC_AWK_STREAM. A change to the find expression or the awk environment has to be made three times.
  • engine/lib/adapter-contract.sh:141-160 - adapter_contract_records_batch repeats adapter_contract_records' source, declare -F and missing-contract refusal (:124-134).

Merge of #48 (b1113b0)

  • Package-named sources are fingerprinted at their store path:
    • sync_cache_dependencies reads every source through load_yaml_list (cli/lib/sync-cache.sh:127-134), which hands over the expanded store path and leaves out references that resolve to nothing;
    • the new e2e-sync-check cases prove a store edit behind @ainova-systems/sync/rules and behind sync:rules reads as "sources changed" without rendering.
  • The preload still equals the single readers:
    • _IS_AWK_LISTS is byte-identical to main's load_yaml_lists program, and _yaml_lists_store rebuilds IS_YL_UNRESOLVED exactly as main did;
    • manifest_view_reset now clears it;
    • in the combined pass the list, field and targets programs share no global they write. The reference functions keep name, line, rest, i and n local, and the targets program still runs last;
    • a differential run of load_manifest_view against read_yaml_list, load_targets_cache, get_yaml_field and load_yaml_lists agreed on manifests with packages: after sources:, CRLF, a repeated targets: block, and unknown, ambiguous and invalid references.
  • project_store_missing checks every spelling as its store directory through _yaml_list_var, as main does through read_yaml_list.
  • sources_remove_package now clears the qmap memo like _qmap_stage.
  • Outside these files the PR's diff is unchanged by the merge, except the version move.

Claims axis

  • Every other behaviour the body claims has code in the diff.
  • v0.19.0 is published and v0.20.0 is not:
    • engine/VERSION, every example's schema_version, the exact @ainova-systems/sync pins and their lock rows, and the root project all moved to 0.20.0 together;
    • CHANGELOG.md adds only ## [0.20.0], and everything from ## [0.19.0] down is identical to main.
  • No migration, environment or secret change. "Notes for reviewers" names the cache-format move.
  • Not re-raised, by owner decision:

Gates

  • CI is green at this head.
  • bash cli/tests/verify.sh all, run on a fresh clone of this head in WSL, printed verify ok; e2e-sync-check passed 161 checks.
  • Natively under Git Bash, e2e-sync-check passed 140 checks. The link and executable-bit cases skip on this host.

@dzykovic dzykovic added ai:reviewed Diff reviewed on the reported head ai:completed Agent work completed; required factors are fresh and removed ai:processing An agent is working on this pull request labels Oct 7, 2026
@dzykovic
dzykovic merged commit f9a44ec into main Oct 7, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai:completed Agent work completed; required factors are fresh ai:reviewed Diff reviewed on the reported head ai:verified Declared behavior verified on the reported head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants