Add "Sync" command#1205
Open
isc-dchui wants to merge 27 commits into
Open
Conversation
…flesh out verbose mode output
isc-dchui
requested review from
isc-eneil,
isc-jili,
isc-jlechtne and
isc-kiyer
as code owners
July 22, 2026 19:16
isc-dchui
requested review from
isc-cborbonm,
isc-egabhart,
isc-pbarton and
isc-tleavitt
as code owners
July 22, 2026 19:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Motivation
The primary use case is LLM-assisted development: an LLM generates or edits
.cls/.inc/.macfiles on disk, then the developer runssyncto reload and recompile only what changed without a fullreloadthat reinstalls the entire module. This makes the tight edit-compile-test loop fast enough to be practical inside an agentic coding session.Secondary use case: any developer editing IPM-managed source who wants faster incremental feedback than
reloadprovides.Architecture
Entry point
Syncis a first-class lifecycle phase registered in%IPM.DataType.PhaseName. It fires the standardOnBeforePhase/OnAfterPhase/<Invoke>hooks for free. With no module name,Main.Syncloops over allDeveloperMode=1modules and collects failures without throwing.Flags:
-delete(remove deleted files from server),-test/-t(run changed test cases),-verbose/-v.10-step pipeline (
%IPM.Lifecycle.Base.%Sync)SyncCheckModuleXmlmodule.xml; if changed, reload manifest immediatelyGetSyncDirectory()on eachSupportsSync()=1processor;DeduplicateScanDirs;WalkAndHashDirstargeted walk; buildbfsFilessubsetSyncBuildReverseIndexrelPath → processorvia (a)ResolveChildren+OnItemRelativePathfor individually-declared files, (b)GetSyncDirectory()prefix scan for directory-owned resourcesFileHash.ComputeChangesSyncRoutePathSetsyncByResourceSyncDispatchProcessorsOnSyncon each routed processorSyncCompileSyncApplyDeletes-delete: delete server-side docs, then recompileFileHash.CommitChangesSyncRunTests-test: run changed test-phase cases via owningTestprocessor onlyTargeted-walk architecture (
GetSyncDirectory)Each processor returns the single directory it owns as a normalized relative path (no leading/trailing slash). Sync walks only those directories, never the full module root. This avoids touching
build/,data/,node_modules/, etc. entirely.DeduplicateScanDirsremoves any entry that is a subdirectory of an already-kept entry (alphabetical scan, prefix check) to prevent double-walking.Abstract.GetSyncDirectory()returns""(sentinel: no owned directory). All callers guardsyncDir '= "".Change detection (
FileHash.ComputeChanges)Three-pass pipeline — SHA-1 hash only, no mtime or size:
cls/inc/mac/int). Checks$$$comClassDefined/%RoutineMgr.Exists. Only files compiled in this namespace are considered.FileHashrow whose file is missing from disk → deleted.No baseline row → new file → modified. Baseline exists → hash-only comparison.
Filesystem walking
WalkAndHashDirsdispatches to Pythonos.walk()(WalkAndHashFilesPython) with automatic SQL BFS fallback (WalkAndHashFilesSQL). Walk-once: step 2 walks all declared dirs intoallFiles+allHashes, shared by bothSyncBuildReverseIndexandComputeChanges.Stage rollout
AbstractCompilablederivatives,Test(test-phase only)FileCopy,WebApplication,PythonWheelCPF,Copy,ArtifactoryTarball,LegacyLocalizedMessages,Default.GlobalCSPApplication,SystemSetting,ModuleExport,LocalizationExportPeculiar implementation details
Trailing-slash contract on
GetSyncDirectory():NormalizePathstrips leading slashes but not trailing. Callers must explicitly strip the trailing slash before returning. If missed,SyncBuildReverseIndexappends"/"to form a prefix, producing"tests/unit//"which never matches anyrelPath, silently finding nothing.%-prefixed classes:RelPathToDocName("src/cls/IPM/Main.cls")→"IPM.Main.CLS". Actual class is%IPM.Main.ComputeChangesPass 1 tries$$$comClassDefined("%" _ className)as a fallback.Test file relPaths:
RelPathToDocName("tests/unit/SyncTest/Tests/Trivial.cls")→"tests.unit.SyncTest.Tests.Trivial.CLS"is non-empty but wrong.$$$comClassDefinedreturns false. Test files enterreverseIndexvia the prefix scan path (step 3b), not the docName/namespace-check path.StampModulestamps all compilable files in scan dirs unconditionally for the same reason.Test.OnSyncowns its own delete cycle:SyncApplyDeletesskipsTestresources (notAbstractCompilable).OnSyncmust call$system.OBJ.Deletefor removed test class files directly. Otherwise the server-side%UnitTest.TestCasesubclass persists with no on-disk source and no hash record, permanently invisible to future syncs.Baseline not committed on failure: if compile fails,
CommitChangesis skipped. The changed files retain their old hash rows (or no rows if new), so the next sync re-detects and retries them once the error is fixed.Migration from no baseline: a module installed before this feature existed has zero
FileHashrows. The first sync self-heals by stamping a baseline from current disk state, then exits. Only the next sync after a real edit detects changes.Performance: what was tried and why things changed
os.walk()because IRIS's%Library.File_FileSetSQL walk has severe per-row overhead on large directories. Python processes the entire tree in native C and returns results in bulk.zsearch/$zsearchGetSyncDirectory()architecture: avoid enteringbuild/,node_modules/,data/entirely rather than filtering them after the I/O.c37ac2d) to avoid double-traversal overhead.080e6bed): too complex, too slow. Only canonical resource paths (declaredName/Directoryattributes) are supported.Testing
All integration tests in
Test.PM.Integration.Sync. Fixture:tests/integration_tests/Test/PM/Integration/_data/sync-test/is loaded in dev mode before each test, uninstalled and deleted after.TestGetStoredPathsReturnsStampedPathsStampModulerecords expected paths inFileHashafterloadTestNoChangeIsNoOpTestMigrationFromNoBaselineTestModifiedClassRecompiles.clsfile detected and recompiledTestSuperclassEditRecompilesSubclassTestIncludeEditRecompilesConsumer.inccauses consumer class to recompileTestUntrackedFileIgnoredTestDeleteSkippedByDefault-deleteflagTestDeleteTestClassRemovesFromServer-deleteTestDeleteRecompilesDependentsTestModuleXmlChangedWarningmodule.xmlreloads manifest and emits warningTestSyncTestFlag-testruns changed test class; without flag, loads but doesn't runTestSyncTestFlagBatchesMultipleChangedClassesTestSyncTestFlagOnlyRunsOwningResourceUnitTestprocessorsTestSyncAllDevModeModules[sync-test]specifically reports nothing to syncTestSyncModuleNotFoundTestSyncNonDevModeModuleTestModuleXmlAddsResourcePicksUpNewFilemodule.xmlchange adding a new<Resource>causes that resource's files to be tracked in the same sync callTestFailedCompileRetriesTestModuleXmlAndClassEditedTogethermodule.xmland a source class in one operation syncs both changesChecklist
mainbranch rebased or merged.zpm test -only) and integration tests (zpm verify -only) pass.