instar 0.28.74 → 0.28.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/discovery.d.ts.map +1 -1
- package/dist/commands/discovery.js +1 -0
- package/dist/commands/discovery.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +2 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/job.d.ts.map +1 -1
- package/dist/commands/job.js +1 -0
- package/dist/commands/job.js.map +1 -1
- package/dist/commands/ledgerCleanup.d.ts.map +1 -1
- package/dist/commands/ledgerCleanup.js +1 -0
- package/dist/commands/ledgerCleanup.js.map +1 -1
- package/dist/commands/listener.d.ts.map +1 -1
- package/dist/commands/listener.js +6 -0
- package/dist/commands/listener.js.map +1 -1
- package/dist/commands/nuke.d.ts.map +1 -1
- package/dist/commands/nuke.js +6 -0
- package/dist/commands/nuke.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +2 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +6 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/slack-cli.d.ts.map +1 -1
- package/dist/commands/slack-cli.js +4 -0
- package/dist/commands/slack-cli.js.map +1 -1
- package/dist/commands/whatsapp.d.ts.map +1 -1
- package/dist/commands/whatsapp.js +1 -0
- package/dist/commands/whatsapp.js.map +1 -1
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +1 -0
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentConnector.d.ts.map +1 -1
- package/dist/core/AgentConnector.js +3 -0
- package/dist/core/AgentConnector.js.map +1 -1
- package/dist/core/AgentRegistry.d.ts.map +1 -1
- package/dist/core/AgentRegistry.js +2 -0
- package/dist/core/AgentRegistry.js.map +1 -1
- package/dist/core/AutoDispatcher.d.ts.map +1 -1
- package/dist/core/AutoDispatcher.js +1 -0
- package/dist/core/AutoDispatcher.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +1 -0
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/AutonomousEvolution.d.ts.map +1 -1
- package/dist/core/AutonomousEvolution.js +1 -0
- package/dist/core/AutonomousEvolution.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +1 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/BranchManager.d.ts.map +1 -1
- package/dist/core/BranchManager.js +3 -0
- package/dist/core/BranchManager.js.map +1 -1
- package/dist/core/CaffeinateManager.d.ts.map +1 -1
- package/dist/core/CaffeinateManager.js +1 -0
- package/dist/core/CaffeinateManager.js.map +1 -1
- package/dist/core/DeferredDispatchTracker.d.ts.map +1 -1
- package/dist/core/DeferredDispatchTracker.js +1 -0
- package/dist/core/DeferredDispatchTracker.js.map +1 -1
- package/dist/core/DispatchManager.d.ts.map +1 -1
- package/dist/core/DispatchManager.js +2 -0
- package/dist/core/DispatchManager.js.map +1 -1
- package/dist/core/EvolutionManager.d.ts.map +1 -1
- package/dist/core/EvolutionManager.js +1 -0
- package/dist/core/EvolutionManager.js.map +1 -1
- package/dist/core/ExecutionJournal.d.ts.map +1 -1
- package/dist/core/ExecutionJournal.js +1 -0
- package/dist/core/ExecutionJournal.js.map +1 -1
- package/dist/core/FeedbackManager.d.ts.map +1 -1
- package/dist/core/FeedbackManager.js +1 -0
- package/dist/core/FeedbackManager.js.map +1 -1
- package/dist/core/FileClassifier.d.ts.map +1 -1
- package/dist/core/FileClassifier.js +4 -0
- package/dist/core/FileClassifier.js.map +1 -1
- package/dist/core/ForegroundRestartWatcher.d.ts.map +1 -1
- package/dist/core/ForegroundRestartWatcher.js +2 -0
- package/dist/core/ForegroundRestartWatcher.js.map +1 -1
- package/dist/core/GitStateManager.d.ts.map +1 -1
- package/dist/core/GitStateManager.js +1 -0
- package/dist/core/GitStateManager.js.map +1 -1
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +5 -0
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/GlobalInstallCleanup.d.ts.map +1 -1
- package/dist/core/GlobalInstallCleanup.js +2 -0
- package/dist/core/GlobalInstallCleanup.js.map +1 -1
- package/dist/core/GlobalSecretStore.d.ts.map +1 -1
- package/dist/core/GlobalSecretStore.js +2 -0
- package/dist/core/GlobalSecretStore.js.map +1 -1
- package/dist/core/HandoffManager.d.ts.map +1 -1
- package/dist/core/HandoffManager.js +4 -0
- package/dist/core/HandoffManager.js.map +1 -1
- package/dist/core/LedgerSessionRegistry.d.ts.map +1 -1
- package/dist/core/LedgerSessionRegistry.js +1 -0
- package/dist/core/LedgerSessionRegistry.js.map +1 -1
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +1 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/ParallelDevWiring.d.ts.map +1 -1
- package/dist/core/ParallelDevWiring.js +1 -0
- package/dist/core/ParallelDevWiring.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +2 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ProjectMapper.d.ts.map +1 -1
- package/dist/core/ProjectMapper.js +2 -0
- package/dist/core/ProjectMapper.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +2 -0
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/core/SafeFsExecutor.d.ts +41 -0
- package/dist/core/SafeFsExecutor.d.ts.map +1 -0
- package/dist/core/SafeFsExecutor.js +146 -0
- package/dist/core/SafeFsExecutor.js.map +1 -0
- package/dist/core/SafeGitExecutor.d.ts +139 -0
- package/dist/core/SafeGitExecutor.d.ts.map +1 -0
- package/dist/core/SafeGitExecutor.js +631 -0
- package/dist/core/SafeGitExecutor.js.map +1 -0
- package/dist/core/ScopeVerifier.d.ts.map +1 -1
- package/dist/core/ScopeVerifier.js +1 -0
- package/dist/core/ScopeVerifier.js.map +1 -1
- package/dist/core/SecretStore.d.ts.map +1 -1
- package/dist/core/SecretStore.js +1 -0
- package/dist/core/SecretStore.js.map +1 -1
- package/dist/core/SharedStateLedger.d.ts.map +1 -1
- package/dist/core/SharedStateLedger.js +1 -0
- package/dist/core/SharedStateLedger.js.map +1 -1
- package/dist/core/SoulManager.d.ts.map +1 -1
- package/dist/core/SoulManager.js +2 -0
- package/dist/core/SoulManager.js.map +1 -1
- package/dist/core/SourceTreeGuard.d.ts +69 -0
- package/dist/core/SourceTreeGuard.d.ts.map +1 -0
- package/dist/core/SourceTreeGuard.js +378 -0
- package/dist/core/SourceTreeGuard.js.map +1 -0
- package/dist/core/StateManager.d.ts.map +1 -1
- package/dist/core/StateManager.js +49 -9
- package/dist/core/StateManager.js.map +1 -1
- package/dist/core/SyncOrchestrator.d.ts.map +1 -1
- package/dist/core/SyncOrchestrator.js +3 -0
- package/dist/core/SyncOrchestrator.js.map +1 -1
- package/dist/core/UpdateChecker.d.ts.map +1 -1
- package/dist/core/UpdateChecker.js +2 -0
- package/dist/core/UpdateChecker.js.map +1 -1
- package/dist/core/UpgradeGuideProcessor.d.ts.map +1 -1
- package/dist/core/UpgradeGuideProcessor.js +2 -0
- package/dist/core/UpgradeGuideProcessor.js.map +1 -1
- package/dist/core/WorktreeManager.d.ts.map +1 -1
- package/dist/core/WorktreeManager.js +7 -0
- package/dist/core/WorktreeManager.js.map +1 -1
- package/dist/knowledge/KnowledgeManager.d.ts.map +1 -1
- package/dist/knowledge/KnowledgeManager.js +1 -0
- package/dist/knowledge/KnowledgeManager.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +14 -0
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +1 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/lifeline/droppedMessages.d.ts.map +1 -1
- package/dist/lifeline/droppedMessages.js +1 -0
- package/dist/lifeline/droppedMessages.js.map +1 -1
- package/dist/memory/EpisodicMemory.d.ts.map +1 -1
- package/dist/memory/EpisodicMemory.js +1 -0
- package/dist/memory/EpisodicMemory.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts.map +1 -1
- package/dist/memory/TopicMemory.js +4 -0
- package/dist/memory/TopicMemory.js.map +1 -1
- package/dist/messaging/AgentTokenManager.d.ts.map +1 -1
- package/dist/messaging/AgentTokenManager.js +1 -0
- package/dist/messaging/AgentTokenManager.js.map +1 -1
- package/dist/messaging/DropPickup.js +1 -0
- package/dist/messaging/DropPickup.js.map +1 -1
- package/dist/messaging/GitSyncTransport.d.ts.map +1 -1
- package/dist/messaging/GitSyncTransport.js +3 -0
- package/dist/messaging/GitSyncTransport.js.map +1 -1
- package/dist/messaging/MessageStore.d.ts.map +1 -1
- package/dist/messaging/MessageStore.js +2 -0
- package/dist/messaging/MessageStore.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +4 -0
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/backends/BaileysBackend.d.ts.map +1 -1
- package/dist/messaging/backends/BaileysBackend.js +2 -0
- package/dist/messaging/backends/BaileysBackend.js.map +1 -1
- package/dist/messaging/shared/EncryptedAuthStore.d.ts.map +1 -1
- package/dist/messaging/shared/EncryptedAuthStore.js +2 -0
- package/dist/messaging/shared/EncryptedAuthStore.js.map +1 -1
- package/dist/messaging/shared/MessageLogger.d.ts.map +1 -1
- package/dist/messaging/shared/MessageLogger.js +1 -0
- package/dist/messaging/shared/MessageLogger.js.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.d.ts.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.js +1 -0
- package/dist/messaging/shared/PrivacyConsent.js.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.d.ts.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.js +1 -0
- package/dist/messaging/shared/SessionChannelRegistry.js.map +1 -1
- package/dist/moltbridge/ProfileCompiler.d.ts.map +1 -1
- package/dist/moltbridge/ProfileCompiler.js +3 -0
- package/dist/moltbridge/ProfileCompiler.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +1 -0
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
- package/dist/monitoring/CredentialProvider.js +1 -0
- package/dist/monitoring/CredentialProvider.js.map +1 -1
- package/dist/monitoring/HealthChecker.d.ts.map +1 -1
- package/dist/monitoring/HealthChecker.js +1 -0
- package/dist/monitoring/HealthChecker.js.map +1 -1
- package/dist/monitoring/HookEventReceiver.d.ts.map +1 -1
- package/dist/monitoring/HookEventReceiver.js +1 -0
- package/dist/monitoring/HookEventReceiver.js.map +1 -1
- package/dist/monitoring/InstructionsVerifier.d.ts.map +1 -1
- package/dist/monitoring/InstructionsVerifier.js +1 -0
- package/dist/monitoring/InstructionsVerifier.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +4 -0
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/QuotaTracker.d.ts.map +1 -1
- package/dist/monitoring/QuotaTracker.js +1 -0
- package/dist/monitoring/QuotaTracker.js.map +1 -1
- package/dist/monitoring/SessionMigrator.d.ts.map +1 -1
- package/dist/monitoring/SessionMigrator.js +1 -0
- package/dist/monitoring/SessionMigrator.js.map +1 -1
- package/dist/monitoring/SessionRecovery.d.ts.map +1 -1
- package/dist/monitoring/SessionRecovery.js +1 -0
- package/dist/monitoring/SessionRecovery.js.map +1 -1
- package/dist/monitoring/TelemetryAuth.d.ts.map +1 -1
- package/dist/monitoring/TelemetryAuth.js +2 -0
- package/dist/monitoring/TelemetryAuth.js.map +1 -1
- package/dist/monitoring/TriageOrchestrator.d.ts.map +1 -1
- package/dist/monitoring/TriageOrchestrator.js +2 -0
- package/dist/monitoring/TriageOrchestrator.js.map +1 -1
- package/dist/monitoring/WorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/WorktreeReaper.js +3 -0
- package/dist/monitoring/WorktreeReaper.js.map +1 -1
- package/dist/monitoring/probes/PlatformProbe.d.ts.map +1 -1
- package/dist/monitoring/probes/PlatformProbe.js +2 -0
- package/dist/monitoring/probes/PlatformProbe.js.map +1 -1
- package/dist/paste/PasteManager.d.ts.map +1 -1
- package/dist/paste/PasteManager.js +4 -0
- package/dist/paste/PasteManager.js.map +1 -1
- package/dist/publishing/PrivateViewer.d.ts.map +1 -1
- package/dist/publishing/PrivateViewer.js +1 -0
- package/dist/publishing/PrivateViewer.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +1 -0
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +21 -9
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AgentDiscovery.d.ts.map +1 -1
- package/dist/threadline/AgentDiscovery.js +1 -0
- package/dist/threadline/AgentDiscovery.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +1 -0
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/CircuitBreaker.d.ts.map +1 -1
- package/dist/threadline/CircuitBreaker.js +1 -0
- package/dist/threadline/CircuitBreaker.js.map +1 -1
- package/dist/threadline/ComputeMeter.d.ts.map +1 -1
- package/dist/threadline/ComputeMeter.js +1 -0
- package/dist/threadline/ComputeMeter.js.map +1 -1
- package/dist/threadline/ContextThreadMap.d.ts.map +1 -1
- package/dist/threadline/ContextThreadMap.js +1 -0
- package/dist/threadline/ContextThreadMap.js.map +1 -1
- package/dist/threadline/InvitationManager.d.ts.map +1 -1
- package/dist/threadline/InvitationManager.js +1 -0
- package/dist/threadline/InvitationManager.js.map +1 -1
- package/dist/threadline/MCPAuth.d.ts.map +1 -1
- package/dist/threadline/MCPAuth.js +1 -0
- package/dist/threadline/MCPAuth.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +2 -0
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/dist/threadline/RateLimiter.d.ts.map +1 -1
- package/dist/threadline/RateLimiter.js +1 -0
- package/dist/threadline/RateLimiter.js.map +1 -1
- package/dist/threadline/SessionLifecycle.d.ts.map +1 -1
- package/dist/threadline/SessionLifecycle.js +1 -0
- package/dist/threadline/SessionLifecycle.js.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.js +1 -0
- package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
- package/dist/threadline/WakeSocketServer.d.ts.map +1 -1
- package/dist/threadline/WakeSocketServer.js +2 -0
- package/dist/threadline/WakeSocketServer.js.map +1 -1
- package/dist/threadline/listener-daemon.d.ts.map +1 -1
- package/dist/threadline/listener-daemon.js +2 -0
- package/dist/threadline/listener-daemon.js.map +1 -1
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +1 -0
- package/dist/users/UserManager.js.map +1 -1
- package/dist/users/UserOnboarding.d.ts.map +1 -1
- package/dist/users/UserOnboarding.js +1 -0
- package/dist/users/UserOnboarding.js.map +1 -1
- package/dist/utils/jsonl-rotation.d.ts.map +1 -1
- package/dist/utils/jsonl-rotation.js +1 -0
- package/dist/utils/jsonl-rotation.js.map +1 -1
- package/package.json +4 -2
- package/scripts/add-migration-marker.js +121 -0
- package/scripts/analyze-release.js +6 -0
- package/scripts/check-contract-evidence.js +2 -0
- package/scripts/destructive-command-shim.js +1 -0
- package/scripts/fix-better-sqlite3.cjs +2 -0
- package/scripts/generate-builtin-manifest.cjs +1 -0
- package/scripts/instar-dev-precommit.js +2 -0
- package/scripts/lint-no-direct-destructive.js +597 -0
- package/scripts/migrate-incident-2026-04-17.mjs +1 -0
- package/scripts/pre-push-gate.js +24 -0
- package/scripts/test-bootstrap-relay.mjs +1 -0
- package/scripts/worktree-commit-msg-hook.js +4 -0
- package/scripts/worktree-precommit-gate.js +1 -0
- package/src/data/builtin-manifest.json +98 -98
- package/src/templates/scripts/git-sync-gate.sh +4 -0
- package/upgrades/0.28.75.md +29 -0
- package/upgrades/0.28.76.md +67 -0
- package/upgrades/side-effects/0.28.75.md +53 -0
- package/upgrades/side-effects/comprehensive-destructive-tool-containment-foundation.md +74 -0
- package/upgrades/side-effects/source-tree-guard.md +340 -0
- package/upgrades/side-effects/telegram-lifeline-version-missing-info.md +76 -0
|
@@ -29,6 +29,7 @@ fi
|
|
|
29
29
|
LOCAL_CHANGES=$(git status --porcelain 2>/dev/null | head -1)
|
|
30
30
|
|
|
31
31
|
# Fetch remote (silent, with timeout)
|
|
32
|
+
// safe-git-allow: incremental-migration
|
|
32
33
|
git fetch origin --quiet 2>/dev/null &
|
|
33
34
|
FETCH_PID=$!
|
|
34
35
|
sleep 5 && kill "$FETCH_PID" 2>/dev/null &
|
|
@@ -55,8 +56,11 @@ fi
|
|
|
55
56
|
# If both sides have changes, check for potential conflicts
|
|
56
57
|
if [ -n "$LOCAL_CHANGES" ] && [ "${BEHIND:-0}" -gt 0 ]; then
|
|
57
58
|
# Stash local changes temporarily and try a dry-run merge
|
|
59
|
+
// safe-git-allow: incremental-migration
|
|
58
60
|
git stash --quiet 2>/dev/null
|
|
61
|
+
// safe-git-allow: incremental-migration
|
|
59
62
|
MERGE_OUTPUT=$(git merge-tree "$(git merge-base HEAD "$TRACKING_BRANCH")" HEAD "$TRACKING_BRANCH" 2>/dev/null)
|
|
63
|
+
// safe-git-allow: incremental-migration
|
|
60
64
|
git stash pop --quiet 2>/dev/null
|
|
61
65
|
|
|
62
66
|
if echo "$MERGE_OUTPUT" | grep -q "<<<<<<"; then
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.75
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
`StateManager` now discriminates permission errors (`EPERM`/`EACCES`) from JSON corruption when reading state files. Previously, any failure in `getSession`, `listSessions`, `getJobState`, or `get` was reported as "Corrupted ... file" — including `EPERM` from launchd-spawned processes lacking macOS Full Disk Access on `~/Documents`. A new internal helper `describeReadError(err, filePath)` classifies failures into `permission`, `parse`, or `io` and feeds the discriminated reason into the DegradationReporter and `console.warn` line.
|
|
8
|
+
|
|
9
|
+
The null-return contract is preserved exactly. `feature`, `primary`, `fallback`, and `impact` strings are unchanged. Only the `reason` content is improved. No API surface changes.
|
|
10
|
+
|
|
11
|
+
Closes feedback cluster `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi` (45 reports across `ai-guy` and `sagemind` agents) and prevents future reports from being mis-clustered as corruption.
|
|
12
|
+
|
|
13
|
+
## What to Tell Your User
|
|
14
|
+
|
|
15
|
+
- **Clearer state-file errors**: when I can't read a state file because of a permissions issue (common on macOS when I'm started by a system service that doesn't have Full Disk Access on your Documents folder), I'll now say so plainly instead of telling you the file is corrupted. That should make the fix obvious — granting Full Disk Access in System Settings — instead of sending you on a corruption hunt.
|
|
16
|
+
|
|
17
|
+
## Summary of New Capabilities
|
|
18
|
+
|
|
19
|
+
| Capability | How to Use |
|
|
20
|
+
|-----------|-----------|
|
|
21
|
+
| Discriminated state-file read errors | automatic — appears in degradation reports and warnings |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
Field repro: cluster `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi` shows 45 reports of `EPERM: operation not permitted, open '<path>'` mislabeled as "Corrupted job state file". Reporters: `ai-guy` and `sagemind` running instar 0.28.73 against `.instar/state/jobs/*.json` and `.instar/state/agent-attention-topic.json` files in `~/Documents/Projects/*`.
|
|
26
|
+
|
|
27
|
+
Post-change verification: `tests/unit/StateManager.test.ts` 26/26 passing, including a new `discriminates permission errors from corruption (EPERM/EACCES)` test that chmod 0o000s a file and asserts the warning emitted is keyed `permission`, not `parse` or `Corrupted`.
|
|
28
|
+
|
|
29
|
+
Build: clean.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Upgrade Guide — v0.28.76
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
`POST /internal/telegram-forward` no longer reports the backward-compat
|
|
8
|
+
`TelegramLifeline.versionMissing` event into the feedback pipeline as a
|
|
9
|
+
`[DEGRADATION]` signal. The condition (an upgraded but un-restarted lifeline
|
|
10
|
+
forwarding a Telegram message without the Stage-B `lifelineVersion` field) is
|
|
11
|
+
expected, accepted, and documented behaviour — emitting it as a critical
|
|
12
|
+
degradation was producing noisy regressions in cluster
|
|
13
|
+
`cmo7wswhj0000mgmdbw4j7dyd` every time any pre-Stage-B lifeline forwarded a
|
|
14
|
+
message.
|
|
15
|
+
|
|
16
|
+
The forward itself still succeeds via the documented backward-compat path;
|
|
17
|
+
the response, the inbound-message log, and the version-handshake logic for
|
|
18
|
+
lifelines that *do* send `lifelineVersion` are all unchanged. On the first
|
|
19
|
+
occurrence per server process, a one-shot `console.info` is emitted so the
|
|
20
|
+
operator-side observability signal is preserved without per-request log
|
|
21
|
+
spam.
|
|
22
|
+
|
|
23
|
+
The systemic work to give `DegradationReporter` a typed severity / category
|
|
24
|
+
field (so callers can mark events as ERROR vs COMPAT_SIGNAL at the reporter
|
|
25
|
+
layer rather than the call site) remains tracked under PROP-543 and is
|
|
26
|
+
deliberately out of scope for this release.
|
|
27
|
+
|
|
28
|
+
## What to Tell Your User
|
|
29
|
+
|
|
30
|
+
- **Quieter feedback signal**: I won't keep logging a critical-looking
|
|
31
|
+
message every time my lifeline forwards a Telegram note before I've had a
|
|
32
|
+
chance to restart it. The forward still works the same way; only the
|
|
33
|
+
noisy critical report goes away.
|
|
34
|
+
|
|
35
|
+
## Summary of New Capabilities
|
|
36
|
+
|
|
37
|
+
| Capability | How to Use |
|
|
38
|
+
|-----------|-----------|
|
|
39
|
+
| Quieter pre-Stage-B lifeline forwards | Automatic on upgrade |
|
|
40
|
+
|
|
41
|
+
## Evidence
|
|
42
|
+
|
|
43
|
+
Reproduction (before fix):
|
|
44
|
+
|
|
45
|
+
1. Run a lifeline at v0.28.67 or later, then upgrade the server-side package
|
|
46
|
+
without restarting the lifeline daemon.
|
|
47
|
+
2. Send a Telegram message that the lifeline forwards via
|
|
48
|
+
`POST /internal/telegram-forward`.
|
|
49
|
+
3. The handler accepts the forward and emits a
|
|
50
|
+
`[DEGRADATION] TelegramLifeline.versionMissing: …` feedback event,
|
|
51
|
+
classified as critical by the Portal cluster intake.
|
|
52
|
+
|
|
53
|
+
After fix:
|
|
54
|
+
|
|
55
|
+
1. Same setup; same forward; same successful response.
|
|
56
|
+
2. On the first forward per server process, a single
|
|
57
|
+
`[telegram-forward] Accepted pre-Stage-B lifeline forward …`
|
|
58
|
+
`console.info` is emitted; subsequent forwards on the same process emit
|
|
59
|
+
nothing.
|
|
60
|
+
3. No `TelegramLifeline.versionMissing` feedback events are submitted, so
|
|
61
|
+
cluster `cmo7wswhj0000mgmdbw4j7dyd` does not auto-reopen on every
|
|
62
|
+
forward.
|
|
63
|
+
|
|
64
|
+
`grep -rn versionMissing src/ tests/ test/ __tests__/` shows the symbol
|
|
65
|
+
appears only in `routes.ts` (the one-shot guard and the comment explaining
|
|
66
|
+
it). No test changed because the call site has no existing test coverage of
|
|
67
|
+
the reporter side-effect.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Side Effects — fix(StateManager): discriminate EPERM/EACCES from JSON corruption
|
|
2
|
+
|
|
3
|
+
**Cluster**: `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi`
|
|
4
|
+
**Risk**: LOW (diagnostic-only)
|
|
5
|
+
**Spec**: `docs/specs/fix-statemanager-eperm-discrimination.md`
|
|
6
|
+
|
|
7
|
+
## What changes (summary)
|
|
8
|
+
|
|
9
|
+
`StateManager.ts` adds a `describeReadError(err, filePath)` helper that classifies read errors into `permission` (EPERM/EACCES), `parse` (SyntaxError), and `io` (other). All four read sites — `getSession`, `listSessions`, `getJobState`, `get` — use the helper to populate `console.warn` and the `DegradationReporter.report({reason})` field. The null-return contract, the `feature` field, and `primary`/`fallback`/`impact` strings are preserved.
|
|
10
|
+
|
|
11
|
+
## 1. What user-facing behavior changes?
|
|
12
|
+
|
|
13
|
+
The DegradationReporter `reason` text changes for state-file read failures. Permission errors now read "Permission denied reading <path> (EPERM). On macOS, launchd-spawned processes need Full Disk Access to read under ~/Documents." instead of "Corrupted job state file: EPERM: operation not permitted, open '<path>'". JSON parse errors now read "Corrupted state file <path> (JSON parse failed): <message>". I/O errors get a generic "Failed to read <path> (<code>): <message>".
|
|
14
|
+
|
|
15
|
+
No CLI surface, dashboard view, or API endpoint changes shape. Anyone consuming the DegradationReporter feed sees better-targeted reasons; anyone grouping by `feature`/`fallback` is unaffected.
|
|
16
|
+
|
|
17
|
+
## 2. What ledger / state / on-disk artifacts change?
|
|
18
|
+
|
|
19
|
+
None. The change is in the catch handler of read paths. No writes added, no schema changes, no new files. State files on disk are read identically.
|
|
20
|
+
|
|
21
|
+
## 3. What downstream agents / consumers rely on the previous behavior?
|
|
22
|
+
|
|
23
|
+
The Outside-Dawn feedback pipeline currently clusters reports by `feature` and (sometimes) substring-match on `reason`. The cluster `cluster-degradation-statemanager-getjobstate-corrupted-job-state-fi` was formed by substring "Corrupted job state file" in reasons. After this fix, EPERM reports will land in a NEW cluster keyed off "Permission denied reading" — that's the intended outcome (the reporter pipeline auto-clusters new substrings into new clusters). The old cluster is being closed out as `fixed` in this same release.
|
|
24
|
+
|
|
25
|
+
No agent code consumes the `reason` text programmatically (verified: grep for `Corrupted job state file` and `Corrupted state file` across `src/` shows zero hits outside `StateManager.ts`).
|
|
26
|
+
|
|
27
|
+
## 4. What tests verify the change?
|
|
28
|
+
|
|
29
|
+
- `tests/unit/StateManager.test.ts` — 25 pre-existing tests pass unchanged. The "Corrupted File Handling" group still asserts `null` returns for genuine parse-error inputs.
|
|
30
|
+
- New test: `discriminates permission errors from corruption (EPERM/EACCES)`. Chmod 0o000s a file, captures `console.warn` calls, asserts that any warning emitted contains "permission" and none contain "parse"/"Corrupted". Skips when running as root (where chmod is bypassed) and tolerates sandboxed runners that no-op chmod.
|
|
31
|
+
|
|
32
|
+
## 5. What rollback path exists?
|
|
33
|
+
|
|
34
|
+
`git revert` of the StateManager.ts diff + patch release. The revert is safe at any time:
|
|
35
|
+
- No persistent state needs cleanup (the change writes nothing new).
|
|
36
|
+
- Agents updated to the fixed version that subsequently downgrade keep working — the same `feature` names and null-return contract apply.
|
|
37
|
+
- The new feedback cluster (keyed off "Permission denied reading") simply stops receiving new reports; existing reports remain in their cluster.
|
|
38
|
+
|
|
39
|
+
## 6. What's the blast radius if the change is wrong?
|
|
40
|
+
|
|
41
|
+
Bounded to log/feedback-feed cosmetics. The worst case is a confusing `reason` string in a degradation report; no functional path is changed because:
|
|
42
|
+
- The catch handler still returns null on every error path it previously returned null on.
|
|
43
|
+
- The `feature` and `fallback` semantics are unchanged, so the scheduler/loader logic that gates on degradation reports continues to work.
|
|
44
|
+
- The new helper has no I/O of its own — it's a pure string formatter over the caught error.
|
|
45
|
+
|
|
46
|
+
A bug in `describeReadError` (e.g., throwing inside the formatter) would be caught by the existing outer null-return contract test cases, which would fail and block the release.
|
|
47
|
+
|
|
48
|
+
## 7. What invariants must hold after this change?
|
|
49
|
+
|
|
50
|
+
- `getSession`, `listSessions`, `getJobState`, `get` still return `null` (or skip the file in `listSessions`) on ANY read failure. Verified by tests.
|
|
51
|
+
- `DegradationReporter.report({feature, primary, fallback, impact})` for these paths still passes the same constants — only `reason` content is discriminated. Verified by inspection.
|
|
52
|
+
- `console.warn` still emits one line per failure, prefixed `[StateManager] <method> <kind>:`. Operators tailing logs see strictly more information than before.
|
|
53
|
+
- No new module-load side effects: the helper is a plain function in the same module, not a new import or class.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Side-Effects Review — Comprehensive Destructive-Tool Containment (PR 1/2 — Foundation)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `comprehensive-destructive-tool-containment-foundation`
|
|
4
|
+
**Date:** `2026-04-26`
|
|
5
|
+
**Author:** Echo
|
|
6
|
+
**Spec:** `docs/specs/COMPREHENSIVE-DESTRUCTIVE-TOOL-CONTAINMENT-SPEC.md`
|
|
7
|
+
**Convergence report:** `docs/specs/reports/comprehensive-destructive-tool-containment-convergence.md`
|
|
8
|
+
**Commitments:** `commitments/comprehensive-destructive-tool-containment.yaml`
|
|
9
|
+
**Approval:** `approved: true` by Justin via Telegram topic 8122 on 2026-04-26 after reading the bundled review doc.
|
|
10
|
+
|
|
11
|
+
## Summary of the change
|
|
12
|
+
|
|
13
|
+
This is the foundation half of the comprehensive containment work. It introduces two new destructive-operation funnels (`SafeGitExecutor`, `SafeFsExecutor`), a lint rule that refuses new direct destructive callsites, a CI step that catches accidental tree mutations, an audit log, and the three structural deferral-honesty layers that prevent recurrence of the "out-of-scope trap" pattern that allowed Incident B.
|
|
14
|
+
|
|
15
|
+
The migration of pre-existing direct callsites (1025 of them — 6× larger than the spec's initial estimate) is a separate PR scheduled for delivery within 7 days under principal-approved deferral with monitoring trigger (`commitment://incremental-migration`, due 2026-05-03). During the transitional period a `// safe-git-allow: incremental-migration` comment marker preserves bisectability — the lint rule blocks NEW direct callsites unconditionally; pre-existing callsites pass via marker.
|
|
16
|
+
|
|
17
|
+
## Decision-point inventory
|
|
18
|
+
|
|
19
|
+
Changes to decision points:
|
|
20
|
+
|
|
21
|
+
- **Added**: `src/core/SafeGitExecutor.ts` — single-funnel destructive git executor. Calls `assertNotInstarSourceTree` (from PR #96) against canonicalized cwd + `-C <dir>` + `--git-dir=` + `--work-tree=` targets. Strips git-redirection env vars from caller-supplied env. Injects `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_SYSTEM=/dev/null`, `GIT_CONFIG_NOSYSTEM=1` to disable user-gitconfig alias bypasses.
|
|
22
|
+
- **Added**: `src/core/SafeFsExecutor.ts` — parallel funnel for in-process destructive `fs` calls (`rm`, `unlink`, `rmdir` and their sync variants). Same canonicalization + assertion pipeline.
|
|
23
|
+
- **Added**: `scripts/lint-no-direct-destructive.js` — AST rule blocking direct `execFileSync('git', …)`, `spawn('git', …)`, `simpleGit(…)`, `execSync('git …')` AND direct `fs.rm*`, `fs.unlink*`, `fs.rmdir*` outside the closed module allowlist. Catches namespace imports, aliased imports, dynamic require, namespace-imported forms.
|
|
24
|
+
- **Added**: `.github/workflows/ci.yml` working-tree integrity step at end of `unit`, `integration`, `e2e` jobs — fails build if `git status --porcelain` shows mutations.
|
|
25
|
+
- **Added**: `.instar/audit/destructive-ops.jsonl` audit log — every safe-executor call appends a structured JSON line (timestamp, executor, operation, verb, target, outcome, reason, caller). Fail-soft on log-write failure.
|
|
26
|
+
- **Added**: Layer A — `/instar-dev` skill deferral-honesty check. LLM classifier with regex fail-closed fallback. Refuses `recurrence-risking` items entirely unless `principal-deferral-approval` lists their commitment IDs. Refuses `tactical-deferral` items without paired tracked commitments. Time horizons: 36 hours / 6 days (10× tightened from initial 14d / 60d on principal directive).
|
|
27
|
+
- **Added**: Layer B — pre-commit hook deferral-section structural check (extends `scripts/instar-dev-precommit.js`). Refuses commits with "Out-of-scope follow-ups" section header but no paired commitments file.
|
|
28
|
+
- **Added**: Layer C — `/spec-converge` "recurrence-containment" reviewer angle. Two questions per deferred item: "if this never ships, does the original problem recur?" and "is there any way this could be done in current scope, even at the cost of a larger PR?"
|
|
29
|
+
- **Added**: 53 new unit tests covering SafeGitExecutor + SafeFsExecutor + lint-rule bypass closures, plus regression tests reproducing Incident A and Incident B at the new funnel level.
|
|
30
|
+
- **Modified**: 570 pre-existing destructive callsites carry the `// safe-git-allow: incremental-migration` marker as a transitional pass-through. Lint rule honors the marker until the migration commitment lands.
|
|
31
|
+
- **Allowlist (transitional)**: `src/messaging/imessage/IMessageAdapter.ts` and `src/messaging/imessage/NativeBackend.ts` are added to the lint ALLOWLIST as transitional entries (not per-line markers) so that the foundation PR does not modify adapter source files. This keeps the pre-push adapter contract test gate from triggering on what would otherwise be no-op marker comments. PR #2 (commitment://incremental-migration, due 2026-05-03) migrates these `fs.unlinkSync` calls through `SafeFsExecutor` and removes the entries.
|
|
32
|
+
|
|
33
|
+
## Roll-up verdict across the seven review dimensions
|
|
34
|
+
|
|
35
|
+
1. **Over-block**: minimal. The marker mechanism is intentionally transitional — pre-existing callsites pass via marker; new ones are refused unconditionally. False-block cost on a new caller is "use the safe executor"; the cost trade is correct.
|
|
36
|
+
2. **Under-block**: known transitional surface — pre-existing direct callsites can still hit the source tree if a fixture passes a misconfigured cwd. Compensating mechanisms during the transitional period: PR #96's three-class constructor guard catches manager-class instantiation; the new CI tree-mutation detector catches anything that mutated the working tree post-test; the migration commitment has a 7-day hard deadline with automatic Telegram notification on the due date.
|
|
37
|
+
3. **Level-of-abstraction fit**: appropriate. Funnels at the right layer (single chokepoint per domain). Lint at the right layer (compile-time AST rule). CI detector at the right layer (post-test integration). Layers A/B/C at the developer-process layer.
|
|
38
|
+
4. **Signal-vs-authority compliance**: compliant. `assertNotInstarSourceTree` is the authority (carve-out applies — irreversible action class). Lint rule is brittle pattern-matcher with refusal authority on a structural rule. Layer A LLM is smart authority; Layer B grep is brittle signal-producer; Layer C reviewer prompt is smart-LLM authority. All within carve-out.
|
|
39
|
+
5. **Interactions**: tested. 53 new tests pass. Three pre-existing test failures (`agent-registry.test.ts:271,287`, `ListenerSessionManager.test.ts:359`) verified to fail on baseline commit `1f06e99` before any changes — not introduced by this work. One flaky E2E (`tunnel-private-view.test.ts`) passes in isolation, fails in full-suite ordering — pre-existing flakiness. Other test suites unaffected.
|
|
40
|
+
6. **External surfaces**: lint rule extends pre-commit and pre-push gates (developer-process surface, not user-runtime). CI workflow gains a post-test step (CI surface, not user-runtime). No user-runtime API changes. Audit log is local, gitignored, and informational only.
|
|
41
|
+
7. **Rollback cost**: low. Per-commit revert restores prior state. No persistent state mutations beyond the new audit log file (which is informational and gitignored). The marker comments are pure no-ops — removing them is mechanical.
|
|
42
|
+
|
|
43
|
+
## Second-pass review
|
|
44
|
+
|
|
45
|
+
External cross-model review across GPT 5.4, Gemini 3.1 Pro, Grok 4.1 Fast. All three returned 8–9/10 CONDITIONAL. Eleven material findings between them, all addressed in spec before approval:
|
|
46
|
+
|
|
47
|
+
1. Env-var redirection bypass closure (GPT)
|
|
48
|
+
2. User-gitconfig alias bypass closure via `readSync` source-tree check + `GIT_CONFIG_GLOBAL=/dev/null` injection (Gemini)
|
|
49
|
+
3. Namespace-import lint coverage (Gemini)
|
|
50
|
+
4. `write-tree` reclassified read-only (GPT)
|
|
51
|
+
5. Path canonicalization on `cwd`/`-C`/`--git-dir`/`--work-tree` (GPT)
|
|
52
|
+
6. `format-patch` shape check (Grok)
|
|
53
|
+
7. LLM-unavailable fail-closed regex fallback (Grok)
|
|
54
|
+
8. Classifier hallucination override path (Gemini)
|
|
55
|
+
9. Audit logging (GPT, Grok)
|
|
56
|
+
10. Self-compliance contradiction → pulled `safe-fs` and `ci-mutation-detector` in-scope (Gemini)
|
|
57
|
+
11. Comprehensive-first stance + 10× time-horizon tightening (principal directive)
|
|
58
|
+
|
|
59
|
+
Synthesis at `~/.instar/agents/echo/.claude/skills/crossreview/output/20260426-131003/synthesis.md`. Convergence report Round 4 documents per-finding disposition.
|
|
60
|
+
|
|
61
|
+
## Evidence pointers
|
|
62
|
+
|
|
63
|
+
- 53 new unit tests in `tests/unit/SafeGitExecutor.test.ts`, `tests/unit/SafeFsExecutor.test.ts`, `tests/unit/lint-no-direct-destructive.test.ts`. All passing.
|
|
64
|
+
- Incident A regression test at `tests/integration/incident-a-fs-regression.test.ts` — verifies in-process `fs.rmSync(realInstarPath, …)` is blocked.
|
|
65
|
+
- Incident B regression test at `tests/integration/incident-b-regression.test.ts` — verifies test-fixture-shape `execFileSync('git', ['add', '-A'], { cwd: <instar source root> })` is blocked.
|
|
66
|
+
- Cross-review raw outputs at `~/.instar/agents/echo/.claude/skills/crossreview/output/20260426-131003/{gpt,gemini,grok,synthesis}.md`.
|
|
67
|
+
- Spec frontmatter records `approved: true`, `approved-by: justin`, `approved-at`, and `principal-deferral-approval` for `commitment://incremental-migration`.
|
|
68
|
+
- Commitments file lists three remaining deferrals (positive-authorization-redesign, kernel-container-guards, autostash-rebase-safety) plus the new incremental-migration with 7-day deadline. All non-`unscheduled` items have automatic Telegram-notification job triggers.
|
|
69
|
+
|
|
70
|
+
## Migration deferral — explicit principal-deferral-approval
|
|
71
|
+
|
|
72
|
+
Justin approved the incremental-migration deferral via Telegram topic 8122 on 2026-04-26 after acknowledging the scope-reality mismatch (spec estimated ~167 callsites; actual 1025). The deferral is recorded in the spec frontmatter under `principal-deferral-approval` with full rationale. The 7-day cap is a principal-approved override of the standard 36-hour `recurrence-risking` cap, justified by the engineering reality that 1025 mechanical migrations + their full-suite test verification cannot ship in a single PR without unacceptable risk of subtle test breakage and an extended no-progress window.
|
|
73
|
+
|
|
74
|
+
This is the first real exercise of the new commitment-tracker infrastructure. The migration PR (PR 2/2) follows this one in the same session.
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Side-Effects Review — Destructive tool target guard
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `source-tree-guard`
|
|
4
|
+
**Date:** `2026-04-24`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required (brittle safety-guard carve-out; see §4)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Adds a new primitive `src/core/SourceTreeGuard.ts` that refuses to let
|
|
11
|
+
destructive managers (`GitSyncManager`, `BranchManager`,
|
|
12
|
+
`HandoffManager`) be constructed against the instar source tree. The
|
|
13
|
+
guard is wired as the first statement of each manager's constructor and
|
|
14
|
+
throws `SourceTreeGuardError` (code `INSTAR_SOURCE_TREE_GUARD`) before
|
|
15
|
+
any collaborator touches anything. Detection is the OR of three layers
|
|
16
|
+
(marker file `.instar-source-tree`, canonical `origin` URL in the
|
|
17
|
+
resolved common git dir's config, or `package.json name === "instar"`
|
|
18
|
+
plus two-of-N signature files). Path resolution closes the
|
|
19
|
+
uncreated-subdirectory bypass via a nearest-existing-ancestor walk, and
|
|
20
|
+
handles worktrees via `.git`-file parsing with
|
|
21
|
+
`basename(dirname(gitdir)) === "worktrees"` common-git-dir resolution.
|
|
22
|
+
Fail-closed is two-tier: detector-level inability to canonicalize/ascend
|
|
23
|
+
returns TRUE; layer-level inability to evaluate returns FALSE for that
|
|
24
|
+
sub-check and the OR across layers decides.
|
|
25
|
+
|
|
26
|
+
Files touched:
|
|
27
|
+
|
|
28
|
+
- `src/core/SourceTreeGuard.ts` (new)
|
|
29
|
+
- `src/core/GitSync.ts` (+1 import, +1 assert at constructor top)
|
|
30
|
+
- `src/core/BranchManager.ts` (+1 import, +1 assert at constructor top)
|
|
31
|
+
- `src/core/HandoffManager.ts` (+1 import, +1 assert at constructor top)
|
|
32
|
+
- `.instar-source-tree` (new — marker file at repo root)
|
|
33
|
+
- `tests/unit/SourceTreeGuard.test.ts` (new — 34 tests)
|
|
34
|
+
- `tests/integration/source-tree-guard-wiring.test.ts` (new — 10 tests)
|
|
35
|
+
- `docs/specs/DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC.md` (spec, pre-existing)
|
|
36
|
+
- `docs/specs/reports/destructive-tool-target-guards-convergence.md` (report, pre-existing)
|
|
37
|
+
|
|
38
|
+
Incident context: the 2026-04-22 branch-lifecycle e2e fixture wiped 1,893
|
|
39
|
+
files from the real instar checkout because destructive components
|
|
40
|
+
trusted an incoming `projectDir` without verification. This PR delivers
|
|
41
|
+
the tactical guardrail described in the spec. E2E sandbox hardening,
|
|
42
|
+
Adriana's autostash fix, the CI mutation detector, and the
|
|
43
|
+
SafeGitExecutor centralization are explicitly deferred to separate PRs.
|
|
44
|
+
|
|
45
|
+
## Decision-point inventory
|
|
46
|
+
|
|
47
|
+
- `src/core/SourceTreeGuard.ts :: isInstarSourceTree` — **add** — new
|
|
48
|
+
detector returning boolean based on the 3-layer OR.
|
|
49
|
+
- `src/core/SourceTreeGuard.ts :: assertNotInstarSourceTree` — **add** —
|
|
50
|
+
new assertion wrapper that throws `SourceTreeGuardError`.
|
|
51
|
+
- `GitSyncManager` constructor — **modify** — first statement now calls
|
|
52
|
+
the assertion; no other behavior change.
|
|
53
|
+
- `BranchManager` constructor — **modify** — same.
|
|
54
|
+
- `HandoffManager` constructor — **modify** — same.
|
|
55
|
+
|
|
56
|
+
No existing decision points are removed or repurposed.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 1. Over-block
|
|
61
|
+
|
|
62
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
63
|
+
|
|
64
|
+
Concrete over-block scenarios considered:
|
|
65
|
+
|
|
66
|
+
- A parallel-dev worktree *of the instar source* used as a destructive
|
|
67
|
+
target. Blocked today. This is intended — the worktree's common git
|
|
68
|
+
dir is the real repo, so `git add -A` from the worktree mutates the
|
|
69
|
+
real repo. If someone legitimately needs this, the fix is to loosen
|
|
70
|
+
the guard deliberately, not to silently permit it.
|
|
71
|
+
- A fork that keeps `package.json.name === "instar"`, ships two of the
|
|
72
|
+
signature files (likely — they're core infra), and carries the
|
|
73
|
+
`.instar-source-tree` marker (only if the forker copied it; it's in
|
|
74
|
+
the upstream repo but not automatically propagated by `git clone`).
|
|
75
|
+
Fork remotes are NOT on the canonical list, so layer (b) does not
|
|
76
|
+
match. If a fork has the marker and keeps the name+signature, it
|
|
77
|
+
blocks — the fork author can delete the marker in their repo.
|
|
78
|
+
- A developer who manually sets their working dir to the instar source
|
|
79
|
+
to run a one-off script. Blocked only for destructive-manager
|
|
80
|
+
construction — read-only tools are untouched. This is the entire
|
|
81
|
+
purpose of the guard.
|
|
82
|
+
- Tests that deliberately construct a manager pointed at the instar
|
|
83
|
+
source to exercise error paths. Use a `mkdtemp` sandbox instead (the
|
|
84
|
+
integration tests in this PR follow that convention).
|
|
85
|
+
|
|
86
|
+
No legitimate input is rejected that shouldn't be. The one "blocked by
|
|
87
|
+
design" case (worktrees of the source) matches the incident shape and
|
|
88
|
+
is a deliberate guard.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 2. Under-block
|
|
93
|
+
|
|
94
|
+
**What failure modes does this still miss?**
|
|
95
|
+
|
|
96
|
+
- Destructive components not on the three-manager list. Addressed by
|
|
97
|
+
the pre-ship enumeration below: every direct git invocation under
|
|
98
|
+
`src/` was inventoried. See §"Pre-ship grep enumeration evidence."
|
|
99
|
+
- Destructive work launched via child processes or shell scripts
|
|
100
|
+
(e.g. `nuke.ts`, `init.ts`) that bypass the manager layer. These are
|
|
101
|
+
either operator-initiated (`nuke`) or target a fresh dir (`init`),
|
|
102
|
+
not the incident shape. The SafeGitExecutor follow-up PR centralizes
|
|
103
|
+
these.
|
|
104
|
+
- Fork of instar that renames `package.json`, changes canonical remote,
|
|
105
|
+
and drops the marker. By design — if all three layers disagree this
|
|
106
|
+
is not the instar source tree, guard passes.
|
|
107
|
+
- A manager constructed against a fresh clone that doesn't have the
|
|
108
|
+
marker yet AND has a non-canonical remote (e.g. contributor fork)
|
|
109
|
+
AND has been renamed. Same as above — by design not caught.
|
|
110
|
+
|
|
111
|
+
Under-block is scoped to "things out of this PR's lane" and enumerated
|
|
112
|
+
in the spec's Out-of-Scope Follow-Ups. Nothing silently degrades.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 3. Level-of-abstraction fit
|
|
117
|
+
|
|
118
|
+
**Is this at the right layer?**
|
|
119
|
+
|
|
120
|
+
Constructor-time on the three destructive managers is the correct
|
|
121
|
+
layer for this PR. It is the point at which `projectDir` first becomes
|
|
122
|
+
trusted state — below this (inside `execFileSync('git', ...)`) is too
|
|
123
|
+
late (damage already in flight); above this (scattered across callers)
|
|
124
|
+
is the layer the incident proved unreliable.
|
|
125
|
+
|
|
126
|
+
A lower-level primitive (`SafeGitExecutor`) will additionally funnel at
|
|
127
|
+
the `execFileSync` boundary in a follow-up PR. The constructor wire-in
|
|
128
|
+
remains as belt-and-suspenders even after that refactor.
|
|
129
|
+
|
|
130
|
+
A higher-level gate does NOT already exist for this — the incident is
|
|
131
|
+
specifically the absence of such a gate. This change creates it.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 4. Signal vs authority compliance
|
|
136
|
+
|
|
137
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
138
|
+
|
|
139
|
+
**Does this change hold blocking authority with brittle logic?**
|
|
140
|
+
|
|
141
|
+
- [ ] No — this change produces a signal consumed by an existing smart gate.
|
|
142
|
+
- [ ] No — this change has no block/allow surface.
|
|
143
|
+
- [ ] Yes — but the logic is a smart gate with full conversational context.
|
|
144
|
+
- [x] ✅ Yes, with brittle logic — **and that is correct** under the
|
|
145
|
+
carve-out in `docs/signal-vs-authority.md` lines 74–77:
|
|
146
|
+
|
|
147
|
+
> **Safety guards on irreversible actions.** `rm -rf /`,
|
|
148
|
+
> force-pushing to main, deleting the database — these can and
|
|
149
|
+
> should be hard-blocked by brittle pattern matchers, because the
|
|
150
|
+
> cost of a false pass is catastrophic and the cost of a false
|
|
151
|
+
> block is merely "try again with the right arguments."
|
|
152
|
+
|
|
153
|
+
This guard is exactly that category. False-pass cost: 1,893-file wipe +
|
|
154
|
+
force-push recovery (established empirically on 2026-04-22). False-block
|
|
155
|
+
cost: developer edits a path or touches a marker file once. The
|
|
156
|
+
asymmetry is overwhelming and the principle explicitly excludes this
|
|
157
|
+
class of check from the "brittle = signal only" rule.
|
|
158
|
+
|
|
159
|
+
No judgment gate is being added. No LLM-backed contextual check is
|
|
160
|
+
being bypassed. The check does not reason about message content, agent
|
|
161
|
+
intent, or conversational state — it asks "is this path the source
|
|
162
|
+
tree?" and acts on the yes/no. Fully compliant with the
|
|
163
|
+
signal-vs-authority separation.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 5. Interactions
|
|
168
|
+
|
|
169
|
+
**Does this interact with existing checks, recovery paths, or infrastructure?**
|
|
170
|
+
|
|
171
|
+
- **Shadowing:** The guard runs as the FIRST statement of each
|
|
172
|
+
constructor, before any other validation. It cannot shadow existing
|
|
173
|
+
checks — it precedes them. If the guard throws, later validation does
|
|
174
|
+
not run, which is correct (the path is untrusted; any later check
|
|
175
|
+
against it is moot).
|
|
176
|
+
- **Double-fire:** No other component performs an equivalent check
|
|
177
|
+
today (grep confirms). Once `SafeGitExecutor` lands, both will fire;
|
|
178
|
+
that's intentional redundancy, not a bug.
|
|
179
|
+
- **Races:** Pure synchronous fs reads. No shared state. No
|
|
180
|
+
concurrency hazard. The detector is idempotent and side-effect-free.
|
|
181
|
+
- **Feedback loops:** None. The guard does not feed any downstream
|
|
182
|
+
decision system.
|
|
183
|
+
- **Test harness interaction:** All existing `mkdtemp`-based tests
|
|
184
|
+
still pass. The smoke tier (2037 tests) passes locally post-wiring.
|
|
185
|
+
Integration test confirms that a sandbox subdirectory still
|
|
186
|
+
constructs successfully.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 6. External surfaces
|
|
191
|
+
|
|
192
|
+
**Does this change anything visible outside the immediate code path?**
|
|
193
|
+
|
|
194
|
+
- **Other agents on the same machine:** none. Per-process guard only.
|
|
195
|
+
- **Other users of the install base:** behaviour changes only for
|
|
196
|
+
callers constructing the three managers against the instar source
|
|
197
|
+
itself — which no legitimate user does (it would mutate the instar
|
|
198
|
+
source). Forks are intentionally not caught by layer (b); layer (c)
|
|
199
|
+
catches unrenamed forks, which is arguably a feature (forkers who
|
|
200
|
+
kept the instar name probably also don't want their own repo wiped).
|
|
201
|
+
- **External systems:** no network, no LLM, no IPC. Sub-millisecond
|
|
202
|
+
synchronous fs check.
|
|
203
|
+
- **Persistent state:** the `.instar-source-tree` marker file at repo
|
|
204
|
+
root is new persistent state. It's a one-line inert sentinel; its
|
|
205
|
+
only consumer is this guard. Safe to leave in place on rollback.
|
|
206
|
+
- **Timing / runtime conditions:** the guard runs at constructor time,
|
|
207
|
+
before any async work. Worst case ~5ms cold (three fs reads); warm
|
|
208
|
+
cache sub-millisecond. Cannot cause gate-vs-client-timeout issues.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 7. Rollback cost
|
|
213
|
+
|
|
214
|
+
**If this turns out wrong in production, what's the back-out?**
|
|
215
|
+
|
|
216
|
+
- **Hot-fix release:** revert the commit — delete
|
|
217
|
+
`src/core/SourceTreeGuard.ts`, remove the three one-line wire-ins,
|
|
218
|
+
ship as next patch. No code outside this module depends on the new
|
|
219
|
+
symbols. The `.instar-source-tree` marker can be left in place (inert
|
|
220
|
+
without the guard reading it) — avoids a second commit.
|
|
221
|
+
- **Data migration:** none. No persistent state introduced beyond the
|
|
222
|
+
inert marker file.
|
|
223
|
+
- **Agent state repair:** none. Per-process guard, no shared state.
|
|
224
|
+
- **User visibility:** none — rollback restores prior behaviour
|
|
225
|
+
instantly on restart. A legitimate caller that was ever blocked by
|
|
226
|
+
this guard was almost certainly pointed at the wrong tree anyway; the
|
|
227
|
+
rollback window does not expose users to regression.
|
|
228
|
+
|
|
229
|
+
Estimated hot-fix time: minutes.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Conclusion
|
|
234
|
+
|
|
235
|
+
This review produced the following design decisions locked into the
|
|
236
|
+
final code:
|
|
237
|
+
|
|
238
|
+
- Two-tier fail-closed (detector-level = TRUE; layer-level = FALSE for
|
|
239
|
+
that sub-check) to avoid the over-block pathology flagged in the
|
|
240
|
+
convergence review.
|
|
241
|
+
- Worktree common-git-dir resolution via the
|
|
242
|
+
`basename(dirname(gitdir)) === "worktrees"` rule, falling through to
|
|
243
|
+
layer (b) inconclusive on non-standard layouts rather than guessing.
|
|
244
|
+
- Canonical-remote list kept intentionally narrow (three exact URLs
|
|
245
|
+
with three narrow normalization rules: strip whitespace, strip
|
|
246
|
+
trailing slash, strip trailing `.git`). No substring or regex
|
|
247
|
+
matching.
|
|
248
|
+
- Error message deliberately does NOT inline bypass instructions (a
|
|
249
|
+
tutorial-in-the-error-message is a convenient "how to defeat the
|
|
250
|
+
guard" crib sheet that outlives the reason it exists).
|
|
251
|
+
- Pre-ship enumeration converts the three-manager defense from "hope
|
|
252
|
+
we found them all" to "documented inventory at ship time." Findings
|
|
253
|
+
below.
|
|
254
|
+
|
|
255
|
+
The change is clear to ship. It is a necessary-but-not-sufficient step
|
|
256
|
+
toward the SafeGitExecutor centralization, which is scheduled as a
|
|
257
|
+
follow-up PR.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Pre-ship grep enumeration evidence
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
$ grep -rnE "execFileSync\('git'|execSync\('git |spawn\(?'git'|spawnSync\('git'" src/
|
|
265
|
+
src/core/SyncOrchestrator.ts:1163 return execFileSync('git', args, { non-destructive (delegates to GitSync / BranchManager / HandoffManager which are now guarded)
|
|
266
|
+
src/core/ProjectMapper.ts:261 execFileSync('git', ['remote', 'get-url', 'origin'], …) non-destructive (read-only)
|
|
267
|
+
src/core/ProjectMapper.ts:275 execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], …) non-destructive (read-only)
|
|
268
|
+
src/core/ScopeVerifier.ts:449 execFileSync('git', ['remote', 'get-url', 'origin'], …) non-destructive (read-only)
|
|
269
|
+
src/core/BranchManager.ts:533 execFileSync('git', args, { destructive — wrapped by the guarded BranchManager constructor
|
|
270
|
+
src/core/AgentConnector.ts:141 execSync('git --version', …) non-destructive (version query)
|
|
271
|
+
src/core/ParallelDevWiring.ts:35 execFileSync('git', ['remote', 'get-url', 'origin'], …) non-destructive (read-only)
|
|
272
|
+
src/core/GitSync.ts:1006 execFileSync('git', args, { destructive — wrapped by the guarded GitSyncManager constructor
|
|
273
|
+
src/core/WorktreeManager.ts:756 execFileSync('git', […'rev-parse', 'HEAD'], …) non-destructive (read-only)
|
|
274
|
+
src/core/WorktreeManager.ts:763 execFileSync('git', […'rev-parse', '--verify', branch], …) non-destructive (read-only)
|
|
275
|
+
src/core/WorktreeManager.ts:765 execFileSync('git', […'branch', branch], …) branch-create only; targets a worktree path, not the source tree — deferred to SafeGitExecutor PR
|
|
276
|
+
src/core/WorktreeManager.ts:767 execFileSync('git', […'worktree', 'add', …], …) creates a new worktree, does not mutate source — out of scope
|
|
277
|
+
src/core/WorktreeManager.ts:879 execFileSync('git', […'worktree', 'list', …], …) non-destructive (read-only)
|
|
278
|
+
src/core/FileClassifier.ts:298 execFileSync('git', ['checkout', '--ours', …], …) destructive — invoked only during sync under GitSyncManager's control; guard fires at manager construction upstream
|
|
279
|
+
src/core/FileClassifier.ts:302 execFileSync('git', ['add', …], …) destructive — same as above; upstream-guarded
|
|
280
|
+
src/core/FileClassifier.ts:324 execFileSync('git', ['add', relPath], …) destructive — same; upstream-guarded
|
|
281
|
+
src/core/HandoffManager.ts:496 execFileSync('git', args, …) destructive — wrapped by the guarded HandoffManager constructor
|
|
282
|
+
src/server/routes.ts:2285 execFileSync('git', ['remote'], …) non-destructive (read-only)
|
|
283
|
+
src/server/routes.ts:3035 execFileSync('git', ['remote', 'get-url', 'origin'], …) non-destructive (read-only)
|
|
284
|
+
src/lifeline/ServerSupervisor.ts:376 spawnSync('git', ['status'], …) non-destructive (read-only)
|
|
285
|
+
src/lifeline/ServerSupervisor.ts:384 spawnSync('git', ['rebase', '--abort'], …) recovery-only, operates on the agent's OWN repo (not the source tree); orthogonal to this guard
|
|
286
|
+
src/commands/init.ts:386 execFileSync('git', ['init'], …) destructive on target dir — target is a fresh user project, not the source tree; out of scope
|
|
287
|
+
src/commands/init.ts:3329 execFileSync('git', ['remote'], …) non-destructive (read-only)
|
|
288
|
+
src/commands/nuke.ts:142 execFileSync('git', ['add', '-A'], …) destructive — operator-initiated on the agent's own directory; documented and out of scope
|
|
289
|
+
src/commands/nuke.ts:143 execFileSync('git', ['status', '--porcelain'], …) non-destructive (read-only)
|
|
290
|
+
src/commands/nuke.ts:150 execFileSync('git', ['commit', …], …) destructive — same as above
|
|
291
|
+
src/commands/nuke.ts:156 execFileSync('git', ['push'], …) destructive — same
|
|
292
|
+
src/commands/nuke.ts:212 execFileSync('git', ['remote'], …) non-destructive (read-only)
|
|
293
|
+
src/commands/machine.ts:285 execFileSync('git', ['clone', …], …) destructive on a fresh target, not the source tree
|
|
294
|
+
src/commands/setup.ts:114 execFileSync('git', ['rev-parse', '--show-toplevel'], …) non-destructive (read-only)
|
|
295
|
+
src/monitoring/WorktreeReaper.ts:208 execFileSync('git', […'worktree', 'add', …], …) creates a new worktree, does not mutate source
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Summary:
|
|
299
|
+
- 31 direct git invocations under `src/`.
|
|
300
|
+
- Destructive call sites routed through the three guarded managers:
|
|
301
|
+
`GitSync.ts:1006`, `BranchManager.ts:533`, `HandoffManager.ts:496`,
|
|
302
|
+
plus three `FileClassifier` helpers invoked only under
|
|
303
|
+
`GitSyncManager`'s control.
|
|
304
|
+
- Destructive sites NOT routed through the three managers and flagged
|
|
305
|
+
for PR 2 (SafeGitExecutor centralization): `WorktreeManager.ts`
|
|
306
|
+
branch/worktree creation, `commands/init.ts`, `commands/nuke.ts`,
|
|
307
|
+
`commands/machine.ts clone`, `ServerSupervisor.ts rebase --abort`.
|
|
308
|
+
None of these target the instar source tree in their documented call
|
|
309
|
+
patterns — they either operate on fresh targets (`init`, `clone`,
|
|
310
|
+
`worktree add`) or on the agent's own dir (`nuke`, `rebase --abort`).
|
|
311
|
+
- No unguarded manager-layer destructive paths found.
|
|
312
|
+
|
|
313
|
+
This inventory is the "documented enumeration at ship time" the spec's
|
|
314
|
+
pre-ship acceptance criterion requires.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Evidence pointers
|
|
319
|
+
|
|
320
|
+
- Spec: `docs/specs/DESTRUCTIVE-TOOL-TARGET-GUARDS-SPEC.md` (approved,
|
|
321
|
+
converged).
|
|
322
|
+
- Convergence report:
|
|
323
|
+
`docs/specs/reports/destructive-tool-target-guards-convergence.md`.
|
|
324
|
+
- Unit tests: `tests/unit/SourceTreeGuard.test.ts` — 34 tests, all
|
|
325
|
+
green locally. Covers detector layers individually, normalization
|
|
326
|
+
rules, worktree handling (standard + non-standard + malformed +
|
|
327
|
+
relative pointer), subdirectory bypass, uncreated-subdirectory
|
|
328
|
+
bypass, symlink canonicalization, two-tier fail-closed, error
|
|
329
|
+
shape.
|
|
330
|
+
- Integration tests:
|
|
331
|
+
`tests/integration/source-tree-guard-wiring.test.ts` — 10 tests, all
|
|
332
|
+
green. Covers: constructor throws for each of the three managers
|
|
333
|
+
pointed at the real instar source; uncreated-subdirectory-of-instar
|
|
334
|
+
still throws; mkdtemp sandbox construction succeeds for all three;
|
|
335
|
+
subdirectory-of-sandbox also succeeds (no over-block on legitimate
|
|
336
|
+
nested paths); side-effect isolation (state dir NOT created when
|
|
337
|
+
guard fires).
|
|
338
|
+
- Smoke tier pass: `npm run test:smoke` — 2037 tests passed locally
|
|
339
|
+
post-wiring (the push-hook's gate).
|
|
340
|
+
- Full-suite sample: `tsc --noEmit` clean; no new warnings.
|