pi-crew 0.9.16 → 0.9.18
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/CHANGELOG.md +130 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21784 -0
- package/dist/index.mjs +83169 -0
- package/dist/index.mjs.map +7 -0
- package/docs/A +358 -0
- package/docs/M-A +357 -0
- package/docs/REVIEW-FINDINGS-2026-06 +357 -0
- package/docs/Y +357 -0
- package/docs/a +357 -0
- package/docs/aA +358 -0
- package/docs/archive/README.md +91 -0
- package/docs/migration/atomic-write-v2-migration.md +297 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/perf/performance-review-2026-07.md +287 -0
- package/docs/skills/REFERENCE.md +0 -17
- package/docs//303/242mmaAAA/303/242 +357 -0
- package/index.bundle.ts +25 -0
- package/index.ts +95 -4
- package/package.json +15 -4
- package/skills/iterative-audit/SKILL.md +0 -1
- package/skills/widget-rendering/SKILL.md +17 -0
- package/src/adapters/claude-adapter.ts +1 -3
- package/src/adapters/export-util.ts +16 -16
- package/src/adapters/index.ts +5 -5
- package/src/agents/agent-config.ts +20 -14
- package/src/agents/agent-serializer.ts +1 -1
- package/src/agents/discover-agents.ts +83 -39
- package/src/benchmark/benchmark-runner.ts +51 -51
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +303 -591
- package/src/config/defaults.ts +28 -2
- package/src/config/drift-detector.ts +11 -14
- package/src/config/markers.ts +201 -203
- package/src/config/resilient-parser.ts +14 -6
- package/src/config/role-tools.ts +3 -3
- package/src/config/suggestions.ts +5 -12
- package/src/config/types.ts +9 -25
- package/src/errors.ts +151 -157
- package/src/extension/action-suggestions.ts +54 -9
- package/src/extension/async-notifier.ts +43 -13
- package/src/extension/autonomous-policy.ts +77 -37
- package/src/extension/command-completions.ts +9 -8
- package/src/extension/context-status-injection.ts +14 -12
- package/src/extension/crew-autocomplete.ts +8 -16
- package/src/extension/crew-cleanup.ts +2 -1
- package/src/extension/crew-shortcuts.ts +9 -6
- package/src/extension/cross-extension-rpc.ts +136 -42
- package/src/extension/help.ts +1 -1
- package/src/extension/import-index.ts +10 -5
- package/src/extension/knowledge-injection.ts +88 -15
- package/src/extension/management.ts +89 -349
- package/src/extension/message-renderers.ts +3 -4
- package/src/extension/notification-router.ts +22 -5
- package/src/extension/notification-sink.ts +6 -3
- package/src/extension/pi-api.ts +17 -8
- package/src/extension/plan-orchestrate.ts +8 -27
- package/src/extension/project-init.ts +21 -6
- package/src/extension/register.ts +329 -975
- package/src/extension/registration/artifact-cleanup.ts +9 -4
- package/src/extension/registration/command-utils.ts +5 -1
- package/src/extension/registration/commands.ts +806 -314
- package/src/extension/registration/compaction-guard.ts +15 -15
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +293 -0
- package/src/extension/registration/subagent-helpers.ts +29 -7
- package/src/extension/registration/subagent-tools.ts +253 -44
- package/src/extension/registration/team-tool.ts +19 -84
- package/src/extension/registration/ui.ts +168 -0
- package/src/extension/registration/viewers.ts +64 -24
- package/src/extension/result-watcher.ts +18 -7
- package/src/extension/run-bundle-schema.ts +21 -6
- package/src/extension/run-export.ts +15 -6
- package/src/extension/run-import.ts +64 -29
- package/src/extension/run-index.ts +28 -9
- package/src/extension/run-maintenance.ts +32 -12
- package/src/extension/session-summary.ts +1 -1
- package/src/extension/team-manager-command.ts +64 -7
- package/src/extension/team-onboard.ts +149 -150
- package/src/extension/team-recommendation.ts +121 -21
- package/src/extension/team-tool/anchor.ts +31 -64
- package/src/extension/team-tool/api.ts +929 -141
- package/src/extension/team-tool/auto-summarize.ts +14 -26
- package/src/extension/team-tool/cache-control.ts +1 -5
- package/src/extension/team-tool/cancel.ts +138 -38
- package/src/extension/team-tool/chain-dispatch.ts +5 -13
- package/src/extension/team-tool/chain-executor.ts +29 -51
- package/src/extension/team-tool/config-patch.ts +1 -1
- package/src/extension/team-tool/context.ts +40 -20
- package/src/extension/team-tool/destructive-gate.ts +10 -5
- package/src/extension/team-tool/doctor.ts +170 -51
- package/src/extension/team-tool/explain.ts +228 -214
- package/src/extension/team-tool/failure-patterns.ts +3 -9
- package/src/extension/team-tool/goal-wrap.ts +71 -29
- package/src/extension/team-tool/goal.ts +185 -35
- package/src/extension/team-tool/handle-schedule.ts +34 -15
- package/src/extension/team-tool/handle-settings.ts +94 -56
- package/src/extension/team-tool/health-monitor.ts +27 -108
- package/src/extension/team-tool/inspect.ts +42 -9
- package/src/extension/team-tool/intent-policy.ts +4 -1
- package/src/extension/team-tool/lifecycle-actions.ts +244 -48
- package/src/extension/team-tool/orchestrate.ts +7 -18
- package/src/extension/team-tool/parallel-dispatch.ts +51 -29
- package/src/extension/team-tool/plan.ts +29 -5
- package/src/extension/team-tool/respond.ts +49 -16
- package/src/extension/team-tool/run-not-found.ts +3 -8
- package/src/extension/team-tool/run.ts +80 -317
- package/src/extension/team-tool/status.ts +135 -43
- package/src/extension/team-tool/workflow-manage.ts +92 -25
- package/src/extension/team-tool-types.ts +8 -1
- package/src/extension/team-tool.ts +142 -551
- package/src/extension/validate-resources.ts +39 -8
- package/src/hooks/registry.ts +66 -17
- package/src/hooks/types.ts +1 -1
- package/src/i18n.ts +25 -11
- package/src/observability/correlation.ts +17 -3
- package/src/observability/event-bus.ts +51 -56
- package/src/observability/event-to-metric.ts +117 -15
- package/src/observability/exporters/otlp-exporter.ts +56 -17
- package/src/observability/exporters/prometheus-exporter.ts +1 -1
- package/src/observability/metric-registry.ts +16 -4
- package/src/observability/metric-retention.ts +8 -2
- package/src/observability/metric-sink.ts +11 -3
- package/src/observability/metrics-primitives.ts +42 -9
- package/src/plugins/plugin-define.ts +2 -2
- package/src/plugins/plugin-registry.ts +25 -25
- package/src/plugins/plugins/index.ts +1 -1
- package/src/plugins/plugins/nextjs.ts +16 -16
- package/src/plugins/plugins/vite.ts +7 -11
- package/src/plugins/plugins/vitest.ts +6 -11
- package/src/runtime/adaptive-plan.ts +225 -40
- package/src/runtime/agent-control.ts +58 -15
- package/src/runtime/agent-memory.ts +15 -9
- package/src/runtime/agent-observability.ts +10 -2
- package/src/runtime/anchor-manager.ts +27 -25
- package/src/runtime/async-marker.ts +7 -1
- package/src/runtime/async-runner.ts +39 -24
- package/src/runtime/auto-summarize.ts +7 -13
- package/src/runtime/background-runner.ts +189 -251
- package/src/runtime/batch-barrier.ts +18 -16
- package/src/runtime/cancellation-token.ts +16 -6
- package/src/runtime/cancellation.ts +46 -8
- package/src/runtime/capability-inventory.ts +6 -4
- package/src/runtime/chain-parser.ts +44 -11
- package/src/runtime/chain-runner.ts +63 -69
- package/src/runtime/checkpoint.ts +12 -56
- package/src/runtime/child-pi.ts +344 -79
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +71 -26
- package/src/runtime/compact-stages/index.ts +15 -5
- package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
- package/src/runtime/compact-stages/truncation-stage.ts +4 -1
- package/src/runtime/compaction-summary.ts +17 -10
- package/src/runtime/completion-guard.ts +33 -16
- package/src/runtime/crash-classification.ts +28 -7
- package/src/runtime/crash-recovery.ts +177 -37
- package/src/runtime/crew-agent-records.ts +145 -47
- package/src/runtime/crew-hooks.ts +9 -24
- package/src/runtime/cross-extension-rpc.ts +25 -23
- package/src/runtime/custom-tools/irc-tool.ts +43 -15
- package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
- package/src/runtime/delivery-coordinator.ts +34 -7
- package/src/runtime/delta-conflict.ts +9 -21
- package/src/runtime/deterministic-ast.ts +1 -1
- package/src/runtime/diagnostic-export.ts +53 -20
- package/src/runtime/direct-run.ts +12 -2
- package/src/runtime/dwf-state-store.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +187 -59
- package/src/runtime/dynamic-workflow-runner.ts +42 -20
- package/src/runtime/effectiveness.ts +16 -8
- package/src/runtime/errors/crew-errors.ts +7 -11
- package/src/runtime/event-stream-bridge.ts +9 -3
- package/src/runtime/foreground-control.ts +54 -14
- package/src/runtime/foreground-watchdog.ts +9 -6
- package/src/runtime/goal-achievement.ts +27 -10
- package/src/runtime/goal-evaluator.ts +54 -19
- package/src/runtime/goal-loop-runner.ts +280 -99
- package/src/runtime/goal-state-store.ts +27 -17
- package/src/runtime/green-contract.ts +11 -2
- package/src/runtime/group-join.ts +64 -31
- package/src/runtime/handoff-manager.ts +37 -42
- package/src/runtime/heartbeat-gradient.ts +10 -2
- package/src/runtime/heartbeat-watcher.ts +32 -6
- package/src/runtime/hidden-handoff.ts +13 -31
- package/src/runtime/important-line-classifier.ts +12 -2
- package/src/runtime/iteration-hooks.ts +25 -15
- package/src/runtime/live-agent-control.ts +55 -7
- package/src/runtime/live-agent-manager.ts +130 -27
- package/src/runtime/live-control-realtime.ts +23 -3
- package/src/runtime/live-irc.ts +4 -1
- package/src/runtime/live-session-health.ts +12 -4
- package/src/runtime/live-session-runtime.ts +379 -117
- package/src/runtime/manifest-cache.ts +28 -12
- package/src/runtime/mcp-proxy.ts +10 -18
- package/src/runtime/metric-parser.ts +1 -5
- package/src/runtime/model-fallback.ts +55 -18
- package/src/runtime/model-resolver.ts +2 -6
- package/src/runtime/model-scope.ts +19 -3
- package/src/runtime/notebook-helpers.ts +60 -62
- package/src/runtime/orphan-worker-registry.ts +37 -26
- package/src/runtime/output-validator.ts +18 -3
- package/src/runtime/overflow-recovery.ts +5 -4
- package/src/runtime/parallel-research.ts +29 -5
- package/src/runtime/parallel-utils.ts +9 -11
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +12 -17
- package/src/runtime/per-write-validator.ts +1 -3
- package/src/runtime/phase-tracker.ts +342 -330
- package/src/runtime/pi-args.ts +26 -8
- package/src/runtime/pi-json-output.ts +1 -1
- package/src/runtime/pi-spawn.ts +30 -19
- package/src/runtime/pipeline-runner.ts +56 -55
- package/src/runtime/plan-templates.ts +5 -6
- package/src/runtime/policy-engine.ts +43 -7
- package/src/runtime/post-checks.ts +12 -4
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-lifecycle.ts +20 -10
- package/src/runtime/process-status.ts +16 -5
- package/src/runtime/progress-event-coalescer.ts +2 -1
- package/src/runtime/progress-tracker.ts +103 -103
- package/src/runtime/prose-compressor.ts +9 -11
- package/src/runtime/recovery-recipes.ts +118 -24
- package/src/runtime/replace.ts +25 -10
- package/src/runtime/resilient-edit.ts +10 -25
- package/src/runtime/result-extractor.ts +2 -6
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +27 -51
- package/src/runtime/role-permission.ts +6 -1
- package/src/runtime/run-coalesced-task-group.ts +256 -0
- package/src/runtime/run-drift.ts +14 -15
- package/src/runtime/run-tracker.ts +6 -28
- package/src/runtime/runtime-policy.ts +1 -6
- package/src/runtime/runtime-resolver.ts +80 -15
- package/src/runtime/scheduler.ts +47 -18
- package/src/runtime/semaphore.ts +5 -7
- package/src/runtime/sensitive-paths.ts +2 -1
- package/src/runtime/session-usage.ts +1 -1
- package/src/runtime/settings-store.ts +16 -12
- package/src/runtime/sidechain-output.ts +6 -2
- package/src/runtime/single-agent-compose.ts +1 -4
- package/src/runtime/skill-effectiveness.ts +29 -97
- package/src/runtime/skill-instructions.ts +40 -83
- package/src/runtime/stale-reconciler.ts +73 -197
- package/src/runtime/stream-preview.ts +1 -1
- package/src/runtime/streaming-output.ts +1 -1
- package/src/runtime/subagent-manager.ts +44 -169
- package/src/runtime/subprocess-tool-registry.ts +4 -1
- package/src/runtime/supervisor-contact.ts +10 -5
- package/src/runtime/task-display.ts +19 -5
- package/src/runtime/task-graph-scheduler.ts +95 -21
- package/src/runtime/task-graph.ts +5 -11
- package/src/runtime/task-health.ts +54 -47
- package/src/runtime/task-id.ts +12 -19
- package/src/runtime/task-output-context.ts +265 -81
- package/src/runtime/task-packet.ts +12 -20
- package/src/runtime/task-quality.ts +6 -14
- package/src/runtime/task-runner/context-retrieval.ts +4 -13
- package/src/runtime/task-runner/live-executor.ts +114 -36
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/progress.ts +50 -12
- package/src/runtime/task-runner/prompt-builder.ts +31 -7
- package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
- package/src/runtime/task-runner/result-utils.ts +3 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +27 -8
- package/src/runtime/task-runner/state-helpers.ts +50 -14
- package/src/runtime/task-runner/tail-read.ts +2 -6
- package/src/runtime/task-runner.ts +180 -326
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +456 -974
- package/src/runtime/tool-output-pruner.ts +6 -9
- package/src/runtime/tool-progress.ts +11 -21
- package/src/runtime/verification-gates.ts +33 -24
- package/src/runtime/verification-integrity.ts +2 -7
- package/src/runtime/verification-worktree.ts +62 -12
- package/src/runtime/worker-heartbeat.ts +5 -1
- package/src/runtime/worker-startup.ts +29 -5
- package/src/runtime/workflow-state.ts +17 -9
- package/src/runtime/workspace-lock.ts +30 -35
- package/src/runtime/workspace-tree.ts +20 -32
- package/src/runtime/yield-handler.ts +43 -9
- package/src/runtime/zombie-scanner.ts +5 -4
- package/src/schema/config-schema.ts +266 -172
- package/src/schema/team-tool-schema.ts +29 -74
- package/src/schema/validation-types.ts +25 -17
- package/src/skills/discover-skills.ts +35 -10
- package/src/skills/skill-templates.ts +109 -27
- package/src/skills/validate.ts +26 -8
- package/src/state/active-run-registry.ts +87 -28
- package/src/state/artifact-store.ts +9 -5
- package/src/state/atomic-write-v2.ts +85 -63
- package/src/state/atomic-write.ts +106 -27
- package/src/state/blob-store.ts +47 -20
- package/src/state/contracts.ts +20 -5
- package/src/state/crew-init.ts +5 -22
- package/src/state/decision-ledger.ts +19 -73
- package/src/state/event-log-rotation.ts +41 -15
- package/src/state/event-log.ts +168 -53
- package/src/state/event-reconstructor.ts +11 -1
- package/src/state/gitignore-manager.ts +2 -8
- package/src/state/health-store.ts +57 -57
- package/src/state/hook-instinct-bridge.ts +1 -1
- package/src/state/hook-integrations.ts +1 -1
- package/src/state/instinct-store.ts +17 -5
- package/src/state/jsonl-writer.ts +1 -1
- package/src/state/locks.ts +23 -9
- package/src/state/mailbox.ts +151 -28
- package/src/state/observation-store.ts +20 -14
- package/src/state/run-cache.ts +142 -135
- package/src/state/run-graph.ts +6 -15
- package/src/state/run-metrics.ts +5 -18
- package/src/state/schedule.ts +15 -9
- package/src/state/state-store.ts +137 -47
- package/src/state/task-claims.ts +12 -2
- package/src/state/tiered-eval.ts +52 -43
- package/src/state/types-eval.ts +2 -2
- package/src/state/types.ts +24 -20
- package/src/state/usage.ts +20 -4
- package/src/state/worker-atomic-writer.ts +6 -3
- package/src/subagents/index.ts +2 -2
- package/src/teams/discover-teams.ts +21 -6
- package/src/tools/safe-bash-extension.ts +5 -6
- package/src/tools/safe-bash.ts +10 -11
- package/src/types/new-api-types.ts +6 -10
- package/src/ui/agent-management-overlay.ts +52 -40
- package/src/ui/card-colors.ts +13 -4
- package/src/ui/crew-footer.ts +8 -7
- package/src/ui/crew-select-list.ts +1 -1
- package/src/ui/dashboard-panes/agents-pane.ts +41 -25
- package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
- package/src/ui/dashboard-panes/capability-pane.ts +32 -15
- package/src/ui/dashboard-panes/health-pane.ts +2 -1
- package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
- package/src/ui/dashboard-panes/progress-pane.ts +12 -9
- package/src/ui/heartbeat-aggregator.ts +15 -4
- package/src/ui/keybinding-map.ts +40 -7
- package/src/ui/live-conversation-overlay.ts +36 -20
- package/src/ui/live-duration.ts +1 -4
- package/src/ui/live-run-sidebar.ts +88 -25
- package/src/ui/loaders.ts +2 -8
- package/src/ui/mascot.ts +20 -36
- package/src/ui/overlays/agent-picker-overlay.ts +11 -3
- package/src/ui/overlays/confirm-overlay.ts +4 -2
- package/src/ui/overlays/help-overlay.ts +25 -14
- package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
- package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
- package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
- package/src/ui/pi-ui-compat.ts +7 -7
- package/src/ui/powerbar-publisher.ts +120 -42
- package/src/ui/render-diff.ts +10 -3
- package/src/ui/render-scheduler.ts +12 -4
- package/src/ui/run-action-dispatcher.ts +81 -18
- package/src/ui/run-dashboard.ts +172 -76
- package/src/ui/run-event-bus.ts +59 -37
- package/src/ui/run-snapshot-cache.ts +276 -86
- package/src/ui/settings-overlay.ts +361 -74
- package/src/ui/status-colors.ts +12 -1
- package/src/ui/syntax-highlight.ts +1 -1
- package/src/ui/terminal-status.ts +1 -3
- package/src/ui/theme-adapter.ts +16 -14
- package/src/ui/theme-discovery.ts +13 -2
- package/src/ui/tool-progress-formatter.ts +7 -7
- package/src/ui/tool-render.ts +128 -56
- package/src/ui/tool-renderers/brief-mode.ts +45 -27
- package/src/ui/tool-renderers/index.ts +109 -43
- package/src/ui/transcript-cache.ts +32 -6
- package/src/ui/transcript-entries.ts +25 -23
- package/src/ui/transcript-viewer.ts +78 -29
- package/src/ui/widget/index.ts +99 -40
- package/src/ui/widget/widget-formatters.ts +13 -6
- package/src/ui/widget/widget-model.ts +19 -8
- package/src/ui/widget/widget-renderer.ts +23 -13
- package/src/ui/widget/widget-types.ts +1 -1
- package/src/utils/bm25-search.ts +199 -199
- package/src/utils/conflict-detect.ts +22 -21
- package/src/utils/env-filter.ts +14 -7
- package/src/utils/file-coalescer.ts +5 -1
- package/src/utils/fingerprint.ts +3 -6
- package/src/utils/frontmatter.ts +3 -1
- package/src/utils/fs-watch.ts +2 -6
- package/src/utils/gh-protocol.ts +119 -44
- package/src/utils/git.ts +13 -15
- package/src/utils/guards.ts +2 -5
- package/src/utils/ids.ts +9 -2
- package/src/utils/incremental-reader.ts +14 -3
- package/src/utils/internal-error.ts +2 -1
- package/src/utils/names.ts +12 -3
- package/src/utils/paths.ts +49 -5
- package/src/utils/project-detector.ts +2 -2
- package/src/utils/redaction.ts +29 -19
- package/src/utils/resolve-shell.ts +9 -7
- package/src/utils/run-watcher-registry.ts +19 -31
- package/src/utils/safe-paths.ts +46 -29
- package/src/utils/scan-cache.ts +9 -2
- package/src/utils/session-utils.ts +2 -4
- package/src/utils/sleep.ts +12 -5
- package/src/utils/sse-parser.ts +5 -17
- package/src/utils/visual.ts +52 -32
- package/src/workflows/discover-workflows.ts +41 -109
- package/src/workflows/intermediate-store.ts +5 -21
- package/src/workflows/preflight-validator.ts +6 -33
- package/src/workflows/topology-analyzer.ts +5 -21
- package/src/workflows/workflow-config.ts +7 -6
- package/src/worktree/branch-freshness.ts +66 -8
- package/src/worktree/cleanup.ts +130 -20
- package/src/worktree/worktree-manager.ts +212 -69
- package/skills/artifact-analysis-loop/SKILL.md +0 -303
- package/skills/detection-pipeline-design/SKILL.md +0 -286
- package/skills/hunting-investigation-loop/SKILL.md +0 -402
- package/skills/incident-playbook-construction/SKILL.md +0 -384
- package/skills/security-review/SKILL.md +0 -561
- package/skills/threat-hypothesis-framework/SKILL.md +0 -176
- package/skills/ui-render-performance/SKILL.md +0 -58
- /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
- /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
- /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
- /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
- /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
- /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
- /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
- /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
- /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
- /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
- /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
- /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
- /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
- /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
# atomic-write-v2 migration plan
|
|
2
|
+
|
|
3
|
+
> Status: **PROPOSED** — decision RECOMMEND MIGRATE (staged, feature-flagged).
|
|
4
|
+
> Source: performance review `docs/perf/performance-review-2026-07.md` (findings F3–F6).
|
|
5
|
+
> Scope: replace the standalone-function module `src/state/atomic-write.ts`
|
|
6
|
+
> with the class-based `src/state/atomic-write-v2.ts` (`AtomicWriter`) over
|
|
7
|
+
> three staged phases behind a feature flag.
|
|
8
|
+
|
|
9
|
+
## Table of contents
|
|
10
|
+
|
|
11
|
+
- [1. Why migrate](#1-why-migrate)
|
|
12
|
+
- [2. API differences](#2-api-differences)
|
|
13
|
+
- [3. Migration phases](#3-migration-phases)
|
|
14
|
+
- [Phase 1 — dual-write (feature flag)](#phase-1--dual-write-feature-flag)
|
|
15
|
+
- [Phase 2 — switch default](#phase-2--switch-default)
|
|
16
|
+
- [Phase 3 — deprecate v1](#phase-3--deprecate-v1)
|
|
17
|
+
- [4. Risks and mitigations](#4-risks-and-mitigations)
|
|
18
|
+
- [5. Effort estimate](#5-effort-estimate)
|
|
19
|
+
- [6. Decision](#6-decision)
|
|
20
|
+
- [7. Appendix — call-site inventory](#7-appendix--call-site-inventory)
|
|
21
|
+
|
|
22
|
+
## 1. Why migrate
|
|
23
|
+
|
|
24
|
+
The performance review (F3, F4, F6) traced a write-path regression to commit
|
|
25
|
+
`13f4490`. That commit hardened `atomicWriteFile` against a CI-flake stale-read
|
|
26
|
+
race (the `mailbox-replay` intermittent failure) by adding **two** `fsync`
|
|
27
|
+
calls per synchronous write:
|
|
28
|
+
|
|
29
|
+
1. **Data fsync** — `fs.fsyncSync(fd)` on the temp file before rename
|
|
30
|
+
(`atomic-write.ts` ~line 287, in the `atomicWriteFile` write body). This
|
|
31
|
+
forces the written bytes out of the page cache to disk so a subsequent
|
|
32
|
+
`readFileSync` after the rename always sees the new content.
|
|
33
|
+
2. **Directory fsync** — `fs.fsyncSync(dirFd)` on the parent directory after
|
|
34
|
+
the rename (`atomic-write.ts` ~lines 307–309, POSIX-only, skipped on
|
|
35
|
+
Windows). This makes the directory-entry update (the rename) durable so a
|
|
36
|
+
subsequent `open()` cannot race and observe `ENOENT` or stale content on a
|
|
37
|
+
high-I/O CI runner.
|
|
38
|
+
|
|
39
|
+
The dir-fsync is the expensive half. `persistSingleTaskUpdate` calls the write
|
|
40
|
+
path ~5 times per task; at 2 fsync per call that is roughly **+10 fsync per
|
|
41
|
+
task**, and directory fsync is especially costly on Windows and on
|
|
42
|
+
network/overlay filesystems. The review also had to bump the CI timeout
|
|
43
|
+
600s → 900s (commit `9bbeb45`) partly to absorb this cost.
|
|
44
|
+
|
|
45
|
+
`atomic-write-v2.ts` (the `AtomicWriter` class) keeps only the **data fsync**,
|
|
46
|
+
and even that is **best-effort** — it is wrapped in a `try/catch` that swallows
|
|
47
|
+
the error (`atomic-write-v2.ts` ~line 30 in `writeSync`, and `fd.sync()` in
|
|
48
|
+
`writeAsync`). It performs **no directory fsync at all**.
|
|
49
|
+
|
|
50
|
+
### Quantified difference
|
|
51
|
+
|
|
52
|
+
| Cost per write | atomic-write.ts (v1) | atomic-write-v2.ts (v2) |
|
|
53
|
+
| ---------------------- | ------------------------------------- | ------------------------------- |
|
|
54
|
+
| Data fsync | 1 (mandatory, throws on failure) | 1 (best-effort, swallowed) |
|
|
55
|
+
| Directory fsync | 1 (POSIX only) | 0 |
|
|
56
|
+
| `isSymlinkSafePath` | 2 full ancestor walks (pre + pre-rename) | 0 |
|
|
57
|
+
| `lstat` syscalls | ~10+ per level × 2 for a deep path | 0 |
|
|
58
|
+
| Rename mechanism | `link()` + `unlink()` (+ retry/jitter) | `rename()` (single syscall) |
|
|
59
|
+
|
|
60
|
+
The headline is roughly **2× fewer fsync operations per call** (drop the dir
|
|
61
|
+
fsync, keep data fsync), plus the elimination of two full ancestor-walk
|
|
62
|
+
symlink scans (F5) and the `link()`+`unlink()` pair in favor of a single
|
|
63
|
+
`rename()`. On a deep state-store path (`.crew/state/runs/{runId}/…`) the v1
|
|
64
|
+
path issues ~20+ `lstat` calls per write for symlink validation alone; v2
|
|
65
|
+
issues none. The net expected win on the hot `persistSingleTaskUpdate` path is
|
|
66
|
+
approximately halving the per-call durability cost, with a larger relative win
|
|
67
|
+
on syscall count.
|
|
68
|
+
|
|
69
|
+
The trade-off is explicit: **v2 loses the dir-fsync durability guarantee.** On
|
|
70
|
+
journaled Linux filesystems (ext4 with `data=ordered`, xfs) the rename is still
|
|
71
|
+
crash-atomic, but a power loss in the sub-millisecond window between write and
|
|
72
|
+
directory-entry flush can leave the *old* content visible after reboot. On
|
|
73
|
+
tmpfs this window is effectively the whole lifetime of the entry (no
|
|
74
|
+
durability at all). This is the guarantee the `13f4490` commit was added to
|
|
75
|
+
provide, so dropping it must be a conscious, documented decision (see
|
|
76
|
+
[risks](#4-risks-and-mitigations)).
|
|
77
|
+
|
|
78
|
+
## 2. API differences
|
|
79
|
+
|
|
80
|
+
The two modules are shaped very differently, so migration is not a
|
|
81
|
+
drop-in symbol rename — callers change from free functions to a constructed
|
|
82
|
+
instance.
|
|
83
|
+
|
|
84
|
+
### v1 — `atomic-write.ts` (module of standalone functions)
|
|
85
|
+
|
|
86
|
+
- `atomicWriteFile(filePath, content, expectedHash?)`
|
|
87
|
+
- `atomicWriteFileAsync(filePath, content)`
|
|
88
|
+
- `atomicWriteJson(filePath, value)`
|
|
89
|
+
- `atomicWriteJsonAsync(filePath, value)`
|
|
90
|
+
- `atomicWriteJsonCoalesced(filePath, value, coalesceMs?)` — 50 ms coalescing buffer
|
|
91
|
+
- `flushPendingAtomicWrites()` — flush the coalesce buffer
|
|
92
|
+
- `isSymlinkSafePath(filePath)` — exported symlink-safety guard
|
|
93
|
+
- `renameWithRetry(...)` / `renameWithRetryAsync(...)` — exported rename helpers with jitter
|
|
94
|
+
- `readJsonFile(filePath)` — tolerant JSON reader
|
|
95
|
+
|
|
96
|
+
Behavioral characteristics:
|
|
97
|
+
|
|
98
|
+
- **Symlink safety**: explicit `isSymlinkSafePath` check both **before open**
|
|
99
|
+
and **again immediately before rename** (TOCTOU re-check), plus a
|
|
100
|
+
post-rename `lstat` guard on failure.
|
|
101
|
+
- **Rename**: `link()` + `unlink()` (`renameWithLinkSync` /
|
|
102
|
+
`renameWithLinkAsync`) so the destination symlink is *not* followed; Windows
|
|
103
|
+
falls back to `renameSync`/`MoveFileEx`. Retries with exponential backoff and
|
|
104
|
+
±20% jitter.
|
|
105
|
+
- **Durability**: mandatory data fsync (throws on failure) + POSIX directory
|
|
106
|
+
fsync.
|
|
107
|
+
- **Tmp naming**: `${filePath}.${randomUUID()}.tmp`.
|
|
108
|
+
- **Coalescing**: a per-path buffer with generation counters, retry/backoff,
|
|
109
|
+
and process `exit`/`SIGTERM`/`SIGINT` auto-flush hooks.
|
|
110
|
+
- **Optional worker offload**: `atomicWriteFileAsync` can dispatch to a worker
|
|
111
|
+
thread when `PI_CREW_WORKER_ATOMIC_WRITER=1`.
|
|
112
|
+
|
|
113
|
+
### v2 — `atomic-write-v2.ts` (`AtomicWriter` class)
|
|
114
|
+
|
|
115
|
+
- `new AtomicWriter(baseDir)` — constructed with a base directory
|
|
116
|
+
- `writeSync(targetPath, content)`
|
|
117
|
+
- `writeAsync(targetPath, content)`
|
|
118
|
+
- `writeJsonSync(targetPath, value)`
|
|
119
|
+
- `writeJsonAsync(targetPath, value)`
|
|
120
|
+
- private `.gitignore`-on-first-use per directory (writes `*\n` the first time
|
|
121
|
+
a directory is touched)
|
|
122
|
+
|
|
123
|
+
Behavioral characteristics:
|
|
124
|
+
|
|
125
|
+
- **Symlink safety**: **none** — no `isSymlinkSafePath`, no pre/post checks.
|
|
126
|
+
- **Rename**: plain `rename()` (POSIX-atomic but **symlink-following**), single
|
|
127
|
+
syscall, no retry/jitter loop.
|
|
128
|
+
- **Durability**: best-effort data fsync (swallowed), **no directory fsync**.
|
|
129
|
+
- **Tmp naming**: `${targetPath}.${randomUUID()}.tmp` (same scheme as v1).
|
|
130
|
+
- **Coalescing**: none (no buffer, no flush hooks).
|
|
131
|
+
- **Reader**: none (no `readJsonFile` equivalent).
|
|
132
|
+
|
|
133
|
+
### Feature-gap summary (what v2 does NOT have yet)
|
|
134
|
+
|
|
135
|
+
| Capability | v1 | v2 | Notes |
|
|
136
|
+
| ---------------------------------- | --- | --- | -------------------------------------------------- |
|
|
137
|
+
| Symlink-safe rename | ✅ | ❌ | v2 uses `rename()` which follows symlinks |
|
|
138
|
+
| `isSymlinkSafePath` export | ✅ | ❌ | consumed elsewhere — must stay available |
|
|
139
|
+
| Coalesced writes + flush hooks | ✅ | ❌ | required by `saveRunTasksCoalesced` path (F4) |
|
|
140
|
+
| `readJsonFile` | ✅ | ❌ | tolerant reader used across state-store |
|
|
141
|
+
| Retry/backoff on contended rename | ✅ | ❌ | v2 throws on first `rename()` failure |
|
|
142
|
+
| Windows short/long-name handling | ✅ | ❌ | v1 has explicit `MoveFileEx` fallback |
|
|
143
|
+
| Directory fsync | ✅ | ❌ | the durability guarantee being intentionally dropped |
|
|
144
|
+
| `.gitignore` on first use | ❌ | ✅ | v2 convenience; v1 leaves this to callers |
|
|
145
|
+
|
|
146
|
+
**Implication:** a straight swap is not viable until v2 grows (or the
|
|
147
|
+
dispatcher re-provides) the coalescing buffer, the tolerant reader, the retry
|
|
148
|
+
loop, and an opt-in symlink guard. The phased plan below keeps v1 as the source
|
|
149
|
+
of those capabilities during transition rather than porting them all up front.
|
|
150
|
+
|
|
151
|
+
## 3. Migration phases
|
|
152
|
+
|
|
153
|
+
### Phase 1 — dual-write (feature flag)
|
|
154
|
+
|
|
155
|
+
Estimated ~1–2 days.
|
|
156
|
+
|
|
157
|
+
- Introduce an environment feature flag `PI_CREW_ATOMIC_WRITER` with values
|
|
158
|
+
`v1` (default) and `v2`.
|
|
159
|
+
- Add a thin adapter module `src/state/atomic-write-dispatcher.ts` that exposes
|
|
160
|
+
the v1 free-function surface (`atomicWriteFile`, `atomicWriteJson`, and their
|
|
161
|
+
async variants) and routes each call to either the v1 functions or a shared
|
|
162
|
+
`AtomicWriter` instance based on the flag. Capabilities v2 lacks
|
|
163
|
+
(`atomicWriteJsonCoalesced`, `flushPendingAtomicWrites`, `isSymlinkSafePath`,
|
|
164
|
+
`readJsonFile`) continue to delegate to v1 unconditionally in this phase.
|
|
165
|
+
- Keep `atomic-write.ts` as the default; nothing changes for users who do not
|
|
166
|
+
set the flag.
|
|
167
|
+
- Add a benchmark test that writes N files through both paths under **tmpfs**
|
|
168
|
+
and **ext4** and asserts the fsync-count / wall-clock delta matches the
|
|
169
|
+
~2× prediction. Land it under `test/bench/` (guarded so it does not run in
|
|
170
|
+
the default `test:unit` gate).
|
|
171
|
+
|
|
172
|
+
Exit criteria: flag flips implementation with zero behavior change for default
|
|
173
|
+
users; benchmark confirms the predicted delta.
|
|
174
|
+
|
|
175
|
+
### Phase 2 — switch default
|
|
176
|
+
|
|
177
|
+
Estimated ~1–2 days.
|
|
178
|
+
|
|
179
|
+
- Flip the dispatcher default to `v2`; users can opt back to the old path with
|
|
180
|
+
`PI_CREW_ATOMIC_WRITER=v1`.
|
|
181
|
+
- Route direct callers of `atomicWriteFile` / `atomicWriteJson` (in
|
|
182
|
+
`extension/team-tool.ts`, `state-store.ts`, and any other direct importers)
|
|
183
|
+
through the dispatcher rather than importing `atomic-write.ts` directly.
|
|
184
|
+
- Before flipping, back-fill v2 (or the dispatcher wrapper around v2) with the
|
|
185
|
+
currently-missing durability/safety features that production relies on:
|
|
186
|
+
retry-on-contended-rename, an **opt-in** symlink guard for
|
|
187
|
+
`userPiRoot()`-adjacent writes, and either the coalescing buffer or continued
|
|
188
|
+
delegation to v1 for the coalesced path.
|
|
189
|
+
- Monitor for one full release cycle. Watch for stale-read CI flakes (the exact
|
|
190
|
+
failure `13f4490` fixed) — if they resurface, the dir-fsync drop is the prime
|
|
191
|
+
suspect and the flag provides an instant rollback.
|
|
192
|
+
|
|
193
|
+
Exit criteria: one clean release cycle on the v2 default with no stale-read or
|
|
194
|
+
symlink regressions reported.
|
|
195
|
+
|
|
196
|
+
### Phase 3 — deprecate v1
|
|
197
|
+
|
|
198
|
+
Estimated ~1 day.
|
|
199
|
+
|
|
200
|
+
- Remove `atomic-write.ts` and the `v1` flag branch (keep any still-needed
|
|
201
|
+
utilities — notably the symlink-safe `link()`+`unlink()` helper and
|
|
202
|
+
`isSymlinkSafePath` — either in the renamed module or a small
|
|
203
|
+
`symlink-safety.ts`).
|
|
204
|
+
- Rename `atomic-write-v2.ts` → `atomic-write.ts` and update imports.
|
|
205
|
+
- Add a `CHANGELOG.md` entry under a **minor** version bump documenting the
|
|
206
|
+
durability trade-off (no directory fsync by default) and the
|
|
207
|
+
`PI_CREW_ATOMIC_WRITER` flag removal.
|
|
208
|
+
|
|
209
|
+
Exit criteria: single implementation, no flag, green CI, CHANGELOG documents
|
|
210
|
+
the durability change.
|
|
211
|
+
|
|
212
|
+
## 4. Risks and mitigations
|
|
213
|
+
|
|
214
|
+
### Drop of strict symlink safety (security)
|
|
215
|
+
|
|
216
|
+
v2 performs **no** `isSymlinkSafePath` check and uses `rename()`, which follows
|
|
217
|
+
a symlink at the destination. An attacker who can plant a symlink inside
|
|
218
|
+
`userPiRoot()` (or an ancestor of a write target) could redirect a write to an
|
|
219
|
+
arbitrary path.
|
|
220
|
+
|
|
221
|
+
Mitigations:
|
|
222
|
+
|
|
223
|
+
1. v2 callers must pre-validate every target path via the `paths.ts` helpers so
|
|
224
|
+
writes are confined to the user's own pi-crew state tree.
|
|
225
|
+
2. Keep the v1 `link()`+`unlink()` rename helper and `isSymlinkSafePath` as
|
|
226
|
+
reusable utilities even after the module rename, and offer them as an opt-in
|
|
227
|
+
"safe rename" mode in the dispatcher for `userPiRoot()`-adjacent writes.
|
|
228
|
+
3. Add a CI test that plants a symlink inside the `.pi/agent/pi-crew/` tree and
|
|
229
|
+
asserts the writer refuses (or does not follow) it.
|
|
230
|
+
|
|
231
|
+
### Lockfile / tmp collision risk
|
|
232
|
+
|
|
233
|
+
Both implementations name temp files `${target}.${randomUUID()}.tmp`. UUIDv4
|
|
234
|
+
collision probability is astronomically low (documented here for completeness).
|
|
235
|
+
The larger concern is the inode-scan cost of many `.tmp` siblings in a large
|
|
236
|
+
directory. Recommend writing tmp files into a per-process subdir
|
|
237
|
+
(`<baseDir>/.tmp/<pid>/`) to bound directory size and simplify crash cleanup.
|
|
238
|
+
|
|
239
|
+
### Crash-durability regression
|
|
240
|
+
|
|
241
|
+
v2's missing directory fsync means a power loss in the window between the data
|
|
242
|
+
write and the directory-entry flush can leave the **old** content visible after
|
|
243
|
+
reboot. This is rare on journaled filesystems (ext4/xfs) and common on tmpfs.
|
|
244
|
+
|
|
245
|
+
Mitigations:
|
|
246
|
+
|
|
247
|
+
1. Document the trade-off in the v2 module docblock and in `CHANGELOG.md`.
|
|
248
|
+
2. Keep the flag so at-risk deployments (non-journaled FS, tmpfs-backed state)
|
|
249
|
+
can pin `v1`.
|
|
250
|
+
3. Optionally expose a per-call `durable: true` option in the dispatcher that
|
|
251
|
+
re-enables the directory fsync for the small set of writes that truly need
|
|
252
|
+
it (e.g. terminal manifest writes), so the common path stays fast while the
|
|
253
|
+
critical path stays durable.
|
|
254
|
+
|
|
255
|
+
## 5. Effort estimate
|
|
256
|
+
|
|
257
|
+
**Size: M (3–5 days.)**
|
|
258
|
+
|
|
259
|
+
| Work item | Estimate |
|
|
260
|
+
| ------------------------------------------ | -------- |
|
|
261
|
+
| Phase 1 — flag + dispatcher + benchmark | ~1–2 days |
|
|
262
|
+
| Phase 2 — feature back-fill + switch + soak | ~1–2 days |
|
|
263
|
+
| Phase 3 — deprecate + rename + CHANGELOG | ~1 day |
|
|
264
|
+
| Benchmark harness (tmpfs + ext4) | ~0.5 day |
|
|
265
|
+
| Docs / review pass | ~0.5 day |
|
|
266
|
+
|
|
267
|
+
## 6. Decision
|
|
268
|
+
|
|
269
|
+
**RECOMMEND MIGRATE** — staged, feature-flagged.
|
|
270
|
+
|
|
271
|
+
Justification:
|
|
272
|
+
|
|
273
|
+
- The write path is hot: `persistSingleTaskUpdate` calls it ~5×/task and the
|
|
274
|
+
state-store fires on the same 1 Hz idle / 6 Hz active cadence as
|
|
275
|
+
`loadConfig`. Halving the per-call durability cost (drop dir fsync, keep data
|
|
276
|
+
fsync) plus eliminating the double ancestor-walk symlink scan is a meaningful
|
|
277
|
+
and broadly-applied win.
|
|
278
|
+
- The durability risk from dropping the directory fsync is bounded: it only
|
|
279
|
+
matters on non-journaled/tmpfs filesystems and only within the sub-millisecond
|
|
280
|
+
on-disk write window. The `PI_CREW_ATOMIC_WRITER=v1` fallback lets at-risk
|
|
281
|
+
setups opt out.
|
|
282
|
+
- The symlink-safety regression is the sharpest edge and is mitigated by
|
|
283
|
+
`paths.ts` pre-validation, an opt-in safe-rename mode, and a CI symlink-plant
|
|
284
|
+
test — not by accepting v2 as-is.
|
|
285
|
+
|
|
286
|
+
Because the plan is staged behind a flag with an instant rollback, the blast
|
|
287
|
+
radius at each step is small and the exact CI flake that `13f4490` fixed remains
|
|
288
|
+
observable (and reversible) during the Phase 2 soak.
|
|
289
|
+
|
|
290
|
+
## 7. Appendix — call-site inventory
|
|
291
|
+
|
|
292
|
+
Before Phase 2, enumerate the direct importers to route through the dispatcher.
|
|
293
|
+
As of this plan, `atomic-write-v2.ts` has **0 importers** (confirmed by
|
|
294
|
+
`grep -rn "atomic-write-v2" src/`). The v1 surface is consumed by
|
|
295
|
+
`state-store.ts`, `extension/team-tool.ts`, and the coalesced/reader paths;
|
|
296
|
+
run `grep -rn "atomic-write\b" src/` at the start of Phase 2 to produce the
|
|
297
|
+
authoritative list, since call sites shift between now and then.
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Performance Review — pi-crew v0.9.17 (2026-07-02)
|
|
2
|
+
|
|
3
|
+
> Deep-dive review toàn bộ codebase (~434 file TS, ~82.7k dòng) tập trung vào hiệu năng.
|
|
4
|
+
> Mọi phát hiện đều có file:line cụ thể và đã được xác minh trực tiếp trên source.
|
|
5
|
+
> Bổ sung cho `docs/perf/final-report-2026-05.md` và `docs/optimization-plan.md` (các vòng tối ưu trước).
|
|
6
|
+
>
|
|
7
|
+
> **Cập nhật sau pull v0.9.17**: ban đầu review trên v0.9.16, sau đó user pull 40 commits mới
|
|
8
|
+
> (`1418fa7..22a8e72`, ~20k dòng thay đổi trong `src/`). Mọi phát hiện đã được re-verify trên
|
|
9
|
+
> code mới. Xem thêm [§5 — Delta v0.9.17](#5-delta-v0917--những-gì-đã-fix-và-regressed).
|
|
10
|
+
|
|
11
|
+
## 1. Đánh giá tổng quan
|
|
12
|
+
|
|
13
|
+
### Điểm mạnh (đã làm tốt)
|
|
14
|
+
|
|
15
|
+
| Hạng mục | Bằng chứng |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Render path zero-fs-IO | `register.ts:1744+` — renderTick chỉ đọc từ frame preload, không chạm disk |
|
|
18
|
+
| Snapshot cache 2 tầng (TTL 1.5s + stamp mtime/size) | `run-snapshot-cache.ts:21-25, 79-97` — tail-read 32KB, không bao giờ parse full events.jsonl |
|
|
19
|
+
| Event tail đọc theo byte-offset | `utils/incremental-reader.ts:23` — `readJsonlSince` dùng openSync/readSync từ offset |
|
|
20
|
+
| Concurrency có hard cap | `config/defaults.ts:46-56` — hardCap 8, per-workflow 2-4, enforced qua `mapConcurrent` |
|
|
21
|
+
| Capture stdout/stderr bounded | `child-pi.ts:53` — tail cap 512KiB, có backpressure soft watermark 256KiB |
|
|
22
|
+
| Timer nền đều `unref()` | auto-repair, temp-reconcile, notifier — không giữ event loop |
|
|
23
|
+
| Watcher bounded theo active runs | `register.ts:1690-1706` — reconcile watcher chỉ O(active runs) |
|
|
24
|
+
| Lazy import có kỷ luật | `// LAZY:` markers + `npm run check:lazy-imports` |
|
|
25
|
+
|
|
26
|
+
Kiến trúc 3 layer rõ ràng, defensive coding tốt (CAS, lock, terminal-state guard). Vấn đề hiệu năng chủ yếu KHÔNG nằm ở thuật toán mà ở **tần suất lặp lại của I/O đồng bộ nhỏ** trên hot path.
|
|
27
|
+
|
|
28
|
+
### Điểm yếu tổng quát
|
|
29
|
+
|
|
30
|
+
1. **State layer ghi-toàn-file + fsync per-event**: mỗi thay đổi status của 1 task ghi lại toàn bộ tasks.json (pretty-printed), mỗi event append kèm 1 fsync + 1 atomic write sidecar.
|
|
31
|
+
2. **Cache tồn tại nhưng tự vô hiệu hóa**: manifest cache bị global-generation invalidation, discovery cache TTL 500ms, config hoàn toàn không cache.
|
|
32
|
+
3. **Sync I/O chặn event loop**: `execFileSync` (git), `withRunLockSync` + `sleepSync`, `statSync` chuỗi — chạy trong extension host của Pi, chặn cả UI.
|
|
33
|
+
4. **Hạ tầng batching/coalescing đã viết nhưng không được dùng**: `appendEventBuffered`, `saveRunTasksCoalesced` đều 0 call site.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Phát hiện chi tiết theo layer
|
|
38
|
+
|
|
39
|
+
### 2.1 State layer
|
|
40
|
+
|
|
41
|
+
#### F1 — HIGH: Global generation counter phá cache của TẤT CẢ runs
|
|
42
|
+
- `src/state/state-store.ts:73, 125-128`
|
|
43
|
+
```ts
|
|
44
|
+
let manifestCacheGeneration = 0;
|
|
45
|
+
function invalidateRunCache(stateRoot: string): void {
|
|
46
|
+
manifestCache.delete(stateRoot);
|
|
47
|
+
manifestCacheGeneration++; // <-- vô hiệu hóa MỌI entry, không chỉ run này
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
- Cache hit yêu cầu `cached.generation === manifestCacheGeneration` (`:589, :685`). Mỗi `saveRunTasks`/`saveRunManifest` của BẤT KỲ run nào đều bump counter, nên khi có 1 run active, hit-rate của cache sụp về ~0. Mỗi miss = 2 statSync + 2 readFileSync + JSON.parse trong retry loop tối đa 5 vòng (`:617-631`).
|
|
51
|
+
- **Fix đề xuất**: generation counter per-stateRoot (`Map<string, number>`) thay vì global. Mtime/size check đã đủ chống spoof cho từng entry.
|
|
52
|
+
|
|
53
|
+
#### F2 — HIGH: `persistSingleTaskUpdate` — sync CAS loop tối đa 100 vòng, mỗi vòng đọc lại full state
|
|
54
|
+
- `src/runtime/task-runner/state-helpers.ts:28-110`, gọi tại `task-runner.ts:233, 424, 538, 617, 1347` (mỗi lần task đổi status/checkpoint)
|
|
55
|
+
```ts
|
|
56
|
+
return withRunLockSync(manifest, () => {
|
|
57
|
+
retryLoop: for (let attempt = 0; attempt < 100; attempt++) {
|
|
58
|
+
const latest = loadRunManifestById(manifest.cwd, manifest.runId)?.tasks ?? fallbackTasks;
|
|
59
|
+
// ... 3x statSync mtime check ...
|
|
60
|
+
```
|
|
61
|
+
- Toàn bộ sync, dưới `withRunLockSync` (poll bằng `sleepSync` chặn event loop). Kết hợp F1 (cache lạnh) nghĩa là mỗi checkpoint = lock + full read manifest.json + tasks.json + full rewrite tasks.json.
|
|
62
|
+
- **Fix đề xuất**: (a) đã cầm run lock thì mtime-CAS 3 lần stat là thừa — lock loại trừ writer khác cùng process-tree, chỉ cần 1 lần đọc + 1 lần ghi; (b) chuyển sang async variant; (c) giữ tasks in-memory per-run và chỉ ghi diff-triggered.
|
|
63
|
+
|
|
64
|
+
#### F3 — HIGH → PARTIALLY FIXED (v0.9.17): Mỗi event append = fsync + atomic-write sidecar `.seq`
|
|
65
|
+
- `src/state/event-log.ts:583-611` (sync path không đổi)
|
|
66
|
+
```ts
|
|
67
|
+
fs.appendFileSync(eventsPath, `${JSON.stringify(redactSecrets(fullEvent))}\n`, "utf-8");
|
|
68
|
+
const fd = fs.openSync(eventsPath, "r+");
|
|
69
|
+
fs.fsyncSync(fd); ...
|
|
70
|
+
persistSequence(eventsPath, seq); // full atomicWriteFile của .seq mỗi event
|
|
71
|
+
```
|
|
72
|
+
- **Đã fix một phần (v0.9.17)**: `appendEventBuffered` giờ đã có call site — `task.progress` trong
|
|
73
|
+
`task-runner.ts:395` và `team-runner.ts:1054` đi qua buffered path (coalesce 20ms, 1 lock acquire).
|
|
74
|
+
Bench trong code comment: p95 producer giảm từ ~13µs → ~0µs. Tuy nhiên:
|
|
75
|
+
- Các event terminal/vài event khác vẫn đi `appendEvent`/`appendEventAsync` → per-event fsync + `.seq` sidecar.
|
|
76
|
+
- `persistSequence` vẫn ghi sidecar per event (trong batch, gọi tuần tự trong lock nhưng mỗi event 1 lần).
|
|
77
|
+
- **Fix đề xuất còn lại**: chỉ fsync cho terminal events (`TERMINAL_EVENT_TYPES`); persist `.seq` theo batch
|
|
78
|
+
hoặc mỗi N events (recovery scan đã tự sửa được khi sidecar tụt hậu); mở rộng `appendEventBuffered`
|
|
79
|
+
cho thêm các event loại informational (`task.checkpoint`, `worker.lifecycle`, ...).
|
|
80
|
+
|
|
81
|
+
#### F4 — HIGH → REGRESSED (v0.9.17): tasks.json ghi lại toàn bộ, pretty-printed, mỗi thay đổi + fsync data+dir
|
|
82
|
+
- `state-store.ts:365-377` → `atomicWriteJson` dùng `JSON.stringify(value, null, 2)` (`atomic-write.ts:523`).
|
|
83
|
+
- Task graph chứa attempts, verification, usage, diagnostics per task → file lớn dần trong run dài; stringify + write cost tỉ lệ O(tổng state) cho MỖI thay đổi 1 field.
|
|
84
|
+
- **REGRESSED (v0.9.17, commit `13f4490`)**: `atomicWriteFile` giờ thêm `fs.fsyncSync(fd)` trên data file
|
|
85
|
+
(`atomic-write.ts:384`) **và** `fs.fsyncSync(dirFd)` trên parent directory (`:410`, POSIX only).
|
|
86
|
+
Mỗi `atomicWriteJson` call = write + fsync(data) + fsync(dir) + rename. Comment trong code ước lượng
|
|
87
|
+
"~1ms per write" nhưng thực tế trên Windows fsync đắt hơn nhiều; CI timeout đã phải bump 600s→900s
|
|
88
|
+
chính vì overhead này (`9bbeb45`). `persistSingleTaskUpdate` (F2) gọi `saveRunTasks` ~5 lần mỗi task,
|
|
89
|
+
mỗi lần giờ tốn thêm 2× fsync.
|
|
90
|
+
- `saveRunTasksCoalesced` (50ms debounce) đã có tại `state-store.ts:428` nhưng **vẫn 0 call site** (v0.9.17 xác nhận).
|
|
91
|
+
- **Fix đề xuất**: bỏ pretty-print cho tasks.json (giữ cho manifest nếu cần đọc tay), wire `saveRunTasksCoalesced`
|
|
92
|
+
vào checkpoint path (checkpoint là informational, mất 50ms cuối khi crash chấp nhận được vì đã có crash-recovery);
|
|
93
|
+
cân nhắc fsync chỉ trên terminal write (manifest final), không phải mỗi checkpoint.
|
|
94
|
+
|
|
95
|
+
#### F5 — MEDIUM: `isSymlinkSafePath` walk toàn bộ ancestor chain, chạy 2 lần mỗi atomic write
|
|
96
|
+
- `atomic-write.ts:43-56, 287, 359-361`: mỗi write = 2 × (lstatSync per path level đến root). Với path sâu điển hình `.crew/state/runs/{runId}/` là ~10+ lstat × 2 mỗi write, nhân với mọi finding ở trên.
|
|
97
|
+
- `withFileLockSync` còn gọi lại check này MỖI vòng retry (`locks.ts:319`).
|
|
98
|
+
- **Fix đề xuất**: cache kết quả validate theo (dirname, generation) trong process — thư mục cha của state root không đổi trong suốt run; chỉ re-validate khi ghi vào path mới.
|
|
99
|
+
|
|
100
|
+
#### F6 — HIGH → REGRESSED (v0.9.17): Mailbox — mỗi message = 2 lock + full rewrite delivery.json + lstat chain × 3 + fsync data+dir
|
|
101
|
+
- `src/state/mailbox.ts:380-430`: `mailboxFile(...)` tính lại 3 lần (mỗi lần re-run lstat validation), `writeDeliveryState` (`:353`) sort đến 10k entries + atomic rewrite toàn bộ pretty-printed delivery.json cho MỖI message.
|
|
102
|
+
- **REGRESSED (v0.9.17)**: `atomicWriteFile` giờ fsync data + parent dir (commit `13f4490`), nên mỗi delivery.json rewrite tốn thêm 2× fsync. CI flake mailbox-replay chính là lý do commit này tồn tại, nhưng代价 là mọi write path đều nặng hơn.
|
|
103
|
+
- Reads: `safeReadMailboxFile` (`:246`) readdirSync tìm archive MỖI lần đọc; `readAllMessages` (`:321`) O(tasks × dir entries); `updateMailboxMessageReply` (`:463`) quét mọi mailbox file, mỗi file full read + full rewrite.
|
|
104
|
+
- **Fix đề xuất**: delivery state chuyển sang append-only JSONL + compaction (giống events), hoặc ít nhất coalesce write; cache danh sách archive theo mtime của dir; chỉ fsync khi reply/terminal, không phải mỗi informational message.
|
|
105
|
+
|
|
106
|
+
#### F7 — MEDIUM/HIGH: active-run-registry O(N runs) full manifest parse mỗi register/unregister
|
|
107
|
+
- `active-run-registry.ts:249-344`: `filterAliveEntries` đọc + parse manifest.json của từng run, 2× existsSync + 2× symlink walk + `process.kill(pid,0)` per entry; `registerActiveRun` gọi 2 lần; `unregisterActiveRun` chạy trên MỖI terminal transition (`state-store.ts:508`). Registry còn ghi 2 định dạng (JSON + binary mirror) = 4 temp-file ops mỗi update (`:183`).
|
|
108
|
+
- **Fix đề xuất**: liveness chỉ cần pid probe + heartbeat stat, không cần parse manifest; bỏ dual-format write nếu binary mirror không còn consumer.
|
|
109
|
+
|
|
110
|
+
### 2.2 Runtime / process layer
|
|
111
|
+
|
|
112
|
+
#### F8 — HIGH: Worktree prep = 5-6 git subprocess `execFileSync` tuần tự + full checkout, chặn event loop
|
|
113
|
+
- `src/worktree/worktree-manager.ts:1,30,331-425`: mỗi task cần worktree chạy lần lượt `rev-parse --show-toplevel`, `status --porcelain` trên leader repo (O(repo size)), `worktree list --porcelain`, `worktree prune`, `rev-parse --verify`, `worktree add -b ... HEAD`. Tất cả `execFileSync` → dù tasks chạy "song song" qua `mapConcurrent`, phần chuẩn bị worktree bị serialize và chặn toàn bộ extension host (UI đơ theo).
|
|
114
|
+
- Reuse path vẫn tốn `worktree list` + `status` + leader `status` lần 2. Cleanup gọi lại `findGitRoot` 3 lần (tới 6 git spawn).
|
|
115
|
+
- **v0.9.17 note**: M6 coalesce (`coalesceMicroTasks`) giảm số spawn cho workflow có nhiều task nhỏ read-only (3 task → 1 child + 1 worktree), nhưng bản thân worktree prep vẫn sync. Khi M6 mở rộng sang mutating roles, vấn đề sẽ tái xuất.
|
|
116
|
+
- **Fix đề xuất**: (a) chuyển sang `execFile` async (promisify) — thay đổi cục bộ, tác động lớn nhất; (b) cache `findGitRoot`/`assertCleanLeader` per (repo, batch) — leader status chỉ cần check 1 lần mỗi batch chứ không phải mỗi task; (c) cân nhắc `git worktree add --no-checkout` + sparse checkout cho task chỉ đọc một phần repo.
|
|
117
|
+
|
|
118
|
+
#### F9 — HIGH: `ChildPiLineObserver` giữ mảng không giới hạn trong suốt run
|
|
119
|
+
- `child-pi.ts` (~:528): `rawTextEvents` push mọi fragment assistant-text KHÔNG cap ("RAW (uncapped)"), chỉ dùng entry cuối qua `getRawFinalText()`. `intermediateFindings` cũng push-only, chỉ trim khi đọc (`slice(-20)`).
|
|
120
|
+
- Run dài + worker nói nhiều = memory tăng tuyến tính theo output, nhân với số worker song song.
|
|
121
|
+
- **Fix đề xuất**: giữ tối đa N entry cuối ngay khi push (ring buffer), vì consumer chỉ cần tail.
|
|
122
|
+
|
|
123
|
+
#### F10 — MEDIUM: Double JSON.parse mỗi dòng stdout + sync transcript write per line
|
|
124
|
+
- Mỗi JSON line từ child bị parse 2 lần (`emitLine` → `extractText` và `compactChildPiLine`).
|
|
125
|
+
- `appendTranscript` (`child-pi.ts:387-414`): mỗi dòng = realpath validation + openSync/writeSync/closeSync. Nên parse 1 lần rồi truyền object; giữ fd mở per-task (mở 1 lần, đóng khi task kết thúc) và validate path 1 lần.
|
|
126
|
+
|
|
127
|
+
#### F11 — MEDIUM: `appendBoundedTail` rebuild chuỗi O(cap) mỗi dòng
|
|
128
|
+
- `child-pi.ts:53, 927, 1121`: `current + chunk` rồi slice với cap 512KiB → copy ~512KB mỗi dòng khi buffer đầy. Fix: array-of-chunks + tổng byte, chỉ join khi đọc.
|
|
129
|
+
|
|
130
|
+
#### F12 — MEDIUM: Latency floor 5s mỗi task thành công (final-drain)
|
|
131
|
+
- `finalDrainMs = 5000` (`defaults.ts:13`): sau final assistant event, chờ 5s rồi mới SIGTERM child. Workflow 5 task tuần tự cộng thêm ~25s chết. Fix: khi đã nhận `message_end` + stopReason=stop, rút xuống 1-1.5s, hoặc drain kết thúc sớm khi stdout im lặng >300ms.
|
|
132
|
+
|
|
133
|
+
#### F13 — MEDIUM: Interrupt guard poll 250ms sync fs suốt đời background run
|
|
134
|
+
- `background-runner.ts:144`: existsSync + readFileSync + JSON.parse foreground-control.json mỗi 250ms, per background run (4 Hz × N runs). Fix: `fs.watch` file cha + fallback poll thưa (2s), hoặc chỉ stat mtime trước khi read.
|
|
135
|
+
|
|
136
|
+
#### F14 — MEDIUM: Temp-reconcile parse cùng manifest tới 4 lần mỗi tick
|
|
137
|
+
- `stale-reconciler.ts:516-700`: tick 5 phút, batch 50 dirs, nhưng mỗi dir parse manifest.json + tasks.json rồi re-scan thêm tối đa 3 pass cho TOCTOU check. Fix: đọc 1 lần, truyền parsed object qua các pass, chỉ re-stat mtime để phát hiện thay đổi.
|
|
138
|
+
|
|
139
|
+
#### F15 — MEDIUM: Discovery cache TTL 500ms gần như vô dụng ở steady state
|
|
140
|
+
- `discover-agents.ts:453`: TTL 500ms → scheduler loop re-scan 4 thư mục (readdir + readFile + ~12 regex pass sanitize mỗi file .md) ~2 lần/giây. `discoverTeams`/`discoverWorkflows` **không có cache nào**.
|
|
141
|
+
- **Fix đề xuất**: nâng TTL lên 5-10s kèm invalidation theo mtime của các dir gốc (stat dir rẻ hơn nhiều so với scan), dùng chung 1 cache module cho cả agents/teams/workflows.
|
|
142
|
+
|
|
143
|
+
### 2.3 Extension / UI layer
|
|
144
|
+
|
|
145
|
+
#### F16 — HIGH: `loadConfig()` không có cache, bị gọi trên mọi hot path
|
|
146
|
+
- `config/config.ts:1358-1425`: mỗi lần gọi = tới 4 file (legacy, user, `.crew/config.json`, `.pi/pi-crew.json`) × (existsSync + statSync + readFileSync + JSON.parse với reviver) + full TypeBox `Value.Check` + nhiều pass sanitize/merge.
|
|
147
|
+
- Caller trên hot path:
|
|
148
|
+
- Preload tick: `register.ts:1684` — 1 Hz idle, ~6 Hz khi có run active
|
|
149
|
+
- **Mỗi write/edit tool_result**: `register.ts:2088` (`perWriteValidation` check)
|
|
150
|
+
- Mỗi subagent completion (`register.ts:265`), mỗi notification
|
|
151
|
+
- **Fix đề xuất (quick win lớn nhất)**: cache mtime+TTL giống pattern `manifest-cache.ts` đã có sẵn trong repo. Stat 4 file (~4 syscall) thay vì parse + validate lại. Ước lượng loại bỏ hàng nghìn parse/validate mỗi phiên.
|
|
152
|
+
|
|
153
|
+
#### F17 — HIGH: Powerbar re-scan toàn bộ workflows tới ~5 lần/giây khi run active
|
|
154
|
+
- `ui/powerbar-publisher.ts:215`:
|
|
155
|
+
```ts
|
|
156
|
+
const workflows = allWorkflows(discoverWorkflows(run.cwd));
|
|
157
|
+
```
|
|
158
|
+
- Chạy trong `updatePiCrewPowerbar` (coalesce 200ms). `discoverWorkflows` = 2× readdirSync × 3 roots + readFileSync + regex-parse mọi `.workflow.md`, không cache. Fix: TTL cache 5-10s như F15, hoặc chỉ resolve workflow 1 lần khi run bắt đầu (workflow của run không đổi giữa chừng).
|
|
159
|
+
|
|
160
|
+
#### F18 — MEDIUM: Preload loop không bao giờ nghỉ khi idle
|
|
161
|
+
- `register.ts:1727-1741`: tick 1 Hz kể cả khi 0 run, mỗi tick gọi `loadConfig` (F16) + `manifestCache.list(20)` (list-cache TTL chỉ 500ms tại `manifest-cache.ts:36` → readdirSync + statSync per manifest gần như mỗi tick). Fix: khi N tick liên tiếp không có run active và không có event bus activity, giãn xuống 5-10s hoặc dừng hẳn, đánh thức bằng event-bus/`run` action.
|
|
162
|
+
|
|
163
|
+
#### F19 — MEDIUM: `mailboxStamp` O(N task dirs) statSync mỗi lần check stale
|
|
164
|
+
- `run-snapshot-cache.ts:110-129`: mỗi refresh check = readdirSync(mailbox/tasks) + 2 statSync per task dir, sync trên UI path. Code đã từng bỏ per-agent output stamp vì lý do này (comment `:604-608`) nhưng mailbox stamp còn giữ pattern cũ. Fix: 1 stamp file tổng do writer touch, hoặc dựa vào event-bus invalidation (đã có) và bỏ stamp mailbox.
|
|
165
|
+
|
|
166
|
+
#### F20 — LOW/MEDIUM: `delivery.json` parse 2 lần, `outbox.jsonl` tail-read 2 lần mỗi snapshot rebuild
|
|
167
|
+
- `run-snapshot-cache.ts:484, 507`. Fix: đọc 1 lần, chia sẻ kết quả giữa `mailboxFrom` và `groupJoinsFrom`.
|
|
168
|
+
|
|
169
|
+
#### F21 — MEDIUM: `async-notifier` full `readEvents` mỗi 5s cho run nghi ngờ chết
|
|
170
|
+
- `async-notifier.ts:74`: `markDeadAsyncRunIfNeeded` đọc + parse toàn bộ events.jsonl (tới 50MB). Fix: dùng `readEventsCursor` với byte offset lưu lại, hoặc chỉ cần tail vài KB cuối để tìm terminal event.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 3. Kế hoạch tối ưu đề xuất (ưu tiên theo tác động / rủi ro)
|
|
175
|
+
|
|
176
|
+
### Đợt 1 — Quick wins (thay đổi cục bộ, rủi ro thấp, tác động lớn)
|
|
177
|
+
|
|
178
|
+
| # | Việc | File | Tác động |
|
|
179
|
+
|---|---|---|---|
|
|
180
|
+
| 1 | Cache `loadConfig` theo mtime+TTL (2s) | config.ts | Loại bỏ nguồn CPU lặp lại lớn nhất ở extension layer (F16) |
|
|
181
|
+
| 2 | Generation counter per-stateRoot | state-store.ts | Khôi phục hit-rate manifest cache khi run active (F1) |
|
|
182
|
+
| 3 | TTL cache cho `discoverWorkflows`/`discoverTeams`, nâng TTL agents lên 5-10s + mtime dir check | discover-*.ts | Diệt 5 Hz scan của powerbar (F17) + steady-state scan (F15) |
|
|
183
|
+
| 4 | Bỏ pretty-print cho tasks.json | atomic-write.ts caller | Giảm ~40-60% byte ghi mỗi update (F4) |
|
|
184
|
+
| 5 | Ring-buffer cho `rawTextEvents`/`intermediateFindings` | child-pi.ts | Chặn memory growth run dài (F9) |
|
|
185
|
+
| 6 | Giảm `finalDrainMs` 5000 → 1500 (config đã có sẵn override) | defaults.ts | Bớt 3.5s/task latency (F12) |
|
|
186
|
+
| 7 | Đọc delivery.json/outbox 1 lần mỗi snapshot rebuild | run-snapshot-cache.ts | (F20) |
|
|
187
|
+
|
|
188
|
+
### Đợt 2 — Batching và giảm fsync (cần chạy lại integration tests)
|
|
189
|
+
|
|
190
|
+
| # | Việc | Tác động |
|
|
191
|
+
|---|---|---|
|
|
192
|
+
| 8 | Wire `appendEventBuffered` cho event không-terminal; fsync chỉ terminal events; persist `.seq` mỗi N events | Giảm mạnh syscall/fsync per event (F3), hạ tầng đã viết sẵn |
|
|
193
|
+
| 9 | Wire `saveRunTasksCoalesced` vào checkpoint path của task-runner | Gộp burst checkpoint writes (F4) |
|
|
194
|
+
| 10 | Đơn giản hóa `persistSingleTaskUpdate`: bỏ mtime-CAS khi đã cầm run lock, chuyển async | Bỏ 3× statSync + retry loop 100 vòng (F2) |
|
|
195
|
+
| 11 | Cache kết quả `isSymlinkSafePath` theo dirname trong process | Bỏ 2× ancestor lstat walk mỗi write (F5) |
|
|
196
|
+
| 12 | Idle backoff cho preload loop + nâng list-cache TTL khi idle | (F18) |
|
|
197
|
+
|
|
198
|
+
### Đợt 3 — Cấu trúc (cần thiết kế, test kỹ)
|
|
199
|
+
|
|
200
|
+
| # | Việc | Tác động |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| 13 | Chuyển worktree-manager sang async `execFile`, cache leader-status per batch | Hết chặn event loop khi fanout, worktree prep song song thật (F8) |
|
|
203
|
+
| 14 | Mailbox delivery-state append-only + compaction; archive-list cache | (F6) |
|
|
204
|
+
| 15 | Registry liveness không cần parse manifest; bỏ dual-format write | (F7) |
|
|
205
|
+
| 16 | In-memory task state per active run (write-through), disk chỉ là bản persist | Về lâu dài loại bỏ read-modify-write cycle hoàn toàn (F1/F2/F4) |
|
|
206
|
+
| 17 | `readEvents` mặc định đi qua cursor/tail; notifier dùng offset | (F21, F6 read path) |
|
|
207
|
+
|
|
208
|
+
### Cách đo và nghiệm thu
|
|
209
|
+
|
|
210
|
+
- Repo đã có `src/benchmark/` — thêm micro-bench cho: `loadConfig` (trước/sau cache), `persistSingleTaskUpdate` (thời gian/syscall mỗi checkpoint), `appendEvent` throughput (events/s), worktree prep wall-time với N task song song.
|
|
211
|
+
- Đo end-to-end: `time` của 1 run `fast-fix` scaffold-mode (loại bỏ nhiễu model) trước/sau từng đợt.
|
|
212
|
+
- Windows là môi trường nhạy nhất (fsync, spawn, stat đều đắt hơn) — benchmark trên Windows trước.
|
|
213
|
+
- Sau mỗi đợt: `npm run typecheck` + `npm test` + `npm run check:lazy-imports`.
|
|
214
|
+
|
|
215
|
+
### Ước lượng tác động tổng
|
|
216
|
+
|
|
217
|
+
- Đợt 1: giảm rõ CPU nền khi idle (config parse 1 Hz → 0), powerbar scan (5 Hz → ~0), latency mỗi task -3.5s, memory ổn định cho run dài.
|
|
218
|
+
- Đợt 2: giảm 5-10× số syscall/fsync trên đường ghi state trong run active (mỗi task hiện tốn ~5 checkpoint × [lock + 2 read + 3 stat + full rewrite] + ~10-20 event × [fsync + sidecar write]).
|
|
219
|
+
- Đợt 3: hết hiện tượng UI đơ khi fanout worktree trên repo lớn; state layer scale theo số run/task.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 4. Ghi chú không phải hiệu năng (quan sát thêm)
|
|
224
|
+
|
|
225
|
+
- `PluginRegistry` builtin trong team-runner.ts được register nhưng chưa có integration point (chính comment trong code thừa nhận) — dead weight nhỏ.
|
|
226
|
+
- Nhiều file lớn >1500 dòng (register.ts 79KB, team-runner.ts 69KB) — không phải vấn đề runtime nhưng làm chậm typecheck/test và khó review; đã có xu hướng tách (`team-tool/*.ts`), nên tiếp tục.
|
|
227
|
+
- Sync lock với `sleepSync` (locks.ts:244, event-log.ts:143) chặn event loop khi contention — Đợt 2/3 nên ưu tiên các async variant đã có sẵn.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 5. Delta v0.9.17 — những gì đã fix và regressed
|
|
232
|
+
|
|
233
|
+
> 40 commits pulled (`1418fa7..22a8e72`), ~20k dòng thay đổi trong `src/`.
|
|
234
|
+
> Re-verify từng phát hiện trên code mới. Bảng tổng hợp:
|
|
235
|
+
|
|
236
|
+
### 5.1 Đã cải thiện (positive)
|
|
237
|
+
|
|
238
|
+
| Commit | Thay đổi | Tác động hiệu năng |
|
|
239
|
+
|---|---|---|
|
|
240
|
+
| `task-runner.ts:395`, `team-runner.ts:1054` | `appendEventBuffered` wired cho `task.progress` | **F3 partial fix** — event tần suất cao nhất giờ coalesce 20ms, 1 lock acquire. p95 producer ~0µs (bench trong comment). Terminal events vẫn per-fsync. |
|
|
241
|
+
| `21ac434` M6 coalesce micro-tasks | `coalesceMicroTasks` workflow flag + `coalesce-tasks.ts` | Giảm số child process spawn khi workflow có nhiều task nhỏ read-only (3 task → 1 spawn). Trực tiếp giảm F8/F9/F12 cho workload phù hợp. |
|
|
242
|
+
| `0543966` M5 serialize on write-path overlap | `path-overlap.ts` | Tránh conflict khi 2 task song song ghi cùng file. Không phải tối ưu tốc độ nhưng giảm retry/conflict overhead. |
|
|
243
|
+
| `48aae01` L4 trim dependency output | `task-output-context.ts` + `MAX_TOTAL_DEP_INLINE_BYTES` | Giảm context size truyền cho child → giảm prompt build cost + model input tokens. |
|
|
244
|
+
| `8a68825`+`06f16d7` Bundle as default entry | `dist/index.mjs` 2.9MB, esbuild | **Cold-start −31.6%** (2509ms → 1717ms p50, bench `scripts/bench-cold-start.mjs`). Tác động lớn nhất đến latency khởi động. |
|
|
245
|
+
| `5281074` Split register.ts | `registration/{lifecycle,observability,ui}.ts` + LAZY markers | Giảm module-graph warmup cost; lazy import boundary rõ hơn. |
|
|
246
|
+
| `cbaa572` Drop withRunLock from catch path | `team-runner.ts` | Sửa closeout lock race (run.completed rồi run.failed 500ms sau). Không phải perf nhưng sửa 1 nguồn spurious failed run → giảm retry waste. |
|
|
247
|
+
|
|
248
|
+
### 5.2 Đã regressed (negative)
|
|
249
|
+
|
|
250
|
+
| Commit | Thay đổi | Tác động |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| `13f4490` fsync data + parent dir | `atomic-write.ts:384,410` | **F4 + F6 regressed** — mỗi `atomicWriteFile` giờ thêm `fs.fsyncSync(fd)` + `fs.fsyncSync(dirFd)` (POSIX). CI timeout phải bump 600s→900s (`9bbeb45`). `persistSingleTaskUpdate` gọi ~5 lần mỗi task × 2 fsync = +10 fsync/task. Trên Windows fsync đặc biệt đắt. |
|
|
253
|
+
| `test:integration` timeout 120s→300s | `22a8e72` | Phản ánh tổng overhead tăng do fsync + bundle + M6. |
|
|
254
|
+
|
|
255
|
+
### 5.3 Không đổi (xác nhận vẫn còn trên v0.9.17)
|
|
256
|
+
|
|
257
|
+
| Finding | Trạng thái | Bằng chứng |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| F1 — global generation counter | **Vẫn còn** | `state-store.ts:132` `manifestCacheGeneration++` |
|
|
260
|
+
| F2 — persistSingleTaskUpdate 100-iteration sync CAS | **Vẫn còn** | `state-helpers.ts:57` `attempt < 100` |
|
|
261
|
+
| F4 — saveRunTasksCoalesced 0 caller | **Vẫn còn** | grep toàn src, chỉ definition tại `state-store.ts:428` |
|
|
262
|
+
| F5 — isSymlinkSafePath ancestor walk ×2 | **Vẫn còn** | `atomic-write.ts` không đổi logic validate |
|
|
263
|
+
| F8 — worktree execFileSync | **Vẫn còn** | `worktree-manager.ts:1,33` — `execFileSync` unchanged |
|
|
264
|
+
| F9 — rawTextEvents unbounded | **Vẫn còn** | `child-pi.ts:600` `private readonly rawTextEvents: string[] = []` |
|
|
265
|
+
| F12 — finalDrainMs 5000 | **Vẫn còn** | `defaults.ts:13` `finalDrainMs: 5000` |
|
|
266
|
+
| F15 — discovery cache TTL 500ms | **Vẫn còn** | `discover-agents.ts:493` `DISCOVERY_CACHE_TTL_MS = 500` |
|
|
267
|
+
| F16 — loadConfig không cache | **Vẫn còn** | `config.ts` không có configCache (grep confirmed) |
|
|
268
|
+
| F17 — discoverWorkflows không cache, gọi ~5Hz | **Vẫn còn** | `powerbar-publisher.ts:284` `discoverWorkflows(run.cwd)` — không cache trong discover-workflows.ts (grep confirmed) |
|
|
269
|
+
| F21 — async-notifier full readEvents | **Vẫn còn** | `async-notifier.ts:90` `readEvents(run.eventsPath)` |
|
|
270
|
+
|
|
271
|
+
### 5.4 Khuyến nghị cập nhật cho Đợt 1
|
|
272
|
+
|
|
273
|
+
Sau v0.9.17, thứ tự ưu tiên Đợt 1 cần điều chỉnh:
|
|
274
|
+
|
|
275
|
+
1. **F4 fsync mitigation** (NEW urgent): thêm tham số `durability: "full" | "best-effort"` cho `atomicWriteJson`.
|
|
276
|
+
Checkpoint/informational writes dùng `best-effort` (bỏ fsync data+dir, chỉ rename). Terminal/manifest writes giữ `full`.
|
|
277
|
+
Đây là fix nhanh nhất cho regression `13f4490` mà không mất durability cho data quan trọng.
|
|
278
|
+
2. **F16 loadConfig cache** (giữ #1): vẫn là quick win lớn nhất, không bị ảnh hưởng bởi pull.
|
|
279
|
+
3. **F1 generation per-stateRoot** (giữ #2): không đổi.
|
|
280
|
+
4. **F3 expand buffered events** (nâng hạng): đã có hạ tầng, chỉ cần mở rộng cho thêm event types.
|
|
281
|
+
5. Các mục #3-#7 còn lại trong Đợt 1 không đổi.
|
|
282
|
+
|
|
283
|
+
### 5.5 Ghi chú thêm
|
|
284
|
+
|
|
285
|
+
- `atomic-write-v2.ts` đã xuất hiện (file mới, `fs.fsyncSync` tại `:30`) nhưng grep `atomic-write-v2` trên toàn `src/` trả về **0 import** — có vẻ là work-in-progress hoặc dead code. Cần xác minh xem có plan migrate không.
|
|
286
|
+
- Bundle entry (`dist/index.mjs`) là default từ v0.9.17. Nếu `dist/` không tồn tại (dev clone), fallback sang strip-types. Bundle giảm cold-start nhưng **không ảnh hưởng** đến các hot-path I/O findings trong review này.
|
|
287
|
+
- `coalesceMicroTasks` (M6) hiện MVP, chỉ hoạt động cho `READ_ONLY` roles. Khi mở rộng sang mutating roles, cần chú ý F8 (worktree prep cho coalesced group vẫn sync).
|
package/docs/skills/REFERENCE.md
CHANGED
|
@@ -62,14 +62,8 @@ verification-before-done (per round)
|
|
|
62
62
|
| Task delegation | `delegation-patterns` |
|
|
63
63
|
| Complex multi-phase work | `orchestration` |
|
|
64
64
|
| After bug is fixed | `post-mortem` |
|
|
65
|
-
| Security review | `security-review` |
|
|
66
65
|
| Workspace safety | `workspace-isolation` |
|
|
67
66
|
| Bash safety | `safe-bash` |
|
|
68
|
-
| Hypothesis-driven investigation | `threat-hypothesis-framework` |
|
|
69
|
-
| Active threat hunting | `hunting-investigation-loop` |
|
|
70
|
-
| Artifact examination | `artifact-analysis-loop` |
|
|
71
|
-
| Building response procedures | `incident-playbook-construction` |
|
|
72
|
-
| Designing detection pipelines | `detection-pipeline-design` |
|
|
73
67
|
|
|
74
68
|
---
|
|
75
69
|
|
|
@@ -83,17 +77,6 @@ verification-before-done (per round)
|
|
|
83
77
|
| `verification-before-done` | Evidence before claims |
|
|
84
78
|
| `orchestration` | Multi-phase coordination, 8 rules including "respawn not absorb" |
|
|
85
79
|
|
|
86
|
-
### Security
|
|
87
|
-
|
|
88
|
-
| Skill | Description |
|
|
89
|
-
|-------|-------------|
|
|
90
|
-
| `security-review` | Security review with audit and detection authoring |
|
|
91
|
-
| `threat-hypothesis-framework` | Hypothesis-driven investigation |
|
|
92
|
-
| `hunting-investigation-loop` | Active threat hunting with validation |
|
|
93
|
-
| `artifact-analysis-loop` | Artifact analysis with IOC extraction |
|
|
94
|
-
| `incident-playbook-construction` | Playbook building with steps, decisions, SLAs |
|
|
95
|
-
| `detection-pipeline-design` | Data pipeline design for security monitoring |
|
|
96
|
-
|
|
97
80
|
### Documentation
|
|
98
81
|
|
|
99
82
|
| Skill | Description |
|