@zq-silk/yui 0.15.7 → 0.15.9
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/ARCHITECTURE.md +194 -399
- package/ARCHITECTURE.zh-CN.md +151 -0
- package/README.md +306 -1131
- package/dist/agent/adapterCatalog.js +15 -2
- package/dist/agent/agent.js +23 -3
- package/dist/agent/argumentPolicy.js +7 -1
- package/dist/agent/connectionPlan.js +62 -0
- package/dist/agent/executionComponents.js +158 -0
- package/dist/agent/launchEnvironment.js +31 -3
- package/dist/agent/managedRuntimeEnvironment.js +3 -5
- package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
- package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
- package/dist/artifacts/artifactCapability.js +74 -0
- package/dist/artifacts/artifactCommitLock.js +249 -0
- package/dist/artifacts/artifactPaths.js +151 -0
- package/dist/artifacts/gitArtifactRef.js +146 -0
- package/dist/artifacts/managedGit.js +332 -0
- package/dist/artifacts/taskArtifactRepository.js +277 -0
- package/dist/brief/taskBrief.js +12 -0
- package/dist/cli/agentConfigurationPicker.js +13 -0
- package/dist/cli/commandCatalog.js +165 -74
- package/dist/cli/interactionCandidates.js +5 -5
- package/dist/cli/interactionPolicy.js +38 -8
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/managedDiagnostics.js +28 -0
- package/dist/cli/operatorWizard.js +1 -7
- package/dist/cli/roleOptionOrder.js +27 -0
- package/dist/cli/roleWizard.js +50 -14
- package/dist/cli/updateOrchestrator.js +1 -1
- package/dist/cli/updatePorts.js +3 -4
- package/dist/cli.js +245 -95
- package/dist/commands/agentCommands.js +72 -14
- package/dist/commands/capabilityCommands.js +9 -6
- package/dist/commands/configCommands.js +20 -20
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +24 -24
- package/dist/commands/globalRoleCommands.js +1 -1
- package/dist/commands/grantCommands.js +4 -4
- package/dist/commands/operatorCommands.js +34 -9
- package/dist/commands/projectCommands.js +4 -4
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/roleConfiguration.js +25 -5
- package/dist/commands/roleRuntimeGuard.js +4 -5
- package/dist/commands/sessionCommands.js +3 -7
- package/dist/commands/taskActivationCommands.js +281 -0
- package/dist/commands/taskActor.js +28 -49
- package/dist/commands/taskCommands.js +1461 -720
- package/dist/commands/taskContextCommand.js +39 -583
- package/dist/commands/taskExecutionCommands.js +32 -32
- package/dist/commands/taskInputCommands.js +40 -104
- package/dist/commands/taskIntegrationCommands.js +3 -2
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -8
- package/dist/commands/taskOverviewCommand.js +33 -45
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
- package/dist/commands/taskRoleRuntimeStatus.js +133 -102
- package/dist/commands/telemetryCommands.js +36 -38
- package/dist/config/configCatalog.js +4 -4
- package/dist/config/yuiConfig.js +8 -8
- package/dist/context/contextSnapshot.js +10 -10
- package/dist/context/dispatchContext.js +11 -11
- package/dist/context/roleSessionContext.js +6 -3
- package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
- package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
- package/dist/context/sessionBootstrapManifest.js +21 -2
- package/dist/context/sourceRunContext.js +30 -0
- package/dist/context/taskContext.js +481 -0
- package/dist/context/wakeNotification.js +27 -27
- package/dist/controller/agentRuntimeObserver.js +21 -24
- package/dist/controller/capabilityBridge.js +17 -6
- package/dist/controller/clientRuntime.js +65 -92
- package/dist/controller/controller.js +120 -188
- package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
- package/dist/controller/jobControl.js +54 -85
- package/dist/controller/resourceInventory.js +8 -27
- package/dist/controller/resourceInventoryLinux.js +12 -13
- package/dist/controller/runtime.js +529 -479
- package/dist/controller/runtimeEventInbox.js +55 -25
- package/dist/controller/runtimeEventProcessor.js +22 -31
- package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
- package/dist/controller/runtimeLaunchCoordinator.js +80 -426
- package/dist/controller/runtimeObservationHook.js +14 -18
- package/dist/controller/sessionNotify.js +16 -24
- package/dist/controller/sessionOwnerReconciliation.js +168 -50
- package/dist/controller/structuredProviderObservation.js +138 -99
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/coordination/workMailboxQueue.js +36 -33
- package/dist/core/boundedRpc.js +8 -1
- package/dist/core/controllerClient.js +20 -1
- package/dist/core/controllerServer.js +4 -4
- package/dist/doctor/doctor.js +13 -2
- package/dist/domain/agentResultTransport.js +9 -9
- package/dist/execution/codexThreadNaming.js +2 -8
- package/dist/execution/executionHealth.js +51 -63
- package/dist/execution/reviewMainRun.js +137 -0
- package/dist/execution/workItemExecution.js +28 -29
- package/dist/execution/workItemExecutionProjection.js +99 -107
- package/dist/execution/workItemMainRun.js +141 -0
- package/dist/executor/agentAdapter.js +227 -20
- package/dist/executor/agentConfigurationCatalog.js +126 -4
- package/dist/executor/agentConfigurationProbe.js +162 -4
- package/dist/executor/agentExecutor.js +79 -78
- package/dist/executor/effectiveLaunch.js +105 -18
- package/dist/executor/executorRegistry.js +29 -44
- package/dist/executor/fileRoleLaunchPlanner.js +229 -154
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +6 -3
- package/dist/input/inputRequest.js +12 -10
- package/dist/integration/gitIntegrationService.js +4 -11
- package/dist/integration/integrationQueueService.js +4 -4
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/kernel/builtinCapabilities.js +255 -12
- package/dist/kernel/capabilityRegistry.js +64 -18
- package/dist/kernel/instanceHost.js +12 -1
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
- package/dist/lifecycle/exactRunTerminalization.js +449 -0
- package/dist/message/message.js +118 -6
- package/dist/message/messageContinuation.js +204 -0
- package/dist/observability/executionAudit.js +70 -72
- package/dist/observability/faultClassification.js +2 -2
- package/dist/observability/orchestrationMetrics.js +8 -8
- package/dist/operator/operatorSessionHistory.js +1 -7
- package/dist/output/agentConfigurationPresentation.js +8 -3
- package/dist/output/agentRunConfigurationPresentation.js +128 -0
- package/dist/output/rolePresentation.js +54 -3
- package/dist/plugins/pluginChild.js +104 -0
- package/dist/plugins/pluginIntent.js +26 -0
- package/dist/plugins/pluginInterpreter.js +43 -0
- package/dist/plugins/pluginPackage.js +101 -0
- package/dist/plugins/pluginProcess.js +112 -0
- package/dist/plugins/pluginService.js +388 -0
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/gitWorkspace.js +26 -4
- package/dist/repository/project.js +19 -4
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +20 -27
- package/dist/repository/taskWorkspacePreparer.js +344 -83
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +3 -3
- package/dist/resources/projectResource.js +75 -0
- package/dist/resources/projectResourceService.js +343 -0
- package/dist/resources/resourceDiscovery.js +6 -6
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/resourceRegistrar.js +1 -1
- package/dist/resources/resourceTypes.js +1 -1
- package/dist/review/deltaRecheck.js +3 -3
- package/dist/review/reviewAcceptance.js +16 -16
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewRound.js +21 -20
- package/dist/review/reviewerAvailability.js +2 -2
- package/dist/role/role.js +51 -7
- package/dist/role/taskRoleUpdate.js +30 -0
- package/dist/runtime/acpProtocol.js +425 -0
- package/dist/runtime/acpSession.js +731 -0
- package/dist/runtime/acpSessionConfiguration.js +260 -0
- package/dist/runtime/agentDriver.js +30 -11
- package/dist/runtime/agentEndpoint.js +278 -0
- package/dist/runtime/agentEndpointIdentity.js +86 -0
- package/dist/runtime/agentEndpointOwnership.js +239 -0
- package/dist/runtime/agentError.js +2 -10
- package/dist/runtime/agentHost.js +565 -314
- package/dist/runtime/agentRunConfiguration.js +258 -0
- package/dist/runtime/builtinAgentDrivers.js +134 -18
- package/dist/runtime/builtinAgentErrorMappers.js +55 -3
- package/dist/runtime/builtinTranscriptUsage.js +1 -1
- package/dist/runtime/claude-process-owner +0 -0
- package/dist/runtime/codexAppServerRuntime.js +38 -30
- package/dist/runtime/codexInteractiveHost.js +41 -6
- package/dist/runtime/continuationManager.js +2 -6
- package/dist/runtime/executionEnvironment.js +30 -0
- package/dist/runtime/firstProgressAdvisory.js +11 -11
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/jsonLineChannel.js +109 -0
- package/dist/runtime/launchBroker.js +91 -16
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +10 -18
- package/dist/runtime/managedCaller.js +61 -17
- package/dist/runtime/nativeSessionControl.js +102 -0
- package/dist/runtime/ports.js +6 -21
- package/dist/runtime/processExitObservation.js +8 -7
- package/dist/runtime/promptEnvelope.js +17 -6
- package/dist/runtime/providerContinuation.js +3 -9
- package/dist/runtime/providerContinuationReconciliationService.js +4 -13
- package/dist/runtime/providerControl.js +2 -7
- package/dist/runtime/providerRuntimeIdentity.js +110 -222
- package/dist/runtime/providerRuntimeReconciler.js +5 -9
- package/dist/runtime/runtimeBinding.js +0 -1
- package/dist/runtime/runtimeContinuationProjection.js +4 -7
- package/dist/runtime/runtimeDeadlines.js +9 -0
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +29 -65
- package/dist/runtime/runtimeProjection.js +43 -51
- package/dist/runtime/runtimeSessionCandidate.js +1 -3
- package/dist/runtime/sessionLaunchRequest.js +3 -7
- package/dist/runtime/sessionOwnerIdentity.js +7 -54
- package/dist/runtime/sessionOwnerRegistry.js +22 -17
- package/dist/runtime/sessionReconciliation.js +4 -8
- package/dist/runtime/sessionTerminationGuard.js +70 -259
- package/dist/runtime/sessionTokenMetrics.js +5 -16
- package/dist/runtime/structuredProviderHost.js +237 -117
- package/dist/runtime/taskRuntimeIsolation.js +39 -122
- package/dist/runtime/tmuxAdapters.js +39 -86
- package/dist/scheduler/activeRoleRunDelivery.js +354 -0
- package/dist/scheduler/leaderWakeupProcessor.js +75 -266
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +80 -9
- package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
- package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
- package/dist/scheduler/taskExecutionProjection.js +120 -124
- package/dist/scheduler/taskObservabilityProjection.js +29 -29
- package/dist/scheduler/taskWake.js +11 -4
- package/dist/scheduler/wakeReason.js +9 -1
- package/dist/setup/setupCommand.js +3 -7
- package/dist/storage/migrations/agentRunContract.js +159 -0
- package/dist/storage/migrations/artifactsToGit.js +338 -0
- package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
- package/dist/storage/migrations/submitIntent.js +126 -0
- package/dist/storage/sqliteSchema.js +467 -7
- package/dist/storage/sqliteStore.js +355 -220
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +10 -5
- package/dist/storage/taskStore.js +13 -11
- package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
- package/dist/surface/surfaceContributions.js +102 -0
- package/dist/task/completionReadiness.js +32 -6
- package/dist/task/deliveryGuard.js +16 -16
- package/dist/task/draftPlan.js +72 -12
- package/dist/task/nextAction.js +144 -128
- package/dist/task/remoteDelivery.js +6 -6
- package/dist/task/task.js +184 -18
- package/dist/task/taskActivation.js +327 -0
- package/dist/task/taskActivationService.js +408 -0
- package/dist/task/taskRecordReference.js +5 -4
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/task/taskSubmission.js +236 -0
- package/dist/telemetry/sqliteTelemetryStore.js +55 -68
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +2 -2
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +121 -20
- package/dist/web/assets/client/components.js +87 -54
- package/dist/web/assets/client/i18n.js +83 -41
- package/dist/web/assets/client/markdown.js +1 -1
- package/dist/web/assets/client/taskSurface.js +442 -0
- package/dist/web/assets/client/view.js +49 -44
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +22 -4
- package/dist/web/controllerWeb.js +60 -0
- package/dist/web/webMutation.js +28 -0
- package/dist/web/webServer.js +133 -8
- package/dist/web/webSnapshot.js +81 -74
- package/dist/web/webTaskSurface.js +64 -0
- package/dist/workItem/dependencyGate.js +1 -1
- package/dist/workItem/workItem.js +84 -48
- package/dist/workspace/workItemChangeSetManager.js +16 -9
- package/docs/agent-result-consumption.md +96 -0
- package/docs/agent-result-consumption.zh-CN.md +81 -0
- package/docs/agent-runtime-drivers.md +93 -0
- package/docs/agent-runtime-drivers.zh-CN.md +77 -0
- package/docs/architecture/README.md +50 -0
- package/docs/architecture/README.zh-CN.md +43 -0
- package/docs/architecture/capabilities-and-resources.md +118 -0
- package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
- package/docs/managed-turn-and-session-runtime.md +224 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
- package/docs/observability/README.md +83 -0
- package/docs/observability/README.zh-CN.md +71 -0
- package/docs/plugin-sdk.md +393 -0
- package/docs/plugin-sdk.zh-CN.md +293 -0
- package/docs/provider-runtime.md +165 -0
- package/docs/provider-runtime.zh-CN.md +132 -0
- package/docs/release-workflow.md +305 -0
- package/docs/release-workflow.zh-CN.md +237 -0
- package/docs/roles-and-configuration.md +115 -0
- package/docs/roles-and-configuration.zh-CN.md +96 -0
- package/docs/sqlite-control-plane-design.md +78 -0
- package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
- package/docs/task-dag-semantics.md +80 -0
- package/docs/task-dag-semantics.zh-CN.md +59 -0
- package/docs/task-delivery.md +105 -0
- package/docs/task-delivery.zh-CN.md +82 -0
- package/docs/task-local-identity.md +8 -6
- package/docs/task-local-identity.zh-CN.md +58 -0
- package/docs/testing/verification-levels.md +88 -0
- package/docs/testing/verification-levels.zh-CN.md +69 -0
- package/i18n/README.zh-CN.md +270 -722
- package/package.json +3 -2
- package/skills/yui-leader/SKILL.md +88 -304
- package/skills/yui-leader/references/execution.md +303 -0
- package/skills/yui-leader/references/integration.md +39 -0
- package/skills/yui-leader/references/planning.md +109 -0
- package/skills/yui-leader/references/replicated-execution.md +42 -0
- package/skills/yui-leader/references/task-plugins.md +37 -0
- package/skills/yui-operator/SKILL.md +46 -62
- package/skills/yui-reviewer/SKILL.md +35 -36
- package/skills/yui-runtime/SKILL.md +88 -24
- package/skills/yui-runtime/references/publication.md +22 -0
- package/skills/yui-runtime/references/recovery.md +64 -0
- package/skills/yui-worker/SKILL.md +37 -39
- package/dist/cli/roleOptionCatalog.js +0 -68
- package/dist/context/sourceTurnContext.js +0 -30
- package/dist/execution/reviewMainTurn.js +0 -161
- package/dist/execution/workItemMainTurn.js +0 -164
- package/dist/lifecycle/exactTurnTerminalization.js +0 -407
- package/dist/runtime/preallocatedNativeSession.js +0 -13
- package/dist/runtime/runtimeStopReceipt.js +0 -42
- package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
<p align="right"><strong>English</strong> | <a href="./release-workflow.zh-CN.md">简体中文</a></p>
|
|
2
|
+
|
|
3
|
+
# Authorized release operations
|
|
4
|
+
|
|
5
|
+
A release workflow is an explicitly chosen, authorized sequence of
|
|
6
|
+
external release effects — pull requests, CI confirmation, merges, version
|
|
7
|
+
tags, npm publishes, fresh-install smoke tests, CLI updates, Controller
|
|
8
|
+
replacements, Project migrations, and post-verification. It is a specialized
|
|
9
|
+
external-effect facility, not Yui's Task planning or Agent execution model.
|
|
10
|
+
The Agent selects a predeclared plan, and the facility drives that plan
|
|
11
|
+
from durable state: every transition is persisted before the next external
|
|
12
|
+
call, so a crash, timeout, or revoked grant never leaves the release guessing.
|
|
13
|
+
|
|
14
|
+
Two task-level record families back it:
|
|
15
|
+
|
|
16
|
+
- **CapabilityGrant** (`capability-grant-N`) — the authority. A named granter
|
|
17
|
+
scopes a grant to actions, parameter bounds, an expiry, a use count, and an
|
|
18
|
+
irreversibility ceiling.
|
|
19
|
+
- **ReleaseWorkflow** (`release-workflow-N`) — the plan and its progress: an
|
|
20
|
+
exact source (repository + pinned commit, optionally an artifact), an
|
|
21
|
+
immutable ordered step plan, and one persisted record per step.
|
|
22
|
+
|
|
23
|
+
The engine (`src/release/releaseWorkflowEngine.ts`) is a pure library; the
|
|
24
|
+
`yui task workflow` and `yui task grant` commands drive it. Every external
|
|
25
|
+
system sits behind `ReleaseWorkflowPorts`
|
|
26
|
+
(`src/release/releaseWorkflowPorts.ts`), so the whole workflow is testable
|
|
27
|
+
with deterministic fakes and no real GitHub, npm, git, Controller, or process
|
|
28
|
+
side effect.
|
|
29
|
+
|
|
30
|
+
## Authorization model
|
|
31
|
+
|
|
32
|
+
Every (re)submission of a step passes `checkGrant(grant, request, now)`
|
|
33
|
+
(`src/grant/capabilityGrant.ts`) **before** the external call. The step kind
|
|
34
|
+
is the grant action: a grant lists the step kinds it authorizes, for example
|
|
35
|
+
`--action npm-publish --action version-tag`. The decision is fail-closed —
|
|
36
|
+
every denial carries a machine-readable reason and stops the run:
|
|
37
|
+
|
|
38
|
+
| Reason | Meaning |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `grant-missing` | No grant record is bound to the workflow (engine-level). |
|
|
41
|
+
| `grant-revoked` | The grant was revoked by an operator. |
|
|
42
|
+
| `grant-expired` | The wall clock passed the grant's `expiresAt`. |
|
|
43
|
+
| `grant-uses-exhausted` | The grant's `maxUses` has been consumed. |
|
|
44
|
+
| `grant-action-not-allowed` | The step kind is not in the grant's actions. |
|
|
45
|
+
| `grant-parameter-missing` | A bounded parameter is absent from the step. |
|
|
46
|
+
| `grant-parameter-value-not-allowed` | A bounded parameter has an out-of-bounds value. |
|
|
47
|
+
| `grant-irreversibility-exceeds-ceiling` | The step is more irreversible than the grant's ceiling. |
|
|
48
|
+
|
|
49
|
+
Additional rules:
|
|
50
|
+
|
|
51
|
+
- **One use per authorized submission.** The engine records a grant use
|
|
52
|
+
between the successful decision and the external call, so a `maxUses` grant
|
|
53
|
+
fails closed on the attempt that would exceed it.
|
|
54
|
+
- **Irreversible steps need a confirmed prefix.** A step marked
|
|
55
|
+
`irreversible` additionally requires every earlier step to be `succeeded`;
|
|
56
|
+
otherwise the step fails with `prerequisite-not-confirmed` and the run
|
|
57
|
+
stops. This is what keeps an `npm-publish` from running behind a failed PR.
|
|
58
|
+
- **Denials are recorded.** When a pending step is denied, the engine starts
|
|
59
|
+
and fails the step with the denial in its log, so `workflow status` shows
|
|
60
|
+
exactly where authorization stopped.
|
|
61
|
+
- **Rebinding.** A revoked, expired, or too-narrow grant does not dead-end
|
|
62
|
+
the workflow. Issue a new grant and resume with
|
|
63
|
+
`yui task workflow resume <task> <workflow> --grant <new-grant>`; the plan,
|
|
64
|
+
source, and all confirmed step evidence are immutable across the rebind.
|
|
65
|
+
|
|
66
|
+
## Stable Task-final Review contract
|
|
67
|
+
|
|
68
|
+
Compatible CLI package updates and Controller replacements do not change an
|
|
69
|
+
active Task's final-review capability. Managed Sessions use the ordinary
|
|
70
|
+
`yui` command, compatibility is checked by protocol and storage identity, and
|
|
71
|
+
a replacement Leader presents the contract already established by durable Task
|
|
72
|
+
evidence. No version-aware Operator action is required.
|
|
73
|
+
Candidate and Task-final ReviewRound records must all carry that one contract.
|
|
74
|
+
Conflicting records fail closed; there is no rebind event, recovery command, or
|
|
75
|
+
second contract state machine.
|
|
76
|
+
|
|
77
|
+
## CLI and Controller release boundary
|
|
78
|
+
|
|
79
|
+
The global `yui` command is the stable user and managed-Session interface. It
|
|
80
|
+
does not follow `runtime/active-release.json` for ordinary commands: that
|
|
81
|
+
pointer selects the Controller release, not the CLI package. This keeps CLI,
|
|
82
|
+
Operator Session, and Controller replacement compatible without pinning every
|
|
83
|
+
command to one immutable build.
|
|
84
|
+
|
|
85
|
+
A source-checkout or otherwise unverified local CLI is not this published
|
|
86
|
+
interface. When `YUI_HOME` already names an active release, such a CLI fails
|
|
87
|
+
before opening storage and reports its build/source, the durable Home identity,
|
|
88
|
+
and its invocation class. `make install-local` continues to default to the
|
|
89
|
+
checkout's isolated `output/dev/home`; explicitly pointing that launcher at a
|
|
90
|
+
release-owned Home is rejected.
|
|
91
|
+
|
|
92
|
+
An explicit `yui release activate <release-id|build-id>` is the one exception.
|
|
93
|
+
The global CLI verifies the installed target release and its matching smoke
|
|
94
|
+
receipt, then delegates the unchanged activation arguments to that target's
|
|
95
|
+
`dist/cli.js`. The target release therefore owns the complete handover protocol
|
|
96
|
+
and timeout hierarchy. A no-target activation, help, `--json`, and every other
|
|
97
|
+
command remain on the global CLI. Activation does not add another ordinary CLI
|
|
98
|
+
routing path.
|
|
99
|
+
|
|
100
|
+
## Step catalog
|
|
101
|
+
|
|
102
|
+
The plan is a fixed, predeclared subset of operations. Each plan entry has an
|
|
103
|
+
id (unique within the workflow), a kind, optional params, and an optional
|
|
104
|
+
irreversibility level (`none` | `reversible` | `irreversible`).
|
|
105
|
+
|
|
106
|
+
| Kind | External effect | Authoritative identity |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `pr-create-or-reuse` | Creates the release PR, or reuses an open one for the head. | `pull-request` number |
|
|
109
|
+
| `ci-confirm` | Reads the CI conclusion for the source ref; succeeds only on `success`. | — |
|
|
110
|
+
| `merge` | Merges the named PR (squash by default). | — |
|
|
111
|
+
| `version-tag` | Creates and pushes the annotated version tag. | `git-tag` name |
|
|
112
|
+
| `npm-publish` | Publishes the tarball to the registry. | `npm-package` version |
|
|
113
|
+
| `fresh-install-smoke` | Installs and runs the published package from the registry. | — |
|
|
114
|
+
| `cli-update` | Updates the Yui CLI/Controller home via the existing update orchestrator. | `controller-home` |
|
|
115
|
+
| `controller-replace` | Stops and restarts the file-task Controller. | — |
|
|
116
|
+
| `project-migrate` | Runs the Project migration through the existing project command. | — |
|
|
117
|
+
| `post-verify` | Runs an arbitrary verification command. | — |
|
|
118
|
+
|
|
119
|
+
Steps may reference earlier evidence: a param value of
|
|
120
|
+
`$externalId:<step-id>` resolves to the referenced step's confirmed external
|
|
121
|
+
id at run time, so a `merge` step can consume the PR number the `pr` step
|
|
122
|
+
produced without the operator knowing it in advance. A reference to an
|
|
123
|
+
unconfirmed step fails the run rather than guessing.
|
|
124
|
+
|
|
125
|
+
## Recovery and resume semantics
|
|
126
|
+
|
|
127
|
+
A run always starts from the **resume cursor**: the first plan step whose
|
|
128
|
+
status is not terminal (`succeeded` or `skipped`). There is no "start over" —
|
|
129
|
+
confirmed steps are never re-run.
|
|
130
|
+
|
|
131
|
+
Because every state transition is persisted before the next external call, a
|
|
132
|
+
process exit at any point is recoverable: re-invoke `run` (or `resume`) and
|
|
133
|
+
the engine continues from the first unconfirmed step. `--max-steps <n>` bounds
|
|
134
|
+
a single run; a run that exhausts its budget mid-workflow returns
|
|
135
|
+
`budget-exhausted` and the next invocation continues.
|
|
136
|
+
|
|
137
|
+
In-flight steps are resolved by **authoritative identity query**, never by
|
|
138
|
+
blind re-submission:
|
|
139
|
+
|
|
140
|
+
- A step left `running` or `unknown` is queried first by its recorded
|
|
141
|
+
`externalIdentity`.
|
|
142
|
+
- `exists` → the step reaches `succeeded` **without a second submission**
|
|
143
|
+
(`unknown` is confirmed, `running` is completed).
|
|
144
|
+
- `unknown` → the run stops with outcome `unknown`; the step is never
|
|
145
|
+
re-submitted while its fate is unknowable.
|
|
146
|
+
- `absent` → the effect never landed, so the step is re-attempted (a
|
|
147
|
+
`running` step records the recovery attempt).
|
|
148
|
+
- A `running` step **without** an external identity crashed before recording a
|
|
149
|
+
submission result. An irreversible step is queried through the port anyway
|
|
150
|
+
(the adapter consults its durable idempotency store): `exists` confirms the
|
|
151
|
+
step without a second submission, `unknown` stops as `unconfirmed`, and only
|
|
152
|
+
an authoritative `absent` re-attempts the step exactly once. A reversible
|
|
153
|
+
step always falls through and re-attempts under the same idempotency key.
|
|
154
|
+
- A timeout **without** an external identity marks the step as `unknown`
|
|
155
|
+
(unconfirmed) so it is never re-submitted blindly; on resume it fails closed
|
|
156
|
+
as `unconfirmed`.
|
|
157
|
+
- A `failed` step is retried on the next run; its `attempts` counter and logs
|
|
158
|
+
grow per attempt.
|
|
159
|
+
|
|
160
|
+
Run outcomes: `succeeded`, `failed`, `unknown`, `unauthorized`,
|
|
161
|
+
`unconfirmed`, `budget-exhausted`. Each carries a machine-readable
|
|
162
|
+
`stopReason` (for example `unknown:publish`, `unauthorized:grant-revoked`,
|
|
163
|
+
`budget-exhausted:verify`) and the list of step ids attempted that run.
|
|
164
|
+
|
|
165
|
+
## Idempotency key contract
|
|
166
|
+
|
|
167
|
+
Each step's idempotency key is **predeclared at create time** and never
|
|
168
|
+
changes:
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
<taskId>/<workflowId>/<stepId>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The key is passed to every `executeStep` call for that step, including
|
|
175
|
+
retries after a confirmed-absent timeout. The port contract requires
|
|
176
|
+
`executeStep` to be idempotent under the same key: a retried attempt must not
|
|
177
|
+
produce a second side effect. The engine side of the contract is stricter
|
|
178
|
+
still — it never calls `executeStep` for a step it has marked `unknown`; it
|
|
179
|
+
re-queries by the recorded identity instead. The fakes record every key, so
|
|
180
|
+
the test suite proves at-most-once execution directly.
|
|
181
|
+
|
|
182
|
+
## Operator guide
|
|
183
|
+
|
|
184
|
+
Session authority is checked against current durable bindings. Telemetry is
|
|
185
|
+
grouped by Role/AgentRun, and process owners use PID/start identity. Storage
|
|
186
|
+
changes follow the [single explicit upgrade boundary](sqlite-control-plane-design.md);
|
|
187
|
+
ordinary commands never rewrite the Home schema.
|
|
188
|
+
|
|
189
|
+
Grant issue and revoke are irreversible-authority operations. They require
|
|
190
|
+
the current registered global Operator conversation. Its native session ID
|
|
191
|
+
must match the durable live session binding: Codex commands use `CODEX_THREAD_ID`
|
|
192
|
+
when present, otherwise `YUI_NATIVE_SESSION_ID`; Claude uses `YUI_NATIVE_SESSION_ID`.
|
|
193
|
+
Host generation and launch-time Agent labels are not caller identity. Resuming
|
|
194
|
+
the same conversation through another entry point does not revoke its authority.
|
|
195
|
+
An unregistered, replaced, or ended conversation has no such authority.
|
|
196
|
+
A managed Task Agent cannot self-issue or
|
|
197
|
+
self-revoke a grant, and clearing the child-process environment does not
|
|
198
|
+
confer user authority. The recorded granter/revoker is bound to that
|
|
199
|
+
Operator session (`operator:<agent-id>`); there is no `--granter`/`--by`
|
|
200
|
+
label to spoof.
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
# 1. The Operator session issues the authority for the release chain.
|
|
204
|
+
yui task grant issue task-15 \
|
|
205
|
+
--action pr-create-or-reuse --action npm-publish --action post-verify \
|
|
206
|
+
--irreversibility-ceiling irreversible
|
|
207
|
+
|
|
208
|
+
# 2. Create the workflow against an exact source and a predeclared plan.
|
|
209
|
+
# An npm-publish step requires a content-addressed source artifact: the
|
|
210
|
+
# immutable workflow source can never gain one later, so a plan without
|
|
211
|
+
# --source-artifact is rejected at creation.
|
|
212
|
+
yui task workflow create task-15 \
|
|
213
|
+
--grant capability-grant-1 \
|
|
214
|
+
--source-repo acme/widget --source-commit abc1234deadbeef0000000000000000000000000 \
|
|
215
|
+
--source-artifact widget-1.0.0.tgz@sha512-<base64-integrity> \
|
|
216
|
+
--step pr:pr-create-or-reuse \
|
|
217
|
+
--step publish:npm-publish --step-irreversibility publish=irreversible \
|
|
218
|
+
--step-param publish:tarball=./dist/widget-1.0.0.tgz \
|
|
219
|
+
--step verify:post-verify --step-param verify:command='yui --version'
|
|
220
|
+
|
|
221
|
+
# 3. Run (or resume) and inspect.
|
|
222
|
+
yui task workflow run task-15 release-workflow-1
|
|
223
|
+
yui task workflow resume task-15 release-workflow-1 [--grant capability-grant-2] [--max-steps 1]
|
|
224
|
+
yui task workflow status task-15 release-workflow-1
|
|
225
|
+
|
|
226
|
+
# 4. Revoke authority at any time; the next step stops unauthorized.
|
|
227
|
+
yui task grant revoke task-15 capability-grant-1
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`workflow status` renders each step's status, attempt count, and confirmed
|
|
231
|
+
external id, so an operator can see exactly where a release stopped and why.
|
|
232
|
+
|
|
233
|
+
## Real-resource boundary
|
|
234
|
+
|
|
235
|
+
Real execution happens only through the `yui` CLI, which wires the real
|
|
236
|
+
adapter (`createReleaseWorkflowPorts`). The adapter is a thin shell over
|
|
237
|
+
existing atomic operations — `gh`, `npm`, `git`, the CLI update orchestrator,
|
|
238
|
+
Controller stop/restart, and `project migrate` — and it runs only when a human
|
|
239
|
+
granter has issued an explicit CapabilityGrant that passes `checkGrant` for
|
|
240
|
+
each step. A local test request never substitutes for that authority.
|
|
241
|
+
|
|
242
|
+
The tag-triggered `publish.yml` workflow is the only maintained release smoke.
|
|
243
|
+
It reuses the exact commit that passed core CI and adds only artifact assembly,
|
|
244
|
+
fresh installation, and provenance checks required to publish.
|
|
245
|
+
|
|
246
|
+
That workflow authenticates through npm Trusted Publishing (OIDC), so the
|
|
247
|
+
release identity lives in two places outside the tag: `repository`, `bugs`, and
|
|
248
|
+
`homepage` are copied verbatim from the source `package.json` into the published
|
|
249
|
+
manifest by `assemble-runtime-package.mjs`, and the package's npm Trusted
|
|
250
|
+
Publisher entry names the GitHub owner, repository, workflow file, and
|
|
251
|
+
environment. npm compares `repository.url` against the building repository
|
|
252
|
+
case-sensitively before accepting provenance. Renaming or transferring the
|
|
253
|
+
GitHub repository therefore has to update those URLs and the npm Trusted
|
|
254
|
+
Publisher entry together with the rename; otherwise the next tag reaches
|
|
255
|
+
`npm publish` and fails there, after the tag and the gated build already
|
|
256
|
+
succeeded.
|
|
257
|
+
|
|
258
|
+
## Adapter security hardening
|
|
259
|
+
|
|
260
|
+
The real adapter (`createReleaseWorkflowPorts`) applies additional safeguards
|
|
261
|
+
beyond the engine's grant checks:
|
|
262
|
+
|
|
263
|
+
- **Tarball option injection.** An option-looking tarball path (one starting
|
|
264
|
+
with `-`) is rejected before any subprocess — both the `tar -xOf` manifest
|
|
265
|
+
inspection and `npm publish` — sees it, so a crafted path can never be
|
|
266
|
+
interpreted as a flag.
|
|
267
|
+
- **Tarball TOCTOU.** After the frozen `source.artifact.integrity` is verified,
|
|
268
|
+
the verified bytes are snapshotted to a workflow-private, read-only temp
|
|
269
|
+
file. Both the `tar -xOf` manifest inspection and `npm publish` read the
|
|
270
|
+
snapshot, never the live tarball path, so a replacement of the original file
|
|
271
|
+
after verification cannot change what is published. The snapshot is removed
|
|
272
|
+
when the step completes.
|
|
273
|
+
- **Pinned external commands.** The adapter resolves the external commands it
|
|
274
|
+
shells out to (`gh`, `git`, `npm`, `tar`, `sh`) to absolute paths at
|
|
275
|
+
construction time via `resolveExecutable`, walking the caller's `PATH`
|
|
276
|
+
once. Every subprocess invocation uses the resolved path, so a later `PATH`
|
|
277
|
+
change (or a manipulated working directory) cannot redirect a release effect
|
|
278
|
+
to a different binary. An unresolvable command returns a synthetic failure
|
|
279
|
+
(exit 127) without invoking any binary.
|
|
280
|
+
- **Pinned cli-update activation target.** Before the irreversible update
|
|
281
|
+
effect, the adapter persists the exact activation target — the Home plus the
|
|
282
|
+
global npm prefix (`bin/yui`) — to a durable file under the Home
|
|
283
|
+
(`release/cli-update-identity/<idempotency-key>.json`). A hard-exit recovery
|
|
284
|
+
query (a step with no recorded identity) reads this file and invokes that
|
|
285
|
+
pinned target; if the file is absent (the process exited before the
|
|
286
|
+
pre-effect persistence), the query returns `unknown` rather than deriving
|
|
287
|
+
the target from the resume caller's `npm prefix --global` or `PATH`, so a
|
|
288
|
+
different installation in the resume environment cannot attest the step.
|
|
289
|
+
- **Controller lifecycle verification.** A `cli-update` recovery query proves
|
|
290
|
+
the replacement Controller actually owns the target Home: it runs
|
|
291
|
+
`yui --json controller status` (with `YUI_HOME` pinned to the recorded Home)
|
|
292
|
+
and requires a `current` controller resource whose `yuiHome` resolves to
|
|
293
|
+
that Home, then `yui --json controller identity` and requires the
|
|
294
|
+
authenticated Controller identity to match the activated artifact: the
|
|
295
|
+
Node.js executable path, the exact Controller entrypoint derived from the
|
|
296
|
+
pinned global binary, and the package version. Binary health alone (doctor,
|
|
297
|
+
`--version`) never confirms the handoff, and any unprovable state returns
|
|
298
|
+
`unknown`. This applies to both the identity-bearing query and the
|
|
299
|
+
hard-exit query (a step with no recorded identity).
|
|
300
|
+
- **npm integrity comparison.** An `npm-publish` recovery query does not stop
|
|
301
|
+
at the published version: it fetches `dist.integrity` via
|
|
302
|
+
`npm view <pkg>@<version> dist.integrity` and compares it byte-for-byte with
|
|
303
|
+
the frozen `source.artifact.integrity`. A match confirms the step; the same
|
|
304
|
+
version with different bytes is a conflict and returns `unknown` (never a
|
|
305
|
+
confirmation, never a re-publish); a missing version is `absent`.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<p align="right"><a href="./release-workflow.md">English</a> | <strong>简体中文</strong></p>
|
|
2
|
+
|
|
3
|
+
# 获授权的发布操作
|
|
4
|
+
|
|
5
|
+
发布工作流是一段被显式选择、获授权的外部发布效果序列——pull request、CI 确认、
|
|
6
|
+
合并、版本 tag、npm 发布、全新安装冒烟、CLI 更新、Controller 替换、Project 迁移和
|
|
7
|
+
后置验证。它是一个专用的外部效果设施,而不是 Yui 的 Task 规划或 Agent 执行模型。
|
|
8
|
+
Agent 选择一个预先声明的计划,设施从持久状态驱动该计划:每次转换都在下一次外部调用
|
|
9
|
+
之前持久化,因此崩溃、超时或被撤销的 grant 都不会让发布陷入猜测。
|
|
10
|
+
|
|
11
|
+
两个 Task 级记录族支撑它:
|
|
12
|
+
|
|
13
|
+
- **CapabilityGrant**(`capability-grant-N`)——权威。一个具名的授权者把 grant
|
|
14
|
+
限定到若干动作、参数边界、一个过期时间、一个使用次数和一个不可逆上限。
|
|
15
|
+
- **ReleaseWorkflow**(`release-workflow-N`)——计划及其进展:一个确切来源(仓库 +
|
|
16
|
+
钉住的 commit,可选一个 artifact)、一份不可变的有序步骤计划,以及每步一条持久记录。
|
|
17
|
+
|
|
18
|
+
引擎(`src/release/releaseWorkflowEngine.ts`)是一个纯库;`yui task workflow` 和
|
|
19
|
+
`yui task grant` 命令驱动它。每个外部系统都位于 `ReleaseWorkflowPorts`
|
|
20
|
+
(`src/release/releaseWorkflowPorts.ts`)之后,因此整个工作流可以用确定性的 fake
|
|
21
|
+
测试,不产生任何真实的 GitHub、npm、git、Controller 或进程副作用。
|
|
22
|
+
|
|
23
|
+
## 授权模型
|
|
24
|
+
|
|
25
|
+
每一次(再)提交一个步骤,都在外部调用**之前**通过 `checkGrant(grant, request, now)`
|
|
26
|
+
(`src/grant/capabilityGrant.ts`)。步骤 kind 就是 grant 动作:一个 grant 列出它授权的
|
|
27
|
+
步骤 kind,例如 `--action npm-publish --action version-tag`。该判定是 fail-closed
|
|
28
|
+
的——每个拒绝都带一个机器可读原因并停止这次运行:
|
|
29
|
+
|
|
30
|
+
| 原因 | 含义 |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `grant-missing` | 没有 grant 记录绑定到该工作流(引擎级)。 |
|
|
33
|
+
| `grant-revoked` | 该 grant 已被 operator 撤销。 |
|
|
34
|
+
| `grant-expired` | 墙钟已过该 grant 的 `expiresAt`。 |
|
|
35
|
+
| `grant-uses-exhausted` | 该 grant 的 `maxUses` 已被消耗。 |
|
|
36
|
+
| `grant-action-not-allowed` | 步骤 kind 不在该 grant 的动作中。 |
|
|
37
|
+
| `grant-parameter-missing` | 步骤缺少一个受约束参数。 |
|
|
38
|
+
| `grant-parameter-value-not-allowed` | 一个受约束参数的取值越界。 |
|
|
39
|
+
| `grant-irreversibility-exceeds-ceiling` | 该步骤比 grant 的上限更不可逆。 |
|
|
40
|
+
|
|
41
|
+
附加规则:
|
|
42
|
+
|
|
43
|
+
- **每次获授权提交消耗一次。** 引擎在成功判定与外部调用之间记录一次 grant 使用,
|
|
44
|
+
因此一个 `maxUses` grant 会在那次将要超额的尝试上 fail closed。
|
|
45
|
+
- **不可逆步骤需要一个已确认的前缀。** 一个标记为 `irreversible` 的步骤额外要求此前
|
|
46
|
+
每个步骤都是 `succeeded`;否则该步骤以 `prerequisite-not-confirmed` 失败并停止运行。
|
|
47
|
+
这正是让 `npm-publish` 不会跟在一个失败的 PR 之后运行的机制。
|
|
48
|
+
- **拒绝会被记录。** 当一个待处理步骤被拒绝时,引擎会启动并把该步骤失败,日志里带上
|
|
49
|
+
这次拒绝,因此 `workflow status` 能准确显示授权在哪里停下。
|
|
50
|
+
- **重新绑定。** 一个被撤销、过期或过窄的 grant 不会让工作流走进死胡同。签发一个新
|
|
51
|
+
grant 并用 `yui task workflow resume <task> <workflow> --grant <new-grant>` 恢复;
|
|
52
|
+
跨越这次重新绑定,计划、来源和所有已确认的步骤证据都不可变。
|
|
53
|
+
|
|
54
|
+
## 稳定的 Task-final Review 合同
|
|
55
|
+
|
|
56
|
+
兼容的 CLI 包更新和 Controller 替换不改变一个活动 Task 的 final-review 能力。受管
|
|
57
|
+
Session 使用普通的 `yui` 命令,兼容性由协议和存储身份检查,一个替换 Leader 呈现由
|
|
58
|
+
持久 Task 证据已确立的合同。不需要任何版本感知的 Operator 动作。Candidate 和
|
|
59
|
+
Task-final 的 ReviewRound 记录必须全部携带那唯一的合同。冲突的记录 fail closed;
|
|
60
|
+
不存在重新绑定事件、恢复命令或第二套合同状态机。
|
|
61
|
+
|
|
62
|
+
## CLI 与 Controller 发布边界
|
|
63
|
+
|
|
64
|
+
全局 `yui` 命令是稳定的用户与受管 Session 接口。对普通命令,它不跟随
|
|
65
|
+
`runtime/active-release.json`:那个指针选择的是 Controller 发布,而不是 CLI 包。这让
|
|
66
|
+
CLI、Operator Session 和 Controller 替换保持兼容,而不必把每个命令钉在一个不可变构建上。
|
|
67
|
+
|
|
68
|
+
一个源码 checkout 或以其他方式未经验证的本地 CLI 不是这个已发布接口。当 `YUI_HOME`
|
|
69
|
+
已经命名了一个活动发布时,这样的 CLI 会在打开存储之前失败,并报告它的构建/来源、
|
|
70
|
+
持久的 Home 身份以及它的调用类别。`make install-local` 仍默认使用 checkout 隔离的
|
|
71
|
+
`output/dev/home`;把那个 launcher 显式指向一个发布拥有的 Home 会被拒绝。
|
|
72
|
+
|
|
73
|
+
一个显式的 `yui release activate <release-id|build-id>` 是唯一的例外。全局 CLI 校验
|
|
74
|
+
已安装的目标发布及其匹配的冒烟回执,然后把未改动的激活参数委派给那个目标的
|
|
75
|
+
`dist/cli.js`。因此目标发布拥有完整的交接协议和超时层级。一次无目标的激活、help、
|
|
76
|
+
`--json` 以及其他每个命令都留在全局 CLI 上。激活不新增另一条普通 CLI 路由路径。
|
|
77
|
+
|
|
78
|
+
## 步骤目录
|
|
79
|
+
|
|
80
|
+
计划是一个固定、预先声明的操作子集。每个计划条目有一个 id(工作流内唯一)、一个
|
|
81
|
+
kind、可选 params,以及一个可选的不可逆级别(`none` | `reversible` | `irreversible`)。
|
|
82
|
+
|
|
83
|
+
| Kind | 外部效果 | 权威身份 |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| `pr-create-or-reuse` | 创建发布 PR,或复用一个针对该 head 的开放 PR。 | `pull-request` 号 |
|
|
86
|
+
| `ci-confirm` | 读取该来源 ref 的 CI 结论;仅在 `success` 时成功。 | — |
|
|
87
|
+
| `merge` | 合并具名 PR(默认 squash)。 | — |
|
|
88
|
+
| `version-tag` | 创建并推送带注释的版本 tag。 | `git-tag` 名 |
|
|
89
|
+
| `npm-publish` | 把 tarball 发布到 registry。 | `npm-package` 版本 |
|
|
90
|
+
| `fresh-install-smoke` | 从 registry 安装并运行已发布的包。 | — |
|
|
91
|
+
| `cli-update` | 通过既有更新编排器更新 Yui CLI/Controller home。 | `controller-home` |
|
|
92
|
+
| `controller-replace` | 停止并重启 file-task Controller。 | — |
|
|
93
|
+
| `project-migrate` | 通过既有的 project 命令运行 Project 迁移。 | — |
|
|
94
|
+
| `post-verify` | 运行一个任意的验证命令。 | — |
|
|
95
|
+
|
|
96
|
+
步骤可以引用更早的证据:一个 param 值为 `$externalId:<step-id>` 时,会在运行时解析为
|
|
97
|
+
被引用步骤已确认的 external id,因此一个 `merge` 步骤可以消费 `pr` 步骤产生的 PR 号,
|
|
98
|
+
而 operator 事先并不需要知道它。对一个未确认步骤的引用会让运行失败,而不是猜测。
|
|
99
|
+
|
|
100
|
+
## 恢复与 resume 语义
|
|
101
|
+
|
|
102
|
+
一次运行总是从 **resume 游标**开始:第一个状态非终态(`succeeded` 或 `skipped`)的
|
|
103
|
+
计划步骤。没有“从头再来”——已确认的步骤绝不重跑。
|
|
104
|
+
|
|
105
|
+
因为每次状态转换都在下一次外部调用之前持久化,所以任意一点的进程退出都是可恢复的:
|
|
106
|
+
重新调用 `run`(或 `resume`),引擎就从第一个未确认步骤继续。`--max-steps <n>` 限定
|
|
107
|
+
单次运行;一次在工作流中途耗尽预算的运行返回 `budget-exhausted`,下一次调用继续。
|
|
108
|
+
|
|
109
|
+
在途步骤通过**权威身份查询**解决,绝不盲目重新提交:
|
|
110
|
+
|
|
111
|
+
- 一个留在 `running` 或 `unknown` 的步骤,先按其记录的 `externalIdentity` 查询。
|
|
112
|
+
- `exists` → 该步骤到达 `succeeded`,且**没有第二次提交**(`unknown` 被确认,
|
|
113
|
+
`running` 被完成)。
|
|
114
|
+
- `unknown` → 运行以结果 `unknown` 停止;在其命运不可知期间,该步骤绝不被重新提交。
|
|
115
|
+
- `absent` → 效果从未落地,因此该步骤被重试(一个 `running` 步骤会记录这次恢复尝试)。
|
|
116
|
+
- 一个**没有** external identity 的 `running` 步骤在记录提交结果之前就崩溃了。一个
|
|
117
|
+
不可逆步骤无论如何都通过端口查询(适配器咨询其持久幂等存储):`exists` 不经第二次
|
|
118
|
+
提交确认该步骤,`unknown` 以 `unconfirmed` 停止,只有权威的 `absent` 才恰好重试该
|
|
119
|
+
步骤一次。一个可逆步骤总是落到重试,并沿用同一个幂等键。
|
|
120
|
+
- 一次**没有** external identity 的超时把该步骤标记为 `unknown`(unconfirmed),因此
|
|
121
|
+
它绝不被盲目重新提交;在 resume 时它以 `unconfirmed` fail closed。
|
|
122
|
+
- 一个 `failed` 步骤在下一次运行时被重试;它的 `attempts` 计数和日志按尝试增长。
|
|
123
|
+
|
|
124
|
+
运行结果:`succeeded`、`failed`、`unknown`、`unauthorized`、`unconfirmed`、
|
|
125
|
+
`budget-exhausted`。每个都带一个机器可读的 `stopReason`(例如 `unknown:publish`、
|
|
126
|
+
`unauthorized:grant-revoked`、`budget-exhausted:verify`)以及该次运行尝试过的步骤 id
|
|
127
|
+
列表。
|
|
128
|
+
|
|
129
|
+
## 幂等键合同
|
|
130
|
+
|
|
131
|
+
每个步骤的幂等键在**创建时预先声明**且永不改变:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
<taskId>/<workflowId>/<stepId>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
该键被传给该步骤的每一次 `executeStep` 调用,包括在一次确认为 absent 的超时之后的
|
|
138
|
+
重试。端口合同要求 `executeStep` 在同一键下是幂等的:一次重试尝试不得产生第二次
|
|
139
|
+
副作用。引擎侧的合同更严格——它绝不为一个已标记 `unknown` 的步骤调用 `executeStep`,
|
|
140
|
+
而是按记录的身份重新查询。fake 记录每一个键,因此测试套件直接证明至多一次执行。
|
|
141
|
+
|
|
142
|
+
## Operator 指南
|
|
143
|
+
|
|
144
|
+
Session 权威依据当前持久绑定检查。Telemetry 按 Role/AgentRun 分组,进程 owner 使用
|
|
145
|
+
PID/start 身份。存储变更遵循[唯一的显式升级边界](sqlite-control-plane-design.zh-CN.md);
|
|
146
|
+
普通命令绝不改写 Home schema。
|
|
147
|
+
|
|
148
|
+
grant 的签发与撤销是不可逆权威操作。它们需要当前已登记的全局 Operator 对话。它的
|
|
149
|
+
原生 session ID 必须与持久的活动 session 绑定匹配:Codex 命令在存在时使用
|
|
150
|
+
`CODEX_THREAD_ID`,否则使用 `YUI_NATIVE_SESSION_ID`;Claude 使用 `YUI_NATIVE_SESSION_ID`。
|
|
151
|
+
Host generation 和启动时的 Agent 标签不是调用者身份。通过另一个入口恢复同一段对话
|
|
152
|
+
不撤销其权威。一个未登记、被替换或已结束的对话没有这种权威。一个受管的 Task Agent
|
|
153
|
+
不能自签发或自撤销 grant,清空子进程环境也不赋予用户权威。被记录的授权者/撤销者
|
|
154
|
+
绑定到那个 Operator session(`operator:<agent-id>`);不存在可伪造的 `--granter`/`--by`
|
|
155
|
+
标签。
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
# 1. Operator session 为发布链签发权威。
|
|
159
|
+
yui task grant issue task-15 \
|
|
160
|
+
--action pr-create-or-reuse --action npm-publish --action post-verify \
|
|
161
|
+
--irreversibility-ceiling irreversible
|
|
162
|
+
|
|
163
|
+
# 2. 针对确切来源和预先声明的计划创建工作流。
|
|
164
|
+
# npm-publish 步骤需要一个内容寻址的来源 artifact:不可变的工作流来源以后
|
|
165
|
+
# 永远无法再获得它,因此没有 --source-artifact 的计划在创建时即被拒绝。
|
|
166
|
+
yui task workflow create task-15 \
|
|
167
|
+
--grant capability-grant-1 \
|
|
168
|
+
--source-repo acme/widget --source-commit abc1234deadbeef0000000000000000000000000 \
|
|
169
|
+
--source-artifact widget-1.0.0.tgz@sha512-<base64-integrity> \
|
|
170
|
+
--step pr:pr-create-or-reuse \
|
|
171
|
+
--step publish:npm-publish --step-irreversibility publish=irreversible \
|
|
172
|
+
--step-param publish:tarball=./dist/widget-1.0.0.tgz \
|
|
173
|
+
--step verify:post-verify --step-param verify:command='yui --version'
|
|
174
|
+
|
|
175
|
+
# 3. 运行(或 resume)并检查。
|
|
176
|
+
yui task workflow run task-15 release-workflow-1
|
|
177
|
+
yui task workflow resume task-15 release-workflow-1 [--grant capability-grant-2] [--max-steps 1]
|
|
178
|
+
yui task workflow status task-15 release-workflow-1
|
|
179
|
+
|
|
180
|
+
# 4. 随时撤销权威;下一个步骤以 unauthorized 停止。
|
|
181
|
+
yui task grant revoke task-15 capability-grant-1
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`workflow status` 渲染每个步骤的状态、尝试次数和已确认的 external id,因此 operator
|
|
185
|
+
能准确看到一次发布在哪里停下以及为什么。
|
|
186
|
+
|
|
187
|
+
## 真实资源边界
|
|
188
|
+
|
|
189
|
+
真实执行只通过 `yui` CLI 发生,它接上真实适配器(`createReleaseWorkflowPorts`)。该
|
|
190
|
+
适配器是既有原子操作——`gh`、`npm`、`git`、CLI 更新编排器、Controller stop/restart 和
|
|
191
|
+
`project migrate`——之上的一层薄壳,并且只在一个人类授权者签发了对每个步骤都通过
|
|
192
|
+
`checkGrant` 的显式 CapabilityGrant 时才运行。一个本地测试请求绝不替代那份权威。
|
|
193
|
+
|
|
194
|
+
由 tag 触发的 `publish.yml` 工作流是唯一维护的发布冒烟。它复用那个通过了 core CI 的
|
|
195
|
+
确切 commit,只增加发布所需的产物装配、全新安装和 provenance 检查。
|
|
196
|
+
|
|
197
|
+
该工作流通过 npm Trusted Publishing(OIDC)认证,因此发布身份存在于 tag 之外的两处:
|
|
198
|
+
`repository`、`bugs` 和 `homepage` 由 `assemble-runtime-package.mjs` 从源 `package.json`
|
|
199
|
+
逐字复制进已发布 manifest,而该包的 npm Trusted Publisher 条目点明 GitHub owner、
|
|
200
|
+
仓库、工作流文件和环境。npm 在接受 provenance 之前会区分大小写地将 `repository.url`
|
|
201
|
+
与正在构建的仓库比较。因此重命名或转移 GitHub 仓库时,必须连同重命名一起更新这些 URL
|
|
202
|
+
和 npm Trusted Publisher 条目;否则下一个 tag 会走到 `npm publish` 并在那里失败——此时
|
|
203
|
+
tag 和已过门的构建都已成功。
|
|
204
|
+
|
|
205
|
+
## 适配器安全加固
|
|
206
|
+
|
|
207
|
+
真实适配器(`createReleaseWorkflowPorts`)在引擎的 grant 检查之外再施加额外防护:
|
|
208
|
+
|
|
209
|
+
- **Tarball 选项注入。** 一个看起来像选项的 tarball 路径(以 `-` 开头)在任何子进程
|
|
210
|
+
——`tar -xOf` manifest 检视和 `npm publish`——看到它之前就被拒绝,因此一个精心构造的
|
|
211
|
+
路径永远不会被当作 flag 解释。
|
|
212
|
+
- **Tarball TOCTOU。** 在校验冻结的 `source.artifact.integrity` 之后,被校验的字节被
|
|
213
|
+
快照到一个工作流私有、只读的临时文件。`tar -xOf` manifest 检视和 `npm publish` 都
|
|
214
|
+
读取该快照,而不是活的 tarball 路径,因此校验之后对原文件的替换不能改变所发布的内容。
|
|
215
|
+
步骤完成时移除该快照。
|
|
216
|
+
- **钉住的外部命令。** 适配器在构造时通过 `resolveExecutable` 把它 shell 调用的外部
|
|
217
|
+
命令(`gh`、`git`、`npm`、`tar`、`sh`)解析为绝对路径,只走一次调用者的 `PATH`。每次
|
|
218
|
+
子进程调用都使用解析后的路径,因此之后的 `PATH` 变化(或被操纵的工作目录)不能把一次
|
|
219
|
+
发布效果重定向到另一个二进制。一个无法解析的命令返回一个合成失败(exit 127),不调用
|
|
220
|
+
任何二进制。
|
|
221
|
+
- **钉住的 cli-update 激活目标。** 在不可逆的更新效果之前,适配器把确切的激活目标——
|
|
222
|
+
Home 加上全局 npm 前缀(`bin/yui`)——持久化到 Home 下的一个持久文件
|
|
223
|
+
(`release/cli-update-identity/<idempotency-key>.json`)。一次硬退出的恢复查询(一个
|
|
224
|
+
没有记录身份的步骤)读取这个文件并调用那个钉住的目标;如果该文件不存在(进程在预效果
|
|
225
|
+
持久化之前退出),查询返回 `unknown`,而不是从 resume 调用者的 `npm prefix --global`
|
|
226
|
+
或 `PATH` 推导目标,因此 resume 环境中的另一个安装不能替这个步骤背书。
|
|
227
|
+
- **Controller 生命周期校验。** 一次 `cli-update` 恢复查询会证明替换后的 Controller
|
|
228
|
+
确实拥有目标 Home:它运行 `yui --json controller status`(`YUI_HOME` 钉在记录的 Home
|
|
229
|
+
上),并要求一个 `current` controller 资源,其 `yuiHome` 解析到那个 Home,然后运行
|
|
230
|
+
`yui --json controller identity`,并要求已认证的 Controller 身份与已激活的产物匹配:
|
|
231
|
+
Node.js 可执行路径、由钉住的全局二进制派生的确切 Controller 入口点,以及包版本。仅有
|
|
232
|
+
二进制健康(doctor、`--version`)绝不确认这次交接,任何不可证明的状态都返回 `unknown`。
|
|
233
|
+
这适用于带身份的查询和硬退出查询(一个没有记录身份的步骤)两者。
|
|
234
|
+
- **npm integrity 比较。** 一次 `npm-publish` 恢复查询不止步于已发布版本:它通过
|
|
235
|
+
`npm view <pkg>@<version> dist.integrity` 取 `dist.integrity`,并与冻结的
|
|
236
|
+
`source.artifact.integrity` 逐字节比较。匹配则确认该步骤;同一版本但字节不同是一个
|
|
237
|
+
冲突,返回 `unknown`(绝不确认,绝不重新发布);一个缺失的版本是 `absent`。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<p align="right"><strong>English</strong> | <a href="./roles-and-configuration.zh-CN.md">简体中文</a></p>
|
|
2
|
+
|
|
3
|
+
# Roles, Profiles and execution configuration
|
|
4
|
+
|
|
5
|
+
## Responsibilities
|
|
6
|
+
|
|
7
|
+
Agent selects an execution component, connection plan and launch environment.
|
|
8
|
+
Role selects an active Agent binding and portable behavior. Each binding retains
|
|
9
|
+
independent runtime options. A Role can keep multiple bindings without creating
|
|
10
|
+
parallel writers or sharing one binding's credentials/configuration with another.
|
|
11
|
+
|
|
12
|
+
Task Roles are Task-local; global Roles provide configured defaults and global
|
|
13
|
+
conversations. Role identity/configuration is not a writable runtime status.
|
|
14
|
+
Session and Provider observations describe actual activity.
|
|
15
|
+
|
|
16
|
+
An explicit Task-final Review uses an existing Task-local Reviewer Role without
|
|
17
|
+
requiring a same-named global Role. A global Role is a creation template only
|
|
18
|
+
when the requested Task Role does not exist. Availability, producer separation
|
|
19
|
+
and the frozen review candidate still apply.
|
|
20
|
+
|
|
21
|
+
## Profiles
|
|
22
|
+
|
|
23
|
+
An Agent Profile combines portable behavior—instructions, Skills and access
|
|
24
|
+
intent—with runtime intent. Runtime either follows the current Global Worker
|
|
25
|
+
binding or explicitly selects an Agent with optional model and effort.
|
|
26
|
+
`config profile reset` supplies `worker`, `explorer`, `implementer` and `reviewer`.
|
|
27
|
+
|
|
28
|
+
Creating a Task Role from a Profile freezes its resolved behavior and binding.
|
|
29
|
+
Later Profile or Global Worker edits do not rewrite existing Task Roles.
|
|
30
|
+
Reapplying a Profile is an explicit configuration change. The selected Agent
|
|
31
|
+
must match the target binding; explicit Role options override the corresponding
|
|
32
|
+
template fields. A Profile is not a Session, workspace owner or resource grant.
|
|
33
|
+
|
|
34
|
+
Native children inherit their parent Agent and authority. A Profile can guide
|
|
35
|
+
their behavior; model/effort overrides require actual native tool support.
|
|
36
|
+
They do not acquire Yui Roles, independent Assignments or broader scope.
|
|
37
|
+
|
|
38
|
+
## Desired, effective and observed
|
|
39
|
+
|
|
40
|
+
Desired settings are next-launch intent. An AgentRun and Session capture the
|
|
41
|
+
effective launch: Agent/component, protocol, model, effort, permission strategy,
|
|
42
|
+
workspace/environment, Role context and planning/delivery authority.
|
|
43
|
+
|
|
44
|
+
Live run-configuration inspection separately reports what the Agent actually
|
|
45
|
+
states. Unsupported and unknown are explicit; an accepted setter without a
|
|
46
|
+
reported current value is not an observed match. Configuration reads do not
|
|
47
|
+
modify the Agent to make the observation agree with desired settings.
|
|
48
|
+
|
|
49
|
+
Worker binding changes preserve an active Assignment's Agent and effective
|
|
50
|
+
snapshot; subsequent explicit dispatch uses the current selection. Leader
|
|
51
|
+
replacement revokes the previous management entry without rewriting Worker
|
|
52
|
+
Assignments. Changing desired configuration does not hot-mutate the native
|
|
53
|
+
Session. An explicit `task role session new` request handles old runtime
|
|
54
|
+
cleanup before selecting a fresh Session; it does not require manually settling
|
|
55
|
+
Run status first. A useful Session may be reused, but it is never the only
|
|
56
|
+
holder of Task context.
|
|
57
|
+
|
|
58
|
+
## Permissions and Project context
|
|
59
|
+
|
|
60
|
+
Provider permission strategy, Profile access intent and Project write scope are
|
|
61
|
+
different contracts. Provider bypass does not grant writes to another Project.
|
|
62
|
+
Managed workspace owner, exact Assignment and resource grants enforce Yui
|
|
63
|
+
operations; broad native permissions are not an OS sandbox.
|
|
64
|
+
|
|
65
|
+
Yui supplies its generic Role Skills and Context pointers. Project Skills remain
|
|
66
|
+
ordinary Project files discovered natively by the Agent. Project Knowledge is
|
|
67
|
+
maintained under `YUI_HOME`; copying repository material into a prompt does not
|
|
68
|
+
make it authoritative Knowledge.
|
|
69
|
+
|
|
70
|
+
## Native authentication
|
|
71
|
+
|
|
72
|
+
Account configuration outlives a Session. Yui preserves `HOME` and the selected
|
|
73
|
+
`CLAUDE_CONFIG_DIR`; new/resumed Sessions do not copy, clear, or fabricate native
|
|
74
|
+
login, key-approval, or onboarding records.
|
|
75
|
+
|
|
76
|
+
For Claude Code, standard API-key, base-URL, bearer/OAuth, model-alias and native
|
|
77
|
+
provider-selection environment variables are forwarded only to Claude. Values
|
|
78
|
+
remain in the Controller's replaceable runtime environment and the child process,
|
|
79
|
+
not in Task/Role records. Unsetting a source and refreshing the Controller
|
|
80
|
+
environment removes it for subsequent launches. Other custom credential variables
|
|
81
|
+
still use explicit Agent environment bindings or native user settings; Yui does
|
|
82
|
+
not inherit the whole shell environment or infer cloud credentials.
|
|
83
|
+
|
|
84
|
+
Claude itself loads native settings and chooses between API keys, existing
|
|
85
|
+
helpers, login credentials, profiles and cloud authentication according to its
|
|
86
|
+
effective configuration. Yui does not inject `apiKeyHelper`, copy credential
|
|
87
|
+
files, or override the native authentication priority. Explicit `--settings`
|
|
88
|
+
paths and settings-source selections are passed through unchanged.
|
|
89
|
+
|
|
90
|
+
Fresh native configuration can still require Claude's initialization, key,
|
|
91
|
+
workspace and security confirmations, including access to initialization
|
|
92
|
+
services. Isolating `YUI_HOME` or replacing a Session does not require a fresh
|
|
93
|
+
native account directory. Managed Task execution uses Claude's non-interactive
|
|
94
|
+
stream-json path with the same native configuration ownership.
|
|
95
|
+
|
|
96
|
+
## Commands
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
yui config agent capabilities <agent-id>
|
|
100
|
+
yui config role show <global-role>
|
|
101
|
+
yui config profile show <profile>
|
|
102
|
+
yui task role add <task> <role> --profile <profile>
|
|
103
|
+
yui task role show <task> <role>
|
|
104
|
+
yui task role update <task> <role> --environment <preparation-id>
|
|
105
|
+
yui task role update <task> <role> --managed-environment
|
|
106
|
+
yui task role session inspect <task> <role>
|
|
107
|
+
yui task role session new <task> <role> --reason "<why a fresh Session is useful>"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
On Role creation, explicit Agent settings require `--agent`. On update, omitted
|
|
111
|
+
`--agent` targets the active binding; a named binding is updated without being
|
|
112
|
+
activated. `task role bind` changes selection. A live Session requires the
|
|
113
|
+
command's explicit confirmation before desired settings are changed.
|
|
114
|
+
|
|
115
|
+
For native configuration and implementation limits, see [Provider runtime](provider-runtime.md).
|