@tacuchi/agent-workflow-cli 25.6.1 → 25.8.0
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 +4398 -0
- package/README.md +1 -1
- package/dist/adapters/git-cli.js +160 -13
- package/dist/adapters/git-cli.js.map +1 -1
- package/dist/adapters/node-file-system.js +39 -2
- package/dist/adapters/node-file-system.js.map +1 -1
- package/dist/application/branch-resolver.js +34 -1
- package/dist/application/branch-resolver.js.map +1 -1
- package/dist/application/capability/readiness.js +26 -10
- package/dist/application/capability/readiness.js.map +1 -1
- package/dist/application/capability/wrapper.js +10 -3
- package/dist/application/capability/wrapper.js.map +1 -1
- package/dist/application/checkpoint-write-service.js +161 -39
- package/dist/application/checkpoint-write-service.js.map +1 -1
- package/dist/application/dates.js +12 -0
- package/dist/application/dates.js.map +1 -1
- package/dist/application/dev-only-services.js +5 -7
- package/dist/application/dev-only-services.js.map +1 -1
- package/dist/application/doctor/provider-mcps.js +1 -1
- package/dist/application/doctor/provider-mcps.js.map +1 -1
- package/dist/application/fix-git-service.js +83 -13
- package/dist/application/fix-git-service.js.map +1 -1
- package/dist/application/flow/advance.js +64 -9
- package/dist/application/flow/advance.js.map +1 -1
- package/dist/application/flow/annul-service.js +159 -0
- package/dist/application/flow/annul-service.js.map +1 -0
- package/dist/application/flow/checkout-observation.js +49 -0
- package/dist/application/flow/checkout-observation.js.map +1 -1
- package/dist/application/flow/close-at-boundary.js +144 -0
- package/dist/application/flow/close-at-boundary.js.map +1 -0
- package/dist/application/flow/flow-service.js +213 -49
- package/dist/application/flow/flow-service.js.map +1 -1
- package/dist/application/flow/internal-actions.js +87 -4
- package/dist/application/flow/internal-actions.js.map +1 -1
- package/dist/application/flow/internal-drive.js +70 -33
- package/dist/application/flow/internal-drive.js.map +1 -1
- package/dist/application/flow/reopen-run.js +152 -0
- package/dist/application/flow/reopen-run.js.map +1 -0
- package/dist/application/flow/run-journey.js +11 -8
- package/dist/application/flow/run-journey.js.map +1 -1
- package/dist/application/flow/run-projection.js +37 -10
- package/dist/application/flow/run-projection.js.map +1 -1
- package/dist/application/flow/run-state-service.js +109 -36
- package/dist/application/flow/run-state-service.js.map +1 -1
- package/dist/application/flow/submit.js +217 -188
- package/dist/application/flow/submit.js.map +1 -1
- package/dist/application/git-flow-service.js +325 -72
- package/dist/application/git-flow-service.js.map +1 -1
- package/dist/application/hook-sql-mutation-guard.js +290 -27
- package/dist/application/hook-sql-mutation-guard.js.map +1 -1
- package/dist/application/host-memory/claude-code.js +65 -0
- package/dist/application/host-memory/claude-code.js.map +1 -0
- package/dist/application/host-memory/codex.js +225 -0
- package/dist/application/host-memory/codex.js.map +1 -0
- package/dist/application/host-memory/destination.js +52 -0
- package/dist/application/host-memory/destination.js.map +1 -0
- package/dist/application/host-memory/reader.js +9 -0
- package/dist/application/host-memory/reader.js.map +1 -0
- package/dist/application/host-memory/report.js +136 -0
- package/dist/application/host-memory/report.js.map +1 -0
- package/dist/application/lifecycle-target.js +3 -1
- package/dist/application/lifecycle-target.js.map +1 -1
- package/dist/application/local-proposal.js +13 -3
- package/dist/application/local-proposal.js.map +1 -1
- package/dist/application/logging/log-events.js +22 -0
- package/dist/application/logging/log-events.js.map +1 -1
- package/dist/application/parsers/spec-relation.js +20 -7
- package/dist/application/parsers/spec-relation.js.map +1 -1
- package/dist/application/persist-service.js +45 -1
- package/dist/application/persist-service.js.map +1 -1
- package/dist/application/plan-exec-batch-service.js +47 -2
- package/dist/application/plan-exec-batch-service.js.map +1 -1
- package/dist/application/plan-lineage-seal.js +93 -0
- package/dist/application/plan-lineage-seal.js.map +1 -0
- package/dist/application/plan-lint-service.js +153 -0
- package/dist/application/plan-lint-service.js.map +1 -0
- package/dist/application/prod-consent.js +75 -0
- package/dist/application/prod-consent.js.map +1 -0
- package/dist/application/reseal-service.js +9 -3
- package/dist/application/reseal-service.js.map +1 -1
- package/dist/application/self/install-skill.js +40 -12
- package/dist/application/self/install-skill.js.map +1 -1
- package/dist/application/self/install-targets.js +73 -2
- package/dist/application/self/install-targets.js.map +1 -1
- package/dist/application/self/uninstall.js +21 -7
- package/dist/application/self/uninstall.js.map +1 -1
- package/dist/application/self/update-self.js +204 -8
- package/dist/application/self/update-self.js.map +1 -1
- package/dist/application/session-create-service.js +49 -0
- package/dist/application/session-create-service.js.map +1 -1
- package/dist/application/session-custody-recorder.js +4 -0
- package/dist/application/session-custody-recorder.js.map +1 -1
- package/dist/application/session-narrative.js +36 -0
- package/dist/application/session-narrative.js.map +1 -1
- package/dist/application/session-resolver.js +28 -9
- package/dist/application/session-resolver.js.map +1 -1
- package/dist/application/session-resume-service.js +65 -16
- package/dist/application/session-resume-service.js.map +1 -1
- package/dist/application/source-boundary-policy.js +168 -34
- package/dist/application/source-boundary-policy.js.map +1 -1
- package/dist/application/working-branch-service.js +68 -0
- package/dist/application/working-branch-service.js.map +1 -0
- package/dist/application/workline-graph.js +7 -2
- package/dist/application/workline-graph.js.map +1 -1
- package/dist/cli/ascii.js +84 -0
- package/dist/cli/ascii.js.map +1 -0
- package/dist/cli/commands/amend.js +1 -0
- package/dist/cli/commands/amend.js.map +1 -1
- package/dist/cli/commands/capability.js +15 -0
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/check-branch.js +1 -0
- package/dist/cli/commands/check-branch.js.map +1 -1
- package/dist/cli/commands/checkpoint-read.js +1 -0
- package/dist/cli/commands/checkpoint-read.js.map +1 -1
- package/dist/cli/commands/checkpoint-write.js +22 -9
- package/dist/cli/commands/checkpoint-write.js.map +1 -1
- package/dist/cli/commands/claims.js +1 -0
- package/dist/cli/commands/claims.js.map +1 -1
- package/dist/cli/commands/code-scan.js +1 -0
- package/dist/cli/commands/code-scan.js.map +1 -1
- package/dist/cli/commands/context-budget.js +1 -0
- package/dist/cli/commands/context-budget.js.map +1 -1
- package/dist/cli/commands/context-plan.js +1 -0
- package/dist/cli/commands/context-plan.js.map +1 -1
- package/dist/cli/commands/cut-intent.js +1 -0
- package/dist/cli/commands/cut-intent.js.map +1 -1
- package/dist/cli/commands/designs.js +1 -0
- package/dist/cli/commands/designs.js.map +1 -1
- package/dist/cli/commands/dev-only.js +4 -0
- package/dist/cli/commands/dev-only.js.map +1 -1
- package/dist/cli/commands/doctor.js +5 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/export.js +13 -0
- package/dist/cli/commands/export.js.map +1 -1
- package/dist/cli/commands/fix-git.js +46 -6
- package/dist/cli/commands/fix-git.js.map +1 -1
- package/dist/cli/commands/flow.js +61 -5
- package/dist/cli/commands/flow.js.map +1 -1
- package/dist/cli/commands/generate-launch.js +1 -0
- package/dist/cli/commands/generate-launch.js.map +1 -1
- package/dist/cli/commands/git-flow.js +104 -29
- package/dist/cli/commands/git-flow.js.map +1 -1
- package/dist/cli/commands/history-update.js +7 -9
- package/dist/cli/commands/history-update.js.map +1 -1
- package/dist/cli/commands/hook.js +11 -3
- package/dist/cli/commands/hook.js.map +1 -1
- package/dist/cli/commands/host-doctor.js +1 -0
- package/dist/cli/commands/host-doctor.js.map +1 -1
- package/dist/cli/commands/host-memory.js +26 -0
- package/dist/cli/commands/host-memory.js.map +1 -0
- package/dist/cli/commands/index.js +6 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/mcp.js +30 -4
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/merge-state.js +1 -0
- package/dist/cli/commands/merge-state.js.map +1 -1
- package/dist/cli/commands/multiroot.js +16 -0
- package/dist/cli/commands/multiroot.js.map +1 -1
- package/dist/cli/commands/persist.js +6 -0
- package/dist/cli/commands/persist.js.map +1 -1
- package/dist/cli/commands/plan.js +51 -0
- package/dist/cli/commands/plan.js.map +1 -0
- package/dist/cli/commands/plugin-cache.js +4 -0
- package/dist/cli/commands/plugin-cache.js.map +1 -1
- package/dist/cli/commands/plugin-doctor.js +3 -0
- package/dist/cli/commands/plugin-doctor.js.map +1 -1
- package/dist/cli/commands/project-md-upsert.js +3 -0
- package/dist/cli/commands/project-md-upsert.js.map +1 -1
- package/dist/cli/commands/release-data.js +12 -0
- package/dist/cli/commands/release-data.js.map +1 -1
- package/dist/cli/commands/release-pass.js +10 -0
- package/dist/cli/commands/release-pass.js.map +1 -1
- package/dist/cli/commands/remove-source.js +1 -0
- package/dist/cli/commands/remove-source.js.map +1 -1
- package/dist/cli/commands/reseal.js +1 -0
- package/dist/cli/commands/reseal.js.map +1 -1
- package/dist/cli/commands/resume-summary.js +9 -2
- package/dist/cli/commands/resume-summary.js.map +1 -1
- package/dist/cli/commands/resume.js +1 -0
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/retirement.js +1 -0
- package/dist/cli/commands/retirement.js.map +1 -1
- package/dist/cli/commands/self.js +33 -0
- package/dist/cli/commands/self.js.map +1 -1
- package/dist/cli/commands/session-artifacts.js +1 -0
- package/dist/cli/commands/session-artifacts.js.map +1 -1
- package/dist/cli/commands/session-close.js +129 -30
- package/dist/cli/commands/session-close.js.map +1 -1
- package/dist/cli/commands/session-create.js +1 -0
- package/dist/cli/commands/session-create.js.map +1 -1
- package/dist/cli/commands/session-resume.js +1 -0
- package/dist/cli/commands/session-resume.js.map +1 -1
- package/dist/cli/commands/sessions.js +1 -0
- package/dist/cli/commands/sessions.js.map +1 -1
- package/dist/cli/commands/set-branch.js +41 -6
- package/dist/cli/commands/set-branch.js.map +1 -1
- package/dist/cli/commands/settle.js +1 -0
- package/dist/cli/commands/settle.js.map +1 -1
- package/dist/cli/commands/skill-index.js +1 -0
- package/dist/cli/commands/skill-index.js.map +1 -1
- package/dist/cli/commands/skills.js +1 -0
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/sources.js +1 -0
- package/dist/cli/commands/sources.js.map +1 -1
- package/dist/cli/commands/stack.js +1 -0
- package/dist/cli/commands/stack.js.map +1 -1
- package/dist/cli/commands/status.js +1 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/tool.js +5 -21
- package/dist/cli/commands/tool.js.map +1 -1
- package/dist/cli/commands/unknown-flags.js +67 -16
- package/dist/cli/commands/unknown-flags.js.map +1 -1
- package/dist/cli/commands/visibility.js +1 -0
- package/dist/cli/commands/visibility.js.map +1 -1
- package/dist/cli/commands/workspace-init.js +12 -0
- package/dist/cli/commands/workspace-init.js.map +1 -1
- package/dist/cli/commands/workspace-migrate.js +2 -13
- package/dist/cli/commands/workspace-migrate.js.map +1 -1
- package/dist/cli/commands/worktree.js +1 -0
- package/dist/cli/commands/worktree.js.map +1 -1
- package/dist/cli/help-groups.js +41 -0
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/main.js +41 -58
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/output-mode.js +11 -2
- package/dist/cli/output-mode.js.map +1 -1
- package/dist/cli/parser.js +14 -5
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli/render.js +30 -1
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/tui/components/git-flow-actions.js +65 -20
- package/dist/cli/tui/components/git-flow-actions.js.map +1 -1
- package/dist/cli/tui/tabs/project-tab.js +101 -12
- package/dist/cli/tui/tabs/project-tab.js.map +1 -1
- package/dist/domain/changelog-contract.js +172 -0
- package/dist/domain/changelog-contract.js.map +1 -0
- package/dist/domain/database-tools.js +1 -189
- package/dist/domain/database-tools.js.map +1 -1
- package/dist/domain/flow/answer.js +11 -0
- package/dist/domain/flow/answer.js.map +1 -1
- package/dist/domain/flow/authority.js +153 -14
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/flow/execution-result.js +78 -7
- package/dist/domain/flow/execution-result.js.map +1 -1
- package/dist/domain/flow/journey-baseline.js +131 -0
- package/dist/domain/flow/journey-baseline.js.map +1 -0
- package/dist/domain/flow/route.js.map +1 -1
- package/dist/domain/flow/rules.js +5 -6
- package/dist/domain/flow/rules.js.map +1 -1
- package/dist/domain/flow/run-state.js +440 -89
- package/dist/domain/flow/run-state.js.map +1 -1
- package/dist/domain/harnesses.js +8 -1
- package/dist/domain/harnesses.js.map +1 -1
- package/dist/domain/host-memory/model.js +2 -0
- package/dist/domain/host-memory/model.js.map +1 -0
- package/dist/domain/host-memory/provenance.js +43 -0
- package/dist/domain/host-memory/provenance.js.map +1 -0
- package/dist/domain/host-memory/topic.js +26 -0
- package/dist/domain/host-memory/topic.js.map +1 -0
- package/dist/domain/lineage.js +11 -3
- package/dist/domain/lineage.js.map +1 -1
- package/dist/domain/proposal.js +7 -0
- package/dist/domain/proposal.js.map +1 -1
- package/dist/domain/session/custody.js.map +1 -1
- package/dist/domain/sql-lexer.js +240 -0
- package/dist/domain/sql-lexer.js.map +1 -0
- package/package.json +2 -2
- package/skills/w/SKILL.md +9 -9
- package/skills/w/commands/README.md +2 -1
- package/skills/w/commands/recall.md +39 -0
- package/skills/w/context/MANIFEST.json +4 -0
- package/skills/w/harness/HARNESS.md +12 -6
- package/skills/w/hooks/README.md +27 -16
- package/skills/w/loops/CHASSIS.md +16 -12
- package/skills/w/loops/CODE-POLICIES.md +11 -6
- package/skills/w/loops/plan-exec-loop/LOOP.md +9 -9
- package/skills/w/modules/COMPACTION.md +1 -1
- package/skills/w/roles/design/CONTRACT.md +10 -3
|
@@ -25,12 +25,12 @@ import { authorizeEffects, touchesTheWorld, } from "../../domain/capability/effe
|
|
|
25
25
|
import { AttemptLedger } from "../../domain/capability/protocol.js";
|
|
26
26
|
import { DEFAULT_CORE_DOCS_CANON, coreDocumentKindForPath } from "../../domain/docs-canon.js";
|
|
27
27
|
import { claimedSeal, parseFlowAnswer, spendsAttempt, } from "../../domain/flow/answer.js";
|
|
28
|
-
import { FIX_PREVIEW_TRANSITION, SETTLEMENT_READINGS, actionOf, alternativesOf, approvalGrantOf, effectsOf, internalActionOf, isRouteEvaluation, proposalContractOf, publishApprovalOf, routeControlOf, scopesSources, } from "../../domain/flow/authority.js";
|
|
28
|
+
import { FIX_PREVIEW_TRANSITION, SETTLEMENT_READINGS, actionOf, alternativesOf, approvalGrantOf, effectsOf, internalActionOf, isRouteEvaluation, occurrenceAt, proposalContractOf, publishApprovalOf, reentryOf, routeControlOf, scopesSources, } from "../../domain/flow/authority.js";
|
|
29
29
|
import { effectApprovalDigest } from "../../domain/flow/authorization.js";
|
|
30
30
|
import { PAUSE_LABEL, STOP_LABEL, stepOf, } from "../../domain/flow/directive.js";
|
|
31
|
-
import { executionVerdict } from "../../domain/flow/execution-result.js";
|
|
31
|
+
import { batchCreditVerdict, executionVerdict, } from "../../domain/flow/execution-result.js";
|
|
32
32
|
import { ROUTE_ACCEPT_LABEL, ROUTE_ADJUST_LABEL, isRouteDisposition, } from "../../domain/flow/route.js";
|
|
33
|
-
import { applyTransition, checkAgainstJourney,
|
|
33
|
+
import { applyTransition, checkAgainstJourney, iterationOf, restartInvocation, restatesLastEvent, sameIteration, settlementAmbiguous, withApproval, withAttempt, withBoundary, withContinuation, withDecisionPreparation, withEvent, withFixPreview, withHandoff, withObservation, withPlanExecBatchCredit, withPlanExecBatchStageForTransition, withProposal, withReentry, withRouteDecisions, withRouteProposal, withScope, withSelectedChoice, withSettlementDeclarations, } from "../../domain/flow/run-state.js";
|
|
34
34
|
import { withSpecBaseline } from "../../domain/lineage.js";
|
|
35
35
|
import { destinationsOf, observedEffects, sealProposal } from "../../domain/proposal.js";
|
|
36
36
|
import { baseDigest } from "../../domain/proposal.js";
|
|
@@ -38,19 +38,19 @@ import { reservationMarker } from "../../domain/reservation.js";
|
|
|
38
38
|
import { checkSafeRelativePath } from "../../domain/safe-path.js";
|
|
39
39
|
import { resolveCoreDocsCanon } from "../docs-canon-service.js";
|
|
40
40
|
import { readWorkspaceBlock } from "../parsers/project-block.js";
|
|
41
|
-
import { functionalSpecDigest } from "../parsers/spec-functional.js";
|
|
42
41
|
import { parseDerivedFromPath, parseSpecRelation } from "../parsers/spec-relation.js";
|
|
43
42
|
import { resolveWorkspaceRootFrom } from "../paths-service.js";
|
|
44
43
|
import { commitStoredPlanExecDecision, preparePlanExecDecision, } from "../plan-exec-decision-service.js";
|
|
44
|
+
import { planBoundaryAction, planGrammarAtEntry, planGrammarAtPublication, } from "../plan-lint-service.js";
|
|
45
45
|
import { semanticDigest } from "../semantic-operation/protocol.js";
|
|
46
46
|
import { resolveSessionTarget } from "../session-resolver.js";
|
|
47
|
-
import { sourceAliasesOfPlan,
|
|
48
|
-
import { actionDigest, advanceFlowRun, directiveFor, effectsOfTransition, resolveBoundary, } from "./advance.js";
|
|
49
|
-
import { observeCheckout, withObservedCheckouts } from "./checkout-observation.js";
|
|
47
|
+
import { sourceAliasesOfPlan, } from "../source-boundary-policy.js";
|
|
48
|
+
import { actionDigest, advanceFlowRun, awaitingCliRerun, directiveFor, effectsOfTransition, resolveBoundary, } from "./advance.js";
|
|
49
|
+
import { observeCheckout, observeScopedFingerprints, withObservedCheckouts, } from "./checkout-observation.js";
|
|
50
50
|
import { resolveCheckoutCandidates } from "./checkout-observation.js";
|
|
51
51
|
import { driveInternalActions } from "./internal-drive.js";
|
|
52
52
|
import { journeyForRun } from "./run-journey.js";
|
|
53
|
-
import { applyUnderLock, locateRun } from "./run-state-service.js";
|
|
53
|
+
import { applyUnderLock, locateRun, readRun } from "./run-state-service.js";
|
|
54
54
|
export async function submitFlow(fs, paths, input) {
|
|
55
55
|
const canon = await resolveCoreDocsCanon(fs, paths);
|
|
56
56
|
if (!canon.ok) {
|
|
@@ -119,125 +119,79 @@ export async function submitFlow(fs, paths, input) {
|
|
|
119
119
|
}
|
|
120
120
|
async function observe(fs, paths, raw, session, git) {
|
|
121
121
|
const root = await resolveWorkspaceRootFrom(fs, paths);
|
|
122
|
+
const plans = await observePlanArtifacts(fs, paths, raw);
|
|
122
123
|
return {
|
|
123
124
|
root,
|
|
124
125
|
destinations: await observeDestinations(fs, paths, raw),
|
|
125
126
|
scope: await observeScope(fs, paths, raw),
|
|
126
|
-
plans:
|
|
127
|
+
plans: plans.evidence,
|
|
127
128
|
checkouts: await observeCheckouts(fs, paths, session, git),
|
|
128
|
-
|
|
129
|
+
scoped: await observeBatchSources(fs, paths, session, git),
|
|
130
|
+
baselines: plans.baselines,
|
|
129
131
|
};
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
132
|
-
* The
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* paragraph must not turn this plan divergent and make it uncloseable.
|
|
137
|
-
*
|
|
138
|
-
* Every `null` here is a plan that stays UNSEALED, which is a legitimate
|
|
139
|
-
* reading: no `Derived from` path, two contradictory ones, a path that escapes
|
|
140
|
-
* the workspace, or a spec nobody can read. None of them is an error — a wrong
|
|
141
|
-
* seal would be.
|
|
134
|
+
* The scoped fingerprints a phase validation is judged against, read before the
|
|
135
|
+
* lock like every other observation. The run read here only decides WHETHER to
|
|
136
|
+
* measure: the decision under the lock re-reads it, and a run that moved in
|
|
137
|
+
* between answers a different seal and is refused as stale anyway.
|
|
142
138
|
*/
|
|
143
|
-
async function
|
|
144
|
-
|
|
145
|
-
if (specPath === null || !checkSafeRelativePath(specPath).ok)
|
|
146
|
-
return null;
|
|
147
|
-
const relation = parseSpecRelation(content, specDir);
|
|
148
|
-
if (relation.status !== "declared")
|
|
149
|
-
return null;
|
|
150
|
-
try {
|
|
151
|
-
const specText = await fs.readText(join(root, specPath));
|
|
152
|
-
return { path: specPath, number: relation.number, digest: functionalSpecDigest(specText) };
|
|
153
|
-
}
|
|
154
|
-
catch {
|
|
139
|
+
async function observeBatchSources(fs, paths, session, git) {
|
|
140
|
+
if (git === undefined)
|
|
155
141
|
return null;
|
|
156
|
-
|
|
142
|
+
// With a reader present, "not measured" is an empty map and never `null`: the
|
|
143
|
+
// judgment then finds every source unobservable and refuses, instead of
|
|
144
|
+
// skipping the one comparison the phase validation exists for.
|
|
145
|
+
const read = await readRun(fs, locateRun(paths, session));
|
|
146
|
+
if (!read.ok || read.state.boundary !== PHASE_VALIDATION || read.state.scope === null) {
|
|
147
|
+
return {};
|
|
148
|
+
}
|
|
149
|
+
const observed = await observeScopedFingerprints(fs, git, paths, session, read.state.scope.sources);
|
|
150
|
+
return observed.ok ? observed.base : {};
|
|
157
151
|
}
|
|
152
|
+
const PHASE_VALIDATION = "plan-exec.validation-execution";
|
|
158
153
|
/**
|
|
159
|
-
*
|
|
160
|
-
* from — read here, never taken from the sender.
|
|
154
|
+
* Judge the plan bytes a proposal is carrying, and derive the seal each one gets.
|
|
161
155
|
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
|
|
168
|
-
async function observeSpecBaselines(fs, paths, raw) {
|
|
169
|
-
const out = new Map();
|
|
170
|
-
let parsed;
|
|
171
|
-
try {
|
|
172
|
-
parsed = JSON.parse(raw);
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
return out;
|
|
176
|
-
}
|
|
177
|
-
const artifacts = parsed?.artifacts;
|
|
178
|
-
if (!Array.isArray(artifacts) || artifacts.length === 0)
|
|
179
|
-
return out;
|
|
180
|
-
const canon = await resolveCoreDocsCanon(fs, paths);
|
|
181
|
-
// An unreadable canon cannot tell a plan from anything else. Sealing on a
|
|
182
|
-
// guess would stamp a baseline into whatever the payload happened to carry.
|
|
183
|
-
if (!canon.ok)
|
|
184
|
-
return out;
|
|
185
|
-
const root = await resolveWorkspaceRootFrom(fs, paths);
|
|
186
|
-
for (const entry of artifacts) {
|
|
187
|
-
const path = entry?.path;
|
|
188
|
-
const content = entry?.content;
|
|
189
|
-
if (typeof path !== "string" || typeof content !== "string")
|
|
190
|
-
continue;
|
|
191
|
-
const relative = path.trim();
|
|
192
|
-
if (out.has(relative))
|
|
193
|
-
continue;
|
|
194
|
-
if (coreDocumentKindForPath(relative, canon.canon) !== "plan")
|
|
195
|
-
continue;
|
|
196
|
-
const baseline = await baselineOfPlan(fs, root, content, canon.canon.spec);
|
|
197
|
-
if (baseline !== null)
|
|
198
|
-
out.set(relative, baseline);
|
|
199
|
-
}
|
|
200
|
-
return out;
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Run the source-boundary policy over the plan bytes a proposal is carrying.
|
|
204
|
-
*
|
|
205
|
-
* The same judgment the execution entry makes, one moment earlier — over the
|
|
206
|
-
* artifact's content instead of a file on disk — so a plan accepted when it
|
|
207
|
-
* closes cannot be rejected for the same cause when somebody tries to run it.
|
|
208
|
-
* Nothing is read when the payload carries no artifact.
|
|
156
|
+
* The grammar half is the same judgment the execution entry makes, one moment
|
|
157
|
+
* earlier — over the artifact's content instead of a file on disk — so a plan
|
|
158
|
+
* accepted when it closes cannot be rejected for the same cause when somebody
|
|
159
|
+
* tries to run it. When the WORKSPACE block cannot be read, only the SEMANTIC
|
|
160
|
+
* half runs: answering an unreadable block with "that alias does not exist"
|
|
161
|
+
* would reject a plan for something the plan did not do.
|
|
209
162
|
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
163
|
+
* The lineage half runs in the same stage, so one refusal carries both. The seal
|
|
164
|
+
* is computed here from the spec the header names — never taken from the sender,
|
|
165
|
+
* whose digest would prove only that they typed something — and a plan that
|
|
166
|
+
* declares neither a lineage nor `> Standalone:` is refused instead of published
|
|
167
|
+
* without a seal. Nothing is read when the payload carries no artifact.
|
|
214
168
|
*/
|
|
215
|
-
async function
|
|
169
|
+
async function observePlanArtifacts(fs, paths, raw) {
|
|
170
|
+
const evidence = [];
|
|
171
|
+
const baselines = new Map();
|
|
216
172
|
const proposed = proposedArtifacts(raw);
|
|
217
173
|
if (proposed.length === 0)
|
|
218
|
-
return
|
|
174
|
+
return { evidence, baselines };
|
|
219
175
|
const canon = await resolveCoreDocsCanon(fs, paths);
|
|
220
|
-
// An unreadable canon cannot tell a plan from anything else
|
|
221
|
-
//
|
|
176
|
+
// An unreadable canon cannot tell a plan from anything else: judging a spec's
|
|
177
|
+
// prose by the plan's closure rule, or sealing on a guess, would both act on
|
|
178
|
+
// whatever the payload happened to carry.
|
|
222
179
|
if (!canon.ok)
|
|
223
|
-
return
|
|
180
|
+
return { evidence, baselines };
|
|
224
181
|
const root = await resolveWorkspaceRootFrom(fs, paths);
|
|
225
182
|
const block = await readWorkspaceBlock(fs, root, paths.blockMarkers());
|
|
226
183
|
const declared = block === null ? null : block.fuentes.map((source) => source.alias);
|
|
227
|
-
const out = [];
|
|
228
184
|
for (const artifact of proposed) {
|
|
229
185
|
if (!checkSafeRelativePath(artifact.path).ok)
|
|
230
186
|
continue;
|
|
231
187
|
if (coreDocumentKindForPath(artifact.path, canon.canon) !== "plan")
|
|
232
188
|
continue;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
: validatePlanSourceBoundary(artifact.content, declared),
|
|
238
|
-
});
|
|
189
|
+
const { failures, seal } = await planGrammarAtPublication(fs, root, artifact.content, declared, canon.canon.spec);
|
|
190
|
+
if (seal.status === "sealed")
|
|
191
|
+
baselines.set(artifact.path, seal.baseline);
|
|
192
|
+
evidence.push({ path: artifact.path, failures });
|
|
239
193
|
}
|
|
240
|
-
return
|
|
194
|
+
return { evidence, baselines };
|
|
241
195
|
}
|
|
242
196
|
/** The `path`/`content` pairs a payload proposes, read as data and judged by nobody. */
|
|
243
197
|
function proposedArtifacts(raw) {
|
|
@@ -322,7 +276,7 @@ async function observeScope(fs, paths, raw) {
|
|
|
322
276
|
declared,
|
|
323
277
|
plan,
|
|
324
278
|
sources: sourceAliasesOfPlan(text),
|
|
325
|
-
boundary_failures:
|
|
279
|
+
boundary_failures: planGrammarAtEntry(text, declared),
|
|
326
280
|
plan_error: null,
|
|
327
281
|
};
|
|
328
282
|
}
|
|
@@ -444,6 +398,24 @@ async function decide(fs, paths, state, input, snapshot) {
|
|
|
444
398
|
const resend = resendCheck(state, resolved, cost);
|
|
445
399
|
if (resend !== null)
|
|
446
400
|
return resend;
|
|
401
|
+
// The one re-run of an exhausted internal boundary belongs to the CLI: an
|
|
402
|
+
// external answer here would be the fourth try the cap exists to refuse.
|
|
403
|
+
if (awaitingCliRerun(state, resolved.stopped)) {
|
|
404
|
+
return reject(state, resolved, `'${resolved.stopped.id}' agotó sus intentos y la vuelve a correr el CLI, no un llamador`, {
|
|
405
|
+
code: "FLOW_ANSWER_NOT_EXPECTED",
|
|
406
|
+
action: "corré 'aw flow advance': el CLI corre una vez más la operación interna y, si reconoce su efecto, avanza sin cobrar",
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
// An internal action is credited only by the driver that ran it: that is what
|
|
410
|
+
// leaves its `executed` event in the trace. A result from outside, however well
|
|
411
|
+
// formed, is a claim nobody verified — and applying it used to publish nothing
|
|
412
|
+
// while marking the step done.
|
|
413
|
+
if (resolved.kind === "execution" && internalActionOf(resolved.stopped) !== null) {
|
|
414
|
+
return reject(state, resolved, `'${resolved.stopped.id}' la ejecuta el CLI: un resultado externo no la acredita`, {
|
|
415
|
+
code: "FLOW_INTERNAL_ACTION_EXTERNAL_RESULT",
|
|
416
|
+
action: `corré 'aw flow advance --session ${state.session}': el CLI corre la operación y la acredita; si la corrida no sale de esta frontera, '${restartInvocation(state.session)}' la destraba`,
|
|
417
|
+
}, cost);
|
|
418
|
+
}
|
|
447
419
|
// The action the run is waiting on is the one that was EMITTED, and the seal
|
|
448
420
|
// would already refuse a result about any other. What the persisted digest adds
|
|
449
421
|
// is the diagnosis: when the two differ, the invocation changed underneath a run
|
|
@@ -453,7 +425,7 @@ async function decide(fs, paths, state, input, snapshot) {
|
|
|
453
425
|
if (drifted !== null)
|
|
454
426
|
return drifted;
|
|
455
427
|
// 2 · The boundary in force decides what is admissible.
|
|
456
|
-
const admissible = admit(state, resolved, resolved.stopped, input, cost, snapshot.checkouts);
|
|
428
|
+
const admissible = admit(state, resolved, resolved.stopped, input, cost, snapshot.checkouts, snapshot.scoped);
|
|
457
429
|
if ("decision" in admissible)
|
|
458
430
|
return admissible.decision;
|
|
459
431
|
const parsed = admissible;
|
|
@@ -467,7 +439,12 @@ async function decide(fs, paths, state, input, snapshot) {
|
|
|
467
439
|
if (route.kind === "adjust") {
|
|
468
440
|
return adjustRouteProposal(state, journey);
|
|
469
441
|
}
|
|
470
|
-
const
|
|
442
|
+
const accepted = route.kind === "accept" ? withRouteDecisions(state, route.decisions) : state;
|
|
443
|
+
// Sealed with the transition it earned, in the same state publication: a batch
|
|
444
|
+
// is never seen validated without the proofs that validated it.
|
|
445
|
+
const routed = parsed.credit === undefined
|
|
446
|
+
? accepted
|
|
447
|
+
: withPlanExecBatchCredit(accepted, parsed.credit.batch, parsed.credit.proofs);
|
|
471
448
|
// The registry, not the Spanish consequence text, owns what a selected
|
|
472
449
|
// alternative does. A handoff must stop before any later plan-exec row can
|
|
473
450
|
// mark work, validate or commit; a decision selection is durably recorded
|
|
@@ -561,9 +538,12 @@ async function decide(fs, paths, state, input, snapshot) {
|
|
|
561
538
|
if (preview.preview !== null) {
|
|
562
539
|
selectedState = withFixPreview(selectedState, preview.preview);
|
|
563
540
|
}
|
|
541
|
+
// A redraft recorded by `grantOf` inserted rows after this boundary, so the
|
|
542
|
+
// transition is applied against the journey as it now reads.
|
|
543
|
+
const walked = journeyForRun(selectedState);
|
|
564
544
|
const outcome = holds
|
|
565
|
-
? holdAfterApproval(selectedState,
|
|
566
|
-
: applyAndAdvance(selectedState,
|
|
545
|
+
? holdAfterApproval(selectedState, walked, identity)
|
|
546
|
+
: applyAndAdvance(selectedState, walked, resolved.stopped, identity, parsed.answer);
|
|
567
547
|
return standalone === null ? outcome : withStandaloneGuidance(outcome, standalone);
|
|
568
548
|
}
|
|
569
549
|
/**
|
|
@@ -738,17 +718,28 @@ function routeAnswer(state, stopped, journey, answer) {
|
|
|
738
718
|
},
|
|
739
719
|
};
|
|
740
720
|
}
|
|
721
|
+
const { decision, control } = configuredControl;
|
|
722
|
+
const consequence = control.consequences[raw.disposition];
|
|
723
|
+
if (consequence === undefined) {
|
|
724
|
+
return {
|
|
725
|
+
kind: "failure",
|
|
726
|
+
failure: {
|
|
727
|
+
code: "FLOW_ROUTE_HARD_GATE",
|
|
728
|
+
message: `'${raw.transition}' no admite '${raw.disposition}': el registro no deja que una ruta la saltee`,
|
|
729
|
+
action: "declará apply o substitute para ese control; la evidencia que acredita el paso no se omite",
|
|
730
|
+
},
|
|
731
|
+
};
|
|
732
|
+
}
|
|
741
733
|
const substitution = routeSubstitution(raw.disposition, raw.substitution);
|
|
742
734
|
if ("failure" in substitution)
|
|
743
735
|
return { kind: "failure", failure: substitution.failure };
|
|
744
|
-
const { decision, control } = configuredControl;
|
|
745
736
|
controls.push({
|
|
746
737
|
transition: decision.id,
|
|
747
738
|
title: decision.title,
|
|
748
739
|
disposition: raw.disposition,
|
|
749
740
|
recommendation: control.recommendation,
|
|
750
741
|
alternatives: control.consequences,
|
|
751
|
-
consequence
|
|
742
|
+
consequence,
|
|
752
743
|
risk: control.risk,
|
|
753
744
|
reason: raw.reason.trim(),
|
|
754
745
|
substitution: substitution.value,
|
|
@@ -1275,11 +1266,11 @@ async function registerSelectedDecision(fs, paths, state, snapshot, resolved, co
|
|
|
1275
1266
|
* run that merely advanced would be indistinguishable from one that never decided.
|
|
1276
1267
|
*/
|
|
1277
1268
|
function standaloneDecisionEvent(state, transition, record) {
|
|
1278
|
-
const
|
|
1269
|
+
const rowIteration = iterationOf(state, transition);
|
|
1279
1270
|
return {
|
|
1280
1271
|
kind: "executed",
|
|
1281
1272
|
transition,
|
|
1282
|
-
...
|
|
1273
|
+
...rowIteration,
|
|
1283
1274
|
operation: "plan-exec.standalone-decision",
|
|
1284
1275
|
summary: record.decision,
|
|
1285
1276
|
output_digest: record.digest,
|
|
@@ -1322,36 +1313,6 @@ function withStandaloneGuidance(decision, record) {
|
|
|
1322
1313
|
function invalidScope(message, action) {
|
|
1323
1314
|
return { code: "FLOW_SCOPE_INVALID", message, action };
|
|
1324
1315
|
}
|
|
1325
|
-
/**
|
|
1326
|
-
* What to do about a source-boundary failure, said at the moment it was found.
|
|
1327
|
-
*
|
|
1328
|
-
* One generic sentence used to answer all five codes, and it was wrong twice
|
|
1329
|
-
* over: it sent somebody to declare sources that were already declared, and it
|
|
1330
|
-
* sent a sentence of prose to a refinement. Structure goes to the refinement
|
|
1331
|
-
* that owns it; a clause that names no observable check is a phrase to fix where
|
|
1332
|
-
* it is being written, and at a save proposal nothing is even saved yet.
|
|
1333
|
-
*/
|
|
1334
|
-
function planBoundaryAction(code, moment) {
|
|
1335
|
-
const then = moment === "proposal"
|
|
1336
|
-
? "corregilo en los bytes y volvé a proponer la vista previa"
|
|
1337
|
-
: "corregilo con /w:plan-refine antes de volver a entrar a ejecución";
|
|
1338
|
-
switch (code) {
|
|
1339
|
-
case "PLAN_SOURCE_BOUNDARY_MISSING":
|
|
1340
|
-
return `falta la declaración estructural: '> Límite de ejecución: checkout' bajo el título y '> Fuentes:' en cada fase — ${then}`;
|
|
1341
|
-
case "PLAN_SOURCE_UNKNOWN":
|
|
1342
|
-
return `ese alias no está en la tabla Fuentes del bloque WORKSPACE: declaralo ahí, o usá uno de los que ya están — ${then}`;
|
|
1343
|
-
case "PLAN_TASK_SOURCE_OUTSIDE_PHASE":
|
|
1344
|
-
return `la fuente de una tarea es un subconjunto de la de su fase: ajustá una de las dos — ${then}`;
|
|
1345
|
-
case "PLAN_SOURCE_EXTERNAL_CLOSURE":
|
|
1346
|
-
return `una cláusula de cierre no puede apoyarse en una superficie externa: llevá esa comprobación a '## Handoff operativo' y dejá en la cláusula una del checkout — ${then}`;
|
|
1347
|
-
case "PLAN_SOURCE_LOCAL_PROOF_MISSING":
|
|
1348
|
-
return moment === "proposal"
|
|
1349
|
-
? "nombrá en esa cláusula el comando, el archivo o la ruta que produce la comprobación, y volvé a proponer la vista previa"
|
|
1350
|
-
: "es una frase del documento y no su estructura: nombrá en esa cláusula el comando, el archivo o la ruta que produce la comprobación";
|
|
1351
|
-
default:
|
|
1352
|
-
return then;
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
1316
|
/**
|
|
1356
1317
|
* The closure-evidence gate, run BEFORE the preview is sealed.
|
|
1357
1318
|
*
|
|
@@ -1370,10 +1331,14 @@ function planEvidenceFrom(stopped, answer, observed) {
|
|
|
1370
1331
|
const failure = plan.failures[0];
|
|
1371
1332
|
if (failure === undefined)
|
|
1372
1333
|
continue;
|
|
1334
|
+
// Grammar and lineage travel together: whoever fixes the first cause
|
|
1335
|
+
// should not learn of the header on the next attempt.
|
|
1336
|
+
const lineage = plan.failures.find((candidate) => candidate !== failure && candidate.code === "PLAN_LINEAGE_UNSEALED");
|
|
1337
|
+
const also = lineage === undefined ? "" : ` · además: ${lineage.message}`;
|
|
1373
1338
|
return {
|
|
1374
1339
|
failure: {
|
|
1375
1340
|
code: failure.code,
|
|
1376
|
-
message: `'${plan.path}': ${failure.message}`,
|
|
1341
|
+
message: `'${plan.path}': ${failure.message}${also}`,
|
|
1377
1342
|
action: planBoundaryAction(failure.code, "proposal"),
|
|
1378
1343
|
},
|
|
1379
1344
|
};
|
|
@@ -1415,25 +1380,27 @@ function sealFrom(state, stopped, answer, snapshot, baselines) {
|
|
|
1415
1380
|
const artifacts = answer.artifacts.map((artifact) => {
|
|
1416
1381
|
const seen = snapshot.get(artifact.path);
|
|
1417
1382
|
const baseline = baselines.get(artifact.path);
|
|
1383
|
+
/**
|
|
1384
|
+
* Completing THIS run's reservation replaces no document.
|
|
1385
|
+
*
|
|
1386
|
+
* The file is there because this run put it there to hold the number, and
|
|
1387
|
+
* nothing was ever published into it — so the write is additive in the only
|
|
1388
|
+
* sense the classes measure, and the preview says so: `overwrite: false`,
|
|
1389
|
+
* `reserved: true`. A destination that exists for any other reason stays an
|
|
1390
|
+
* overwrite, which is what keeps a save row declaring only `local_additive`
|
|
1391
|
+
* from reaching a real plan.
|
|
1392
|
+
*/
|
|
1393
|
+
const reserved = seen?.exists === true && seen.digest === reservation;
|
|
1418
1394
|
return {
|
|
1419
1395
|
path: artifact.path,
|
|
1420
1396
|
// Publication seals the baseline into the very bytes it is about to
|
|
1421
1397
|
// propose, so preview, approval and write cover it as one thing.
|
|
1422
1398
|
content: baseline === undefined ? artifact.content : withSpecBaseline(artifact.content, baseline),
|
|
1423
|
-
overwrite: seen?.exists === true,
|
|
1424
|
-
|
|
1425
|
-
* Completing THIS run's reservation replaces no document.
|
|
1426
|
-
*
|
|
1427
|
-
* The file is there because this run put it there to hold the number, and
|
|
1428
|
-
* nothing was ever published into it — so the write is additive in the only
|
|
1429
|
-
* sense the classes measure: nobody's content is lost. A destination that
|
|
1430
|
-
* exists for any other reason stays `mutate_overwrite`, which is what keeps
|
|
1431
|
-
* a save row declaring only `local_additive` from reaching a real plan.
|
|
1432
|
-
*/
|
|
1433
|
-
reserved: seen?.exists === true && seen.digest === reservation,
|
|
1399
|
+
overwrite: seen?.exists === true && !reserved,
|
|
1400
|
+
...(reserved ? { reserved: true } : {}),
|
|
1434
1401
|
};
|
|
1435
1402
|
});
|
|
1436
|
-
const effects = observedEffects(artifacts
|
|
1403
|
+
const effects = observedEffects(artifacts);
|
|
1437
1404
|
const beyond = effects.filter((effect) => !contract.effects.includes(effect));
|
|
1438
1405
|
if (beyond.length > 0) {
|
|
1439
1406
|
return {
|
|
@@ -1444,8 +1411,10 @@ function sealFrom(state, stopped, answer, snapshot, baselines) {
|
|
|
1444
1411
|
},
|
|
1445
1412
|
};
|
|
1446
1413
|
}
|
|
1414
|
+
// The reservation is a base too: the marker has to be the one this run left
|
|
1415
|
+
// when the write lands, or somebody published into the slot in between.
|
|
1447
1416
|
const bases = artifacts
|
|
1448
|
-
.filter((a) => a.overwrite)
|
|
1417
|
+
.filter((a) => a.overwrite || a.reserved === true)
|
|
1449
1418
|
.map((a) => ({ path: a.path, digest: snapshot.get(a.path)?.digest ?? "" }));
|
|
1450
1419
|
const unreadable = bases.filter((base) => base.digest.length === 0);
|
|
1451
1420
|
if (unreadable.length > 0) {
|
|
@@ -1477,6 +1446,26 @@ function sealFrom(state, stopped, answer, snapshot, baselines) {
|
|
|
1477
1446
|
})),
|
|
1478
1447
|
};
|
|
1479
1448
|
}
|
|
1449
|
+
/**
|
|
1450
|
+
* The redraft a confirmation row's alternative asked for, or `null`.
|
|
1451
|
+
*
|
|
1452
|
+
* It unseats any standing proposal and records a reentry from the flow's
|
|
1453
|
+
* authoring row, so the run walks the redraft and its gate again instead of
|
|
1454
|
+
* carrying on to a publication with nothing to publish and a finalize that
|
|
1455
|
+
* closes the session. Independent of whether a proposal is seated: a proposal
|
|
1456
|
+
* the run gave up on is exactly when redrafting matters most.
|
|
1457
|
+
*/
|
|
1458
|
+
function redraftOf(state, stopped, answer, journey) {
|
|
1459
|
+
const redraft = reentryOf(stopped);
|
|
1460
|
+
if (redraft === null || answer.choice !== redraft.label)
|
|
1461
|
+
return null;
|
|
1462
|
+
return withReentry(withProposal(state, null), {
|
|
1463
|
+
kind: "refine",
|
|
1464
|
+
transition: stopped.id,
|
|
1465
|
+
occurrence: occurrenceAt(journey, state.applied.length),
|
|
1466
|
+
from: redraft.from,
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1480
1469
|
/**
|
|
1481
1470
|
* The grant this answer produced, scoped to what it was given over.
|
|
1482
1471
|
*
|
|
@@ -1491,8 +1480,13 @@ function sealFrom(state, stopped, answer, snapshot, baselines) {
|
|
|
1491
1480
|
*/
|
|
1492
1481
|
function grantOf(state, resolved, answer, granted, journey) {
|
|
1493
1482
|
const stopped = resolved.stopped;
|
|
1483
|
+
if (stopped === null)
|
|
1484
|
+
return state;
|
|
1485
|
+
const redrafted = redraftOf(state, stopped, answer, journey);
|
|
1486
|
+
if (redrafted !== null)
|
|
1487
|
+
return redrafted;
|
|
1494
1488
|
const proposal = state.proposal;
|
|
1495
|
-
const approve =
|
|
1489
|
+
const approve = publishApprovalOf(stopped);
|
|
1496
1490
|
if (approve !== null && proposal !== null) {
|
|
1497
1491
|
if (answer.choice === approve) {
|
|
1498
1492
|
return withApproval(state, {
|
|
@@ -1501,13 +1495,11 @@ function grantOf(state, resolved, answer, granted, journey) {
|
|
|
1501
1495
|
classes: [...proposal.requires_approval],
|
|
1502
1496
|
});
|
|
1503
1497
|
}
|
|
1504
|
-
// Declining unseats the proposal
|
|
1505
|
-
// nothing: the publication downstream finds nothing to publish and is skipped
|
|
1506
|
-
// saying so, instead of stopping to ask for an authorization over bytes the
|
|
1498
|
+
// Declining unseats the proposal: no authorization is asked over bytes the
|
|
1507
1499
|
// person just turned down.
|
|
1508
1500
|
return withProposal(state, null);
|
|
1509
1501
|
}
|
|
1510
|
-
const link =
|
|
1502
|
+
const link = approvalGrantOf(stopped);
|
|
1511
1503
|
if (link !== null && answer.choice === link.approve) {
|
|
1512
1504
|
const target = journey.find((row) => row.id === link.transition);
|
|
1513
1505
|
// A link to a transition this journey does not walk grants nothing: the
|
|
@@ -1538,7 +1530,7 @@ function grantOf(state, resolved, answer, granted, journey) {
|
|
|
1538
1530
|
* a payload the boundary's own contract rejects, a decline (a real answer that
|
|
1539
1531
|
* applies nothing), and an execution result that did not earn its transition.
|
|
1540
1532
|
*/
|
|
1541
|
-
function admit(state, resolved, stopped, input, cost, checkouts) {
|
|
1533
|
+
function admit(state, resolved, stopped, input, cost, checkouts, scoped) {
|
|
1542
1534
|
const expectedApproval = resolved.kind === "authorization"
|
|
1543
1535
|
? effectApprovalDigest(stopped.id, resolved.authorization?.planned ?? [])
|
|
1544
1536
|
: null;
|
|
@@ -1584,7 +1576,7 @@ function admit(state, resolved, stopped, input, cost, checkouts) {
|
|
|
1584
1576
|
return {
|
|
1585
1577
|
decision: reject(state, resolved, `'${PAUSE_LABEL}': la frontera queda en pie y la corrida retoma acá`, {
|
|
1586
1578
|
code: "FLOW_BOUNDARY_PAUSED",
|
|
1587
|
-
action:
|
|
1579
|
+
action: `escribí el CHECKPOINT con 'aw checkpoint-write --code ${state.session}', compactá, y volvé con 'aw flow advance --code ${state.session}' a esta misma frontera`,
|
|
1588
1580
|
outcome: "needs_input",
|
|
1589
1581
|
}, cost),
|
|
1590
1582
|
};
|
|
@@ -1601,22 +1593,53 @@ function admit(state, resolved, stopped, input, cost, checkouts) {
|
|
|
1601
1593
|
// An execution result has to EARN the transition. Anything short of a completed
|
|
1602
1594
|
// run with its evidence and its whole effect keeps the boundary standing: the
|
|
1603
1595
|
// work stays pending, with the recovery the action declared.
|
|
1604
|
-
if (resolved.kind
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1596
|
+
if (resolved.kind !== "execution")
|
|
1597
|
+
return parsed;
|
|
1598
|
+
return earned(state, resolved, stopped, parsed, cost, checkouts, scoped);
|
|
1599
|
+
}
|
|
1600
|
+
/** The execution verdict, then — at the phase validation — the batch's own credit. */
|
|
1601
|
+
function earned(state, resolved, stopped, parsed, cost, checkouts, scoped) {
|
|
1602
|
+
// The row's `effects` are the ceiling and the sealed proposal is what really
|
|
1603
|
+
// happens: demanding an overwrite from a publication that only creates files
|
|
1604
|
+
// would leave the transition pending for an effect nobody could produce.
|
|
1605
|
+
const verdict = executionVerdict(parsed.answer.result, resolved.action, effectsOfTransition(state, stopped), checkouts);
|
|
1606
|
+
if (verdict !== null) {
|
|
1607
|
+
const trace = declaredTrace(state, stopped, resolved, parsed.answer, verdict);
|
|
1608
|
+
return {
|
|
1609
|
+
decision: reject(state, resolved, verdict.message, verdict.detail, {
|
|
1610
|
+
...cost,
|
|
1611
|
+
...(trace === null ? {} : { trace }),
|
|
1612
|
+
}),
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
const credited = batchCredit(state, stopped, parsed.answer, resolved, scoped);
|
|
1616
|
+
if (credited === null)
|
|
1617
|
+
return parsed;
|
|
1618
|
+
if (!credited.ok) {
|
|
1619
|
+
return {
|
|
1620
|
+
decision: reject(state, resolved, credited.refusal.message, credited.refusal.detail, cost),
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
return { ...parsed, credit: credited.credit };
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* The phase validation's own judgment: the batch it closes must bring a proof of
|
|
1627
|
+
* each of its sources, of its own checkout. `null` on every other boundary, and
|
|
1628
|
+
* on a run with no batch standing, where there is nothing to credit.
|
|
1629
|
+
*/
|
|
1630
|
+
function batchCredit(state, stopped, answer, resolved, scoped) {
|
|
1631
|
+
if (stopped.id !== PHASE_VALIDATION || answer.result === null || state.scope === null) {
|
|
1632
|
+
return null;
|
|
1618
1633
|
}
|
|
1619
|
-
|
|
1634
|
+
const iteration = state.batch_loop?.iteration ?? null;
|
|
1635
|
+
const batches = state.batches ?? [];
|
|
1636
|
+
const batch = batches.find((entry) => entry.iteration === iteration);
|
|
1637
|
+
if (batch === undefined)
|
|
1638
|
+
return null;
|
|
1639
|
+
const verdict = batchCreditVerdict(answer.result, { batch, batches, sources: state.scope.sources, scoped }, resolved.action?.recovery ?? "");
|
|
1640
|
+
if (!verdict.ok)
|
|
1641
|
+
return verdict;
|
|
1642
|
+
return { ok: true, credit: { batch: batch.id, proofs: verdict.credit } };
|
|
1620
1643
|
}
|
|
1621
1644
|
/**
|
|
1622
1645
|
* The material fact a REFUSED external result still leaves behind — or `null`.
|
|
@@ -1638,11 +1661,11 @@ function declaredTrace(state, stopped, resolved, answer, verdict) {
|
|
|
1638
1661
|
if (!touchesTheWorld(applied) && verdict.detail.code !== "FLOW_EFFECT_PARTIAL")
|
|
1639
1662
|
return null;
|
|
1640
1663
|
const invocation = resolved.action?.invocation;
|
|
1641
|
-
const
|
|
1664
|
+
const rowIteration = iterationOf(state, stopped.id);
|
|
1642
1665
|
return {
|
|
1643
1666
|
kind: "failed",
|
|
1644
1667
|
transition: stopped.id,
|
|
1645
|
-
...
|
|
1668
|
+
...rowIteration,
|
|
1646
1669
|
// What was really run, named the way whoever ran it would recognize it: an
|
|
1647
1670
|
// external execution has no internal operation id to quote.
|
|
1648
1671
|
operation: invocation === undefined ? stopped.id : [invocation.program, ...invocation.args].join(" "),
|
|
@@ -1699,13 +1722,6 @@ function applyAndAdvance(approved, journey, stopped, identity, answer) {
|
|
|
1699
1722
|
? withObservation(approved, observationFor(approved, stopped.id, answer.signals))
|
|
1700
1723
|
: approved;
|
|
1701
1724
|
next = withPlanExecBatchStageForTransition(applyTransition(next, stopped.id, effectsOfTransition(next, stopped)), stopped.id);
|
|
1702
|
-
// A published proposal is spent, whoever published it. This is the degraded
|
|
1703
|
-
// path — no internal executor, so the caller ran the write and returned its
|
|
1704
|
-
// result — and leaving the proposal seated would keep a preview of bytes that
|
|
1705
|
-
// are already on disk standing in front of the next boundary.
|
|
1706
|
-
if (internalActionOf(stopped)?.operation === "proposal.publish") {
|
|
1707
|
-
next = withProposal(next, null);
|
|
1708
|
-
}
|
|
1709
1725
|
next = withAttempt(next, identity);
|
|
1710
1726
|
// The boundary follows the position, always: handing the engine a state whose
|
|
1711
1727
|
// boundary still names the transition just applied is exactly the incoherence
|
|
@@ -1860,7 +1876,7 @@ function resendCheck(state, resolved, cost) {
|
|
|
1860
1876
|
// The twin was refused: this is a retry of the same wrong answer, so it gets
|
|
1861
1877
|
// the same real diagnosis again and counts toward the cap, which is what
|
|
1862
1878
|
// eventually degrades the boundary instead of leaving the caller looping.
|
|
1863
|
-
if (twin !== undefined &&
|
|
1879
|
+
if (twin !== undefined && twinWasRefused(state, twin))
|
|
1864
1880
|
return null;
|
|
1865
1881
|
return reject(state, resolved, "esta respuesta ya se había aplicado: el recorrido no avanza dos veces", {
|
|
1866
1882
|
code: "FLOW_ANSWER_RESENT",
|
|
@@ -1868,6 +1884,19 @@ function resendCheck(state, resolved, cost) {
|
|
|
1868
1884
|
outcome: "completed",
|
|
1869
1885
|
}, cost);
|
|
1870
1886
|
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Whether the attempt a resend matches never advanced anything.
|
|
1889
|
+
*
|
|
1890
|
+
* Its transition missing from `applied` says so. So does its transition being
|
|
1891
|
+
* the boundary still standing at the same iteration: a repeated row (a batch or
|
|
1892
|
+
* a redraft copy) has its id in `applied` from an earlier walk, and that earlier
|
|
1893
|
+
* walk is not this one.
|
|
1894
|
+
*/
|
|
1895
|
+
function twinWasRefused(state, twin) {
|
|
1896
|
+
if (!state.applied.includes(twin.transition))
|
|
1897
|
+
return true;
|
|
1898
|
+
return (twin.transition === state.boundary && sameIteration(twin, iterationOf(state, twin.transition)));
|
|
1899
|
+
}
|
|
1871
1900
|
/**
|
|
1872
1901
|
* A rejection, expressed as the recalculated directive.
|
|
1873
1902
|
*
|
|
@@ -1929,7 +1958,7 @@ function reject(state, resolved, message, detail, cost) {
|
|
|
1929
1958
|
*/
|
|
1930
1959
|
function attemptIdentity(state, input, seal, transition) {
|
|
1931
1960
|
const digest = semanticDigest({ payload: input.raw, approval: input.approval });
|
|
1932
|
-
const
|
|
1961
|
+
const rowIteration = iterationOf(state, transition);
|
|
1933
1962
|
const prior = state.attempts.filter((past) => past.invocation_id === seal);
|
|
1934
1963
|
const twin = prior.find((past) => past.request_digest === digest);
|
|
1935
1964
|
// The next ordinal, asked with the SAME expression `AttemptLedger.record`
|
|
@@ -1953,18 +1982,18 @@ function attemptIdentity(state, input, seal, transition) {
|
|
|
1953
1982
|
// The seal moves with the state; the transition does not while the run
|
|
1954
1983
|
// stands there. It is what the cap counts over.
|
|
1955
1984
|
transition,
|
|
1956
|
-
...
|
|
1985
|
+
...rowIteration,
|
|
1957
1986
|
};
|
|
1958
1987
|
}
|
|
1959
1988
|
function isRecord(value) {
|
|
1960
1989
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1961
1990
|
}
|
|
1962
1991
|
function observationFor(state, transition, signals) {
|
|
1963
|
-
const
|
|
1992
|
+
const rowIteration = iterationOf(state, transition);
|
|
1964
1993
|
return {
|
|
1965
1994
|
transition,
|
|
1966
1995
|
signals,
|
|
1967
|
-
...
|
|
1996
|
+
...rowIteration,
|
|
1968
1997
|
};
|
|
1969
1998
|
}
|
|
1970
1999
|
//# sourceMappingURL=submit.js.map
|