gentle-pi 2.1.2 → 2.3.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/README.md +108 -79
- package/assets/agents/gentle-ai-worker.md +7 -3
- package/assets/agents/jd-fix-agent.md +1 -1
- package/assets/agents/jd-judge-a.md +3 -1
- package/assets/agents/jd-judge-b.md +3 -1
- package/assets/agents/review-readability.md +4 -3
- package/assets/agents/review-reliability.md +4 -3
- package/assets/agents/review-resilience.md +4 -3
- package/assets/agents/review-risk.md +4 -3
- package/assets/agents/sdd-apply.md +11 -4
- package/assets/agents/sdd-archive.md +6 -1
- package/assets/agents/sdd-design.md +6 -1
- package/assets/agents/sdd-explore.md +6 -2
- package/assets/agents/sdd-init.md +10 -2
- package/assets/agents/sdd-onboard.md +6 -1
- package/assets/agents/sdd-proposal.md +8 -1
- package/assets/agents/sdd-research.md +54 -0
- package/assets/agents/sdd-spec.md +6 -1
- package/assets/agents/sdd-status.md +10 -5
- package/assets/agents/sdd-sync.md +6 -1
- package/assets/agents/sdd-tasks.md +8 -4
- package/assets/agents/sdd-verify.md +27 -2
- package/assets/chains/4r-review.chain.md +2 -0
- package/assets/chains/sdd-full.chain.md +2 -2
- package/assets/chains/sdd-plan.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +2 -2
- package/assets/orchestrator-delegation.md +145 -160
- package/assets/orchestrator-memory.md +2 -0
- package/assets/orchestrator.md +25 -48
- package/assets/sdd-orchestrator-workflow.md +163 -25
- package/assets/support/sdd-status-contract.md +24 -6
- package/contracts/review-integration/v1/fixtures/consent.fixture.json +3 -3
- package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +19 -28
- package/contracts/review-integration/v1/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +12 -21
- package/contracts/review-integration/v1/schemas/correction-plan-request.schema.json +49 -0
- package/contracts/review-integration/v1/schemas/operation.schema.json +76 -0
- package/contracts/review-integration/v1/schemas/repair.schema.json +39 -0
- package/contracts/review-integration/v1/schemas/status-v2.schema.json +4 -2
- package/contracts/review-integration/v1/schemas/status.schema.json +4 -2
- package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
- package/contracts/review-integration/v2/fixtures/consent.fixture.json +1 -1
- package/contracts/review-integration/v2/fixtures/start.fixture.json +1 -10
- package/contracts/review-integration/v2/fixtures/status.fixture.json +1 -10
- package/contracts/review-integration/v2/schemas/failure.schema.json +5 -1
- package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
- package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
- package/contracts/review-integration/v2/schemas/operation.schema.json +6 -1
- package/contracts/review-integration/v2/schemas/repair.schema.json +4 -2
- package/contracts/review-integration/v2/schemas/start.schema.json +5 -2
- package/contracts/review-integration/v2/schemas/status.schema.json +4 -2
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +30 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/README.md +12 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/manifest.json +65 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/lens.schema.json +16 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/refuter.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/targeted-validator.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/lens.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/refuter.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/targeted-validator.json +1 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-capabilities.baseline.json +15 -0
- package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-roles.baseline.json +42 -0
- package/docs/native-authority-architecture.md +14 -16
- package/docs/review-integration.md +27 -353
- package/extensions/ask-user-choice.ts +151 -0
- package/extensions/gentle-ai.ts +2710 -3211
- package/extensions/quiet-tools.ts +515 -32
- package/extensions/sdd-init.ts +21 -12
- package/extensions/skill-registry.ts +10 -2
- package/extensions/startup-banner.ts +10 -4
- package/lib/gentle-ai-binary.ts +173 -2
- package/lib/gentle-ai-renderer.ts +70 -0
- package/lib/model-routing-authority.ts +133 -0
- package/lib/native-review-cli.ts +676 -781
- package/lib/opaque-pi-reviewer-adapter.ts +284 -0
- package/lib/provider-contract-bundle.ts +704 -0
- package/lib/review-candidate-view.ts +836 -118
- package/lib/review-compact-contract.ts +59 -248
- package/lib/review-host-relay.ts +578 -0
- package/lib/review-integration-v2.ts +1254 -221
- package/lib/review-last-event-controller.ts +35 -0
- package/lib/review-relay-contract.ts +16 -0
- package/lib/sdd-preflight.ts +230 -67
- package/lib/sdd-status.ts +66 -111
- package/lib/terminal-theme.ts +1 -1
- package/package.json +83 -79
- package/runtime/gentle-ai-binary.mjs +174 -3
- package/runtime/native-review-cli.mjs +610 -715
- package/runtime/review-integration-v2.mjs +1223 -190
- package/runtime/review-relay-contract.mjs +17 -0
- package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +5 -5
- package/scripts/check-provider-contract.mjs +138 -0
- package/scripts/gentle-ai-installer.mjs +85 -22
- package/scripts/maintainer/provider-relay-matrix.mjs +403 -0
- package/scripts/mirror-provider-contract.mjs +143 -0
- package/scripts/test-packed-runner.mjs +20 -9
- package/scripts/verify-package-files.mjs +119 -43
- package/skills/_shared/review-ledger-contract.md +11 -19
- package/skills/chained-pr/SKILL.md +3 -0
- package/skills/cognitive-doc-design/SKILL.md +1 -1
- package/skills/comment-writer/SKILL.md +1 -1
- package/skills/gentle-ai/SKILL.md +7 -74
- package/skills/issue-creation/SKILL.md +94 -168
- package/skills/judgment-day/SKILL.md +9 -5
- package/skills/judgment-day/references/prompts-and-formats.md +2 -0
- package/skills/rdd-defect-workflow/SKILL.md +54 -0
- package/skills/release/SKILL.md +3 -3
- package/skills/skill-registry/SKILL.md +1 -1
- package/skills/work-unit-commits/SKILL.md +3 -1
- package/tests/artifact-language.test.ts +24 -11
- package/tests/ask-user-choice.test.ts +264 -0
- package/tests/background-subagents.test.ts +771 -0
- package/tests/codegraph-tools.test.ts +3 -3
- package/tests/crosslane/cross-lane.mjs +16 -0
- package/tests/delegated-key-learnings-contract.test.ts +240 -0
- package/tests/devbinary/native-review-parity.devtest.ts +167 -250
- package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
- package/tests/fixtures/devbinary/capabilities-v2.1.derived.json +331 -0
- package/tests/fixtures/devbinary/capabilities-v2.2.captured.json +340 -0
- package/tests/fixtures/devbinary/consent-v3.captured.json +37 -0
- package/tests/fixtures/devbinary/failure-v2-capture-evidence.captured.json +16 -0
- package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
- package/tests/fixtures/devbinary/result-artifact-v2-path.captured.json +12 -0
- package/tests/fixtures/devbinary/result-artifact-v2.captured.json +12 -0
- package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
- package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
- package/tests/fixtures/devbinary/start-v3-consent-declined.captured.json +19 -0
- package/tests/fixtures/devbinary/start-v3-consent-granted.captured.json +109 -0
- package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
- package/tests/fixtures/devbinary/status-v5-capture-result-submission.captured.json +184 -0
- package/tests/fixtures/devbinary/status-v5-repository-context.captured.json +138 -0
- package/tests/fixtures/devbinary/status-v5.captured.json +88 -0
- package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/README.md +12 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/manifest.json +65 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/targeted-validator.json +1 -0
- package/tests/gentle-ai-binary.test.ts +83 -13
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +195 -0
- package/tests/gentle-ai-dev-binary.test.ts +159 -0
- package/tests/gentle-ai-installer.test.ts +138 -48
- package/tests/gentle-ai.test.ts +820 -131
- package/tests/gentle-theme.test.ts +133 -0
- package/tests/maintainer/provider-relay.maintest.ts +601 -0
- package/tests/model-routing-authority.test.ts +257 -0
- package/tests/native-review-capability-contract.test.ts +75 -2
- package/tests/native-review-cli.test.ts +588 -908
- package/tests/native-review-consent.test.ts +283 -38
- package/tests/native-review-parity-runtime.test.ts +108 -350
- package/tests/native-review-parity.test.ts +684 -732
- package/tests/native-sdd-attempt-authority.test.ts +235 -0
- package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
- package/tests/orchestrator-budget.test.ts +158 -89
- package/tests/orchestrator-rdd-ownership.test.ts +103 -0
- package/tests/package-manifest.test.ts +128 -100
- package/tests/provider-contract-bundle.test.ts +385 -0
- package/tests/provider-contract-mirror.test.ts +206 -0
- package/tests/provider-defect-handoff.test.ts +252 -0
- package/tests/quiet-tool-rendering.test.ts +1055 -28
- package/tests/review-actor-tool-deny.test.ts +12 -13
- package/tests/review-authority-recovery-docs.test.ts +1 -2
- package/tests/review-candidate-view.test.ts +898 -12
- package/tests/review-compact-contract.test.ts +29 -122
- package/tests/review-controller-lock-status.test.ts +2 -2
- package/tests/review-controller-native-recovery.test.ts +366 -857
- package/tests/review-controller-native-routing.test.ts +1253 -4199
- package/tests/review-controller-retired-ops.test.ts +1 -1
- package/tests/review-controller-workspace-root.test.ts +236 -70
- package/tests/review-controller.test.ts +26 -816
- package/tests/review-corrected-finalize-binding.test.ts +134 -0
- package/tests/review-dispatch-hydration-gap.test.ts +145 -0
- package/tests/review-gate.test.ts +0 -45
- package/tests/review-host-relay-restart-parity.test.ts +360 -0
- package/tests/review-host-relay-routing.test.ts +352 -0
- package/tests/review-host-relay.test.ts +754 -0
- package/tests/review-integration-v2-forward.test.ts +654 -0
- package/tests/review-integration-v2.test.ts +317 -129
- package/tests/review-last-event-closure.test.ts +408 -0
- package/tests/review-ledger-contract.test.ts +106 -60
- package/tests/review-recovered-lineage-routing.test.ts +199 -0
- package/tests/review-relay-transport-agent.test.ts +322 -0
- package/tests/review-snapshot.test.ts +3 -2
- package/tests/runtime-harness.mjs +573 -180
- package/tests/sdd-agent-tools.test.ts +53 -32
- package/tests/sdd-preflight.test.ts +81 -15
- package/tests/sdd-status.test.ts +109 -110
- package/tests/skill-collision-prefixes.test.ts +6 -8
- package/tests/skill-registry.test.ts +50 -1
- package/tests/verify-package-files.test.ts +62 -0
- package/tests/writer-edit-surface-scope.test.ts +230 -0
- package/themes/Gentleman-Cute.json +94 -0
- package/themes/Gentleman-Sexy.json +92 -0
- package/assets/agents/review-refuter.md +0 -40
- package/assets/agents/review-validator.md +0 -23
- package/lib/git-commit-transaction.ts +0 -801
- package/lib/native-review-remediation.ts +0 -49
- package/lib/review-compact.ts +0 -947
- package/lib/review-refuter-adapter.ts +0 -129
- package/lib/review-runtime-contract.ts +0 -68
- package/prompts/gcl.md +0 -54
- package/prompts/gis.md +0 -25
- package/prompts/gpr.md +0 -41
- package/prompts/gwr.md +0 -31
- package/runtime/git-commit-transaction.mjs +0 -802
- package/scripts/run-git-commit-transaction.mjs +0 -35
- package/tests/fixtures/native-review-cli/v2.1.2/bind-sdd.json +0 -25
- package/tests/fixtures/native-review-cli/v2.1.2/finalize.json +0 -8
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.2/sdd-status.json +0 -200
- package/tests/fixtures/native-review-cli/v2.1.2/start.json +0 -12
- package/tests/fixtures/native-review-cli/v2.1.2/validate-allow.json +0 -24
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny-empty-context.json +0 -20
- package/tests/fixtures/native-review-cli/v2.1.2/validate-deny.json +0 -28
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
- package/tests/git-commit-transaction.test.ts +0 -302
- package/tests/review-compact.test.ts +0 -243
- package/tests/review-refuter-adapter.test.ts +0 -89
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { NativeReviewCli } from "./native-review-cli.ts";
|
|
2
|
+
import type { ReviewLastEventClosureBinding, ReviewStatusV3 } from "./review-integration-v2.ts";
|
|
3
|
+
|
|
4
|
+
export interface ReviewLastEventCaptureSelector {
|
|
5
|
+
readonly baseRef?: string;
|
|
6
|
+
readonly committedOnly?: true;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reconcile exactly one ambiguous native capture outcome. Successful captures
|
|
11
|
+
* return their native artifact or closure directly; this helper is never a
|
|
12
|
+
* post-success lifecycle step and never replays a capture.
|
|
13
|
+
*/
|
|
14
|
+
export async function reconcileUnknownReviewLastEventCapture(
|
|
15
|
+
nativeReviewCli: NativeReviewCli,
|
|
16
|
+
cwd: string,
|
|
17
|
+
binding: ReviewLastEventClosureBinding,
|
|
18
|
+
selector?: ReviewLastEventCaptureSelector,
|
|
19
|
+
): Promise<ReviewStatusV3> {
|
|
20
|
+
if (nativeReviewCli.targetStatus === undefined) {
|
|
21
|
+
throw new TypeError("native target-scoped STATUS is required to reconcile an ambiguous capture outcome");
|
|
22
|
+
}
|
|
23
|
+
const status = await nativeReviewCli.targetStatus({
|
|
24
|
+
cwd,
|
|
25
|
+
lineageId: binding.lineageId,
|
|
26
|
+
...(selector === undefined ? {} : selector),
|
|
27
|
+
});
|
|
28
|
+
if (binding.targetIdentity !== undefined && status.targetIdentity !== binding.targetIdentity) {
|
|
29
|
+
throw new TypeError("capture reconciliation returned a different target");
|
|
30
|
+
}
|
|
31
|
+
if (status.authority?.lineageId !== undefined && status.authority.lineageId !== binding.lineageId) {
|
|
32
|
+
throw new TypeError("capture reconciliation returned a different lineage");
|
|
33
|
+
}
|
|
34
|
+
return status;
|
|
35
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// gentle-pi.review-relay/v1 — the Pi host relay handshake (gentle-pi#311 P4,
|
|
2
|
+
// provider side gentle-ai#3249/#3254).
|
|
3
|
+
//
|
|
4
|
+
// gentle-ai admits `pi` as a host-mediated runtime identity only when the
|
|
5
|
+
// launcher that relays the invocation declares this exact contract through
|
|
6
|
+
// this exact environment variable. Any other value (or none) keeps Pi
|
|
7
|
+
// fail-closed at admission, before any repository, target, or authority work.
|
|
8
|
+
// That refusal is correct provider behavior and is never worked around here;
|
|
9
|
+
// the single central gentle-ai process runner injects this declaration on
|
|
10
|
+
// every invocation the extension makes.
|
|
11
|
+
//
|
|
12
|
+
// Both values are compiled constants: the contract identity is versioned by
|
|
13
|
+
// release, never by configuration.
|
|
14
|
+
|
|
15
|
+
export const GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV = "GENTLE_PI_REVIEW_RELAY_CONTRACT";
|
|
16
|
+
export const GENTLE_PI_REVIEW_RELAY_CONTRACT = "gentle-pi.review-relay/v1";
|
package/lib/sdd-preflight.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -23,19 +23,36 @@ function gentlePiAgentHome(): string {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export type SddExecutionMode = "interactive" | "auto";
|
|
26
|
+
export type SddDeliveryStrategy =
|
|
27
|
+
| "ask-on-risk"
|
|
28
|
+
| "auto-chain"
|
|
29
|
+
| "single-pr"
|
|
30
|
+
| "exception-ok";
|
|
31
|
+
/** @deprecated Use SddDeliveryStrategy; legacy values normalize at persistence boundaries. */
|
|
26
32
|
export type SddChainedPrStrategy =
|
|
33
|
+
| SddDeliveryStrategy
|
|
27
34
|
| "auto-forecast"
|
|
28
35
|
| "ask-always"
|
|
29
36
|
| "single-pr-default"
|
|
30
37
|
| "force-chained";
|
|
38
|
+
export type SddPreflightField = "executionMode" | "artifactStore" | "chainedPrStrategy" | "reviewBudgetLines";
|
|
39
|
+
export const SDD_PREFLIGHT_FIELDS = ["executionMode", "artifactStore", "chainedPrStrategy", "reviewBudgetLines"] as const;
|
|
31
40
|
|
|
32
41
|
export interface SddPreflightPreferences {
|
|
33
42
|
executionMode: SddExecutionMode;
|
|
34
43
|
artifactStore: SddArtifactStore;
|
|
44
|
+
/** Runtime values are canonical; legacy assignments normalize at persistence/render boundaries. */
|
|
35
45
|
chainedPrStrategy: SddChainedPrStrategy;
|
|
36
46
|
reviewBudgetLines: number;
|
|
37
47
|
engramAvailable: boolean;
|
|
38
48
|
prompted: boolean;
|
|
49
|
+
sizeExceptionAccepted?: true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface SddPreflightResolutionOptions {
|
|
53
|
+
persisted?: Partial<SddPreflightPreferences>;
|
|
54
|
+
promptFields?: readonly SddPreflightField[];
|
|
55
|
+
acceptSizeException?: true;
|
|
39
56
|
}
|
|
40
57
|
|
|
41
58
|
interface SddPreflightCallbacks {
|
|
@@ -69,14 +86,14 @@ interface LegacyManagedAssetsManifest extends ManagedAssetsManifest {
|
|
|
69
86
|
packageVersion: string;
|
|
70
87
|
}
|
|
71
88
|
|
|
72
|
-
const DEFAULT_SDD_PREFLIGHT: SddPreflightPreferences = {
|
|
73
|
-
executionMode: "
|
|
89
|
+
export const DEFAULT_SDD_PREFLIGHT: SddPreflightPreferences = Object.freeze({
|
|
90
|
+
executionMode: "auto",
|
|
74
91
|
artifactStore: "openspec",
|
|
75
|
-
chainedPrStrategy: "
|
|
92
|
+
chainedPrStrategy: "ask-on-risk",
|
|
76
93
|
reviewBudgetLines: 400,
|
|
77
94
|
engramAvailable: false,
|
|
78
95
|
prompted: false,
|
|
79
|
-
};
|
|
96
|
+
});
|
|
80
97
|
|
|
81
98
|
const sddPreflightBySession = new Map<string, SddPreflightPreferences>();
|
|
82
99
|
const sddPreflightInFlight = new Map<string, Promise<SddPreflightPreferences>>();
|
|
@@ -85,6 +102,69 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
85
102
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
86
103
|
}
|
|
87
104
|
|
|
105
|
+
function isSddExecutionMode(value: unknown): value is SddExecutionMode {
|
|
106
|
+
return value === "interactive" || value === "auto";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function isSddArtifactStore(value: unknown): value is SddArtifactStore {
|
|
110
|
+
return value === "openspec" || value === "engram" || value === "hybrid" || value === "none";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// normalizeSddArtifactStore accepts the canonical names and the legacy "both"
|
|
114
|
+
// spelling of the dual-store mode. Operator preflight files written before the
|
|
115
|
+
// rename carry "both" on disk; rejecting it would silently drop the operator's
|
|
116
|
+
// choice back to the default, so it maps forward instead.
|
|
117
|
+
export function normalizeSddArtifactStore(value: unknown): SddArtifactStore | undefined {
|
|
118
|
+
if (value === "both") return "hybrid";
|
|
119
|
+
return isSddArtifactStore(value) ? value : undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeReviewBudgetValue(value: unknown): number | undefined {
|
|
123
|
+
const parsed = typeof value === "number"
|
|
124
|
+
? value
|
|
125
|
+
: typeof value === "string"
|
|
126
|
+
? Number.parseInt(value.trim(), 10)
|
|
127
|
+
: Number.NaN;
|
|
128
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function normalizeSddStrategySelection(
|
|
132
|
+
value: unknown,
|
|
133
|
+
allowExceptionOk: boolean,
|
|
134
|
+
): SddDeliveryStrategy | undefined {
|
|
135
|
+
if (value === "exception-ok") return allowExceptionOk ? value : undefined;
|
|
136
|
+
if (value === "auto-forecast" || value === "ask-always") return "ask-on-risk";
|
|
137
|
+
if (value === "single-pr-default") return "single-pr";
|
|
138
|
+
if (value === "force-chained") return "auto-chain";
|
|
139
|
+
return value === "ask-on-risk" || value === "auto-chain" || value === "single-pr"
|
|
140
|
+
? value
|
|
141
|
+
: undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function normalizeSddChainedPrStrategy(
|
|
145
|
+
value: unknown,
|
|
146
|
+
allowExceptionOk = false,
|
|
147
|
+
): SddDeliveryStrategy {
|
|
148
|
+
return normalizeSddStrategySelection(value, allowExceptionOk) ?? "ask-on-risk";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizedSelections(
|
|
152
|
+
value: Partial<Record<SddPreflightField, unknown>> | undefined,
|
|
153
|
+
engramAvailable: boolean,
|
|
154
|
+
allowExceptionOk: boolean,
|
|
155
|
+
): Partial<Record<SddPreflightField, unknown>> {
|
|
156
|
+
if (!value) return {};
|
|
157
|
+
const result: Partial<Record<SddPreflightField, unknown>> = {};
|
|
158
|
+
if (isSddExecutionMode(value.executionMode)) result.executionMode = value.executionMode;
|
|
159
|
+
const artifactStore = normalizeSddArtifactStore(value.artifactStore);
|
|
160
|
+
if (artifactStore !== undefined && (engramAvailable || artifactStore === "openspec" || artifactStore === "none")) result.artifactStore = artifactStore;
|
|
161
|
+
const strategy = normalizeSddStrategySelection(value.chainedPrStrategy, allowExceptionOk);
|
|
162
|
+
if (strategy) result.chainedPrStrategy = strategy;
|
|
163
|
+
const reviewBudgetLines = normalizeReviewBudgetValue(value.reviewBudgetLines);
|
|
164
|
+
if (reviewBudgetLines !== undefined) result.reviewBudgetLines = reviewBudgetLines;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
|
|
88
168
|
function emptyManagedAssetsManifest(): ManagedAssetsManifest {
|
|
89
169
|
return {
|
|
90
170
|
schemaVersion: MANAGED_ASSETS_SCHEMA_VERSION,
|
|
@@ -273,28 +353,28 @@ export function readSddPreflightFromDisk(cwd: string): SddPreflightPreferences |
|
|
|
273
353
|
try {
|
|
274
354
|
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
275
355
|
if (!isRecord(parsed)) return undefined;
|
|
276
|
-
// Validate required fields to guard against stale/corrupt writes
|
|
356
|
+
// Validate required fields to guard against stale/corrupt writes.
|
|
277
357
|
const { executionMode, artifactStore, chainedPrStrategy, reviewBudgetLines, engramAvailable, prompted } = parsed;
|
|
358
|
+
// Normalize before validating: a preflight file written before the
|
|
359
|
+
// dual-store rename carries "both", and discarding it would silently
|
|
360
|
+
// drop the operator's choice back to the default.
|
|
361
|
+
const canonicalArtifactStore = normalizeSddArtifactStore(artifactStore);
|
|
278
362
|
if (
|
|
279
|
-
(executionMode
|
|
280
|
-
|
|
363
|
+
!isSddExecutionMode(executionMode) ||
|
|
364
|
+
canonicalArtifactStore === undefined ||
|
|
281
365
|
typeof reviewBudgetLines !== "number" ||
|
|
366
|
+
!Number.isFinite(reviewBudgetLines) ||
|
|
367
|
+
reviewBudgetLines <= 0 ||
|
|
282
368
|
typeof engramAvailable !== "boolean" ||
|
|
283
369
|
typeof prompted !== "boolean"
|
|
284
370
|
) {
|
|
285
371
|
return undefined;
|
|
286
372
|
}
|
|
287
|
-
const normalizedChain: SddChainedPrStrategy =
|
|
288
|
-
chainedPrStrategy === "ask-always" ||
|
|
289
|
-
chainedPrStrategy === "single-pr-default" ||
|
|
290
|
-
chainedPrStrategy === "force-chained"
|
|
291
|
-
? (chainedPrStrategy as SddChainedPrStrategy)
|
|
292
|
-
: "auto-forecast";
|
|
293
373
|
return {
|
|
294
374
|
executionMode,
|
|
295
|
-
artifactStore,
|
|
296
|
-
chainedPrStrategy:
|
|
297
|
-
reviewBudgetLines,
|
|
375
|
+
artifactStore: canonicalArtifactStore,
|
|
376
|
+
chainedPrStrategy: normalizeSddChainedPrStrategy(chainedPrStrategy),
|
|
377
|
+
reviewBudgetLines: normalizeReviewBudgetValue(reviewBudgetLines) ?? DEFAULT_SDD_PREFLIGHT.reviewBudgetLines,
|
|
298
378
|
engramAvailable,
|
|
299
379
|
prompted,
|
|
300
380
|
};
|
|
@@ -306,8 +386,19 @@ export function readSddPreflightFromDisk(cwd: string): SddPreflightPreferences |
|
|
|
306
386
|
export function writeSddPreflightToDisk(cwd: string, prefs: SddPreflightPreferences): void {
|
|
307
387
|
try {
|
|
308
388
|
const path = sddPreflightDiskPath(cwd);
|
|
389
|
+
const prompted = prefs.prompted === true;
|
|
390
|
+
const canonical: SddPreflightPreferences = {
|
|
391
|
+
executionMode: isSddExecutionMode(prefs.executionMode) ? prefs.executionMode : DEFAULT_SDD_PREFLIGHT.executionMode,
|
|
392
|
+
artifactStore: normalizeSddArtifactStore(prefs.artifactStore) ?? DEFAULT_SDD_PREFLIGHT.artifactStore,
|
|
393
|
+
chainedPrStrategy: normalizeSddChainedPrStrategy(prefs.chainedPrStrategy),
|
|
394
|
+
reviewBudgetLines:
|
|
395
|
+
normalizeReviewBudgetValue(prefs.reviewBudgetLines) ??
|
|
396
|
+
DEFAULT_SDD_PREFLIGHT.reviewBudgetLines,
|
|
397
|
+
engramAvailable: prefs.engramAvailable === true,
|
|
398
|
+
prompted,
|
|
399
|
+
};
|
|
309
400
|
mkdirSync(dirname(path), { recursive: true });
|
|
310
|
-
writeFileSync(path, JSON.stringify(
|
|
401
|
+
writeFileSync(path, JSON.stringify(canonical, null, 2));
|
|
311
402
|
} catch {
|
|
312
403
|
// Disk write failures are non-fatal; in-memory cache is the primary store
|
|
313
404
|
}
|
|
@@ -394,6 +485,57 @@ function copyDirectoryFiles(
|
|
|
394
485
|
return { copied, skipped };
|
|
395
486
|
}
|
|
396
487
|
|
|
488
|
+
// Assets retired by gentle-pi#311 P5: the Pi-owned adversarial review actors.
|
|
489
|
+
// The refuter and validator verdicts now execute through Go-owned pi
|
|
490
|
+
// processes via provider-rendered self-contained vectors, so these agent
|
|
491
|
+
// definitions have no runtime consumer. The migration manifests under
|
|
492
|
+
// assets/migrations are append-only legacy-hash HISTORY (adoption evidence
|
|
493
|
+
// for force-installs) with no removal semantics, so history stays untouched
|
|
494
|
+
// and retirement happens here: an installed copy is deleted only when its
|
|
495
|
+
// content hash proves package ownership (current manifest or legacy
|
|
496
|
+
// history); user-modified copies are left in place and only lose managed
|
|
497
|
+
// ownership.
|
|
498
|
+
const RETIRED_MANAGED_ASSETS = Object.freeze([
|
|
499
|
+
"agents/review-refuter.md",
|
|
500
|
+
"agents/review-validator.md",
|
|
501
|
+
]);
|
|
502
|
+
|
|
503
|
+
function removeRetiredManagedAssets(
|
|
504
|
+
agentHome: string,
|
|
505
|
+
manifest: ManagedAssetsManifest,
|
|
506
|
+
): void {
|
|
507
|
+
let legacyHashes: Record<string, readonly string[]> | undefined;
|
|
508
|
+
for (const ownershipKey of RETIRED_MANAGED_ASSETS) {
|
|
509
|
+
const installedPath = join(agentHome, ...ownershipKey.split("/"));
|
|
510
|
+
if (!existsSync(installedPath)) {
|
|
511
|
+
delete manifest.assets[ownershipKey];
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
let installedContent: string | undefined;
|
|
515
|
+
try {
|
|
516
|
+
installedContent = readFileSync(installedPath, "utf8");
|
|
517
|
+
} catch {
|
|
518
|
+
installedContent = undefined;
|
|
519
|
+
}
|
|
520
|
+
if (installedContent === undefined) continue;
|
|
521
|
+
const installedHash = managedAssetHash(installedContent);
|
|
522
|
+
const managed = manifest.assets[ownershipKey] === installedHash;
|
|
523
|
+
const legacy = (legacyHashes ??= readLegacyManagedAssetHashes())[ownershipKey]?.includes(
|
|
524
|
+
managedAssetHash(legacyComparableAssetContent(ownershipKey, installedContent)),
|
|
525
|
+
) === true;
|
|
526
|
+
if (managed || legacy) {
|
|
527
|
+
try {
|
|
528
|
+
rmSync(installedPath);
|
|
529
|
+
} catch {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// Managed copies are gone; user-modified copies stay but stop being
|
|
534
|
+
// package-managed either way.
|
|
535
|
+
delete manifest.assets[ownershipKey];
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
397
539
|
export function installSddAssets(
|
|
398
540
|
_cwd: string,
|
|
399
541
|
force: boolean,
|
|
@@ -407,6 +549,7 @@ export function installSddAssets(
|
|
|
407
549
|
(cachedLegacyAssetHashes ??= readLegacyManagedAssetHashes());
|
|
408
550
|
}
|
|
409
551
|
const manifest = readManagedAssetsManifest(manifestPath);
|
|
552
|
+
removeRetiredManagedAssets(agentHome, manifest);
|
|
410
553
|
const agents = copyDirectoryFiles(
|
|
411
554
|
join(ASSETS_DIR, "agents"),
|
|
412
555
|
join(agentHome, "agents"),
|
|
@@ -443,7 +586,7 @@ export function installSddAssets(
|
|
|
443
586
|
|
|
444
587
|
export function isSddPreflightTrigger(text: string): boolean {
|
|
445
588
|
const trimmed = text.trim();
|
|
446
|
-
if (/^\/sdd(?:[-:][^\s]*)?(?:\s|$)/i.test(trimmed)) return true;
|
|
589
|
+
if (/^\/(?:gentle-)?sdd(?:[-:][^\s]*)?(?:\s|$)/i.test(trimmed)) return true;
|
|
447
590
|
if (/[??]\s*$/.test(trimmed)) return false;
|
|
448
591
|
if (
|
|
449
592
|
/\b(?:don't|do\s+not|not\s+use|never\s+use|without\s+using|sin\s+usar|no\s+(?:quiero|queremos|vamos\s+a)?\s*usar)\s+sdd\b/i.test(
|
|
@@ -498,56 +641,67 @@ function hasWritableEngramTool(pi: ExtensionAPI): boolean {
|
|
|
498
641
|
}
|
|
499
642
|
}
|
|
500
643
|
|
|
501
|
-
function
|
|
502
|
-
const parsed = Number.parseInt(value.trim(), 10);
|
|
503
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 400;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
async function collectSddPreflightPreferences(
|
|
644
|
+
export async function collectSddPreflightPreferences(
|
|
507
645
|
ctx: ExtensionContext,
|
|
508
646
|
engramAvailable: boolean,
|
|
647
|
+
options: SddPreflightResolutionOptions = {},
|
|
509
648
|
): Promise<SddPreflightPreferences> {
|
|
510
|
-
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
649
|
+
const persistedPrompted = options.persisted?.prompted === true;
|
|
650
|
+
const allowExceptionOk = options.acceptSizeException === true;
|
|
651
|
+
const persisted = normalizedSelections(options.persisted, engramAvailable, allowExceptionOk);
|
|
652
|
+
const resolved: Partial<Record<SddPreflightField, unknown>> = { ...persisted };
|
|
653
|
+
let prompted = persistedPrompted;
|
|
654
|
+
let sizeExceptionAccepted = allowExceptionOk && persisted.chainedPrStrategy === "exception-ok";
|
|
655
|
+
const promptFields = new Set(options.promptFields ?? []);
|
|
656
|
+
|
|
657
|
+
const usePromptedValue = (
|
|
658
|
+
field: SddPreflightField,
|
|
659
|
+
value: unknown,
|
|
660
|
+
): void => {
|
|
661
|
+
const candidate = normalizedSelections({ [field]: value }, engramAvailable, allowExceptionOk);
|
|
662
|
+
if (candidate[field] !== undefined) {
|
|
663
|
+
resolved[field] = candidate[field];
|
|
664
|
+
if (field === "chainedPrStrategy" && candidate[field] === "exception-ok") sizeExceptionAccepted = true;
|
|
665
|
+
prompted = true;
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const promptField = async (
|
|
670
|
+
field: SddPreflightField,
|
|
671
|
+
read: () => Promise<unknown>,
|
|
672
|
+
enabled = true,
|
|
673
|
+
): Promise<void> => {
|
|
674
|
+
if (ctx.hasUI && enabled && promptFields.has(field)) {
|
|
675
|
+
usePromptedValue(field, await read());
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
const artifactOptions = engramAvailable ? ["openspec", "engram", "hybrid"] : ["openspec"];
|
|
679
|
+
await promptField("executionMode", () => ctx.ui.select("SDD execution mode", ["interactive", "auto"]));
|
|
680
|
+
await promptField("artifactStore", () => ctx.ui.select("SDD artifact store", artifactOptions), artifactOptions.length > 1);
|
|
681
|
+
await promptField("chainedPrStrategy", () => ctx.ui.select("SDD delivery strategy", ["ask-on-risk", "auto-chain", "single-pr"]));
|
|
682
|
+
await promptField("reviewBudgetLines", () => ctx.ui.input("SDD review budget lines", String(DEFAULT_SDD_PREFLIGHT.reviewBudgetLines)));
|
|
683
|
+
|
|
684
|
+
const resolvedValue = <T>(field: SddPreflightField, fallback: T): T =>
|
|
685
|
+
(resolved[field] as T | undefined) ?? fallback;
|
|
528
686
|
return {
|
|
529
|
-
executionMode:
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
? artifactStore
|
|
534
|
-
: DEFAULT_SDD_PREFLIGHT.artifactStore,
|
|
535
|
-
chainedPrStrategy:
|
|
536
|
-
chainedPrStrategy === "ask-always" ||
|
|
537
|
-
chainedPrStrategy === "single-pr-default" ||
|
|
538
|
-
chainedPrStrategy === "force-chained"
|
|
539
|
-
? chainedPrStrategy
|
|
540
|
-
: DEFAULT_SDD_PREFLIGHT.chainedPrStrategy,
|
|
541
|
-
reviewBudgetLines,
|
|
687
|
+
executionMode: resolvedValue("executionMode", DEFAULT_SDD_PREFLIGHT.executionMode),
|
|
688
|
+
artifactStore: resolvedValue("artifactStore", DEFAULT_SDD_PREFLIGHT.artifactStore),
|
|
689
|
+
chainedPrStrategy: resolvedValue("chainedPrStrategy", DEFAULT_SDD_PREFLIGHT.chainedPrStrategy),
|
|
690
|
+
reviewBudgetLines: resolvedValue("reviewBudgetLines", DEFAULT_SDD_PREFLIGHT.reviewBudgetLines),
|
|
542
691
|
engramAvailable,
|
|
543
|
-
prompted
|
|
692
|
+
prompted,
|
|
693
|
+
...(sizeExceptionAccepted ? { sizeExceptionAccepted: true as const } : {}),
|
|
544
694
|
};
|
|
545
695
|
}
|
|
546
696
|
|
|
547
697
|
export function renderSddPreflightPrompt(prefs: SddPreflightPreferences): string {
|
|
698
|
+
const deliveryStrategy = normalizeSddChainedPrStrategy(
|
|
699
|
+
prefs.chainedPrStrategy,
|
|
700
|
+
prefs.sizeExceptionAccepted === true,
|
|
701
|
+
);
|
|
548
702
|
const sourceLine = prefs.prompted
|
|
549
|
-
? "
|
|
550
|
-
: "
|
|
703
|
+
? "These SDD preferences are explicit current-session choices. Reuse them unless the user explicitly changes them."
|
|
704
|
+
: "These SDD preferences are canonical defaults or persisted choices. Treat them as authoritative; do not revisit dependent decisions unless a genuine human-control gate is reached.";
|
|
551
705
|
const interactiveRules =
|
|
552
706
|
prefs.executionMode === "interactive"
|
|
553
707
|
? [
|
|
@@ -563,25 +717,34 @@ export function renderSddPreflightPrompt(prefs: SddPreflightPreferences): string
|
|
|
563
717
|
sourceLine,
|
|
564
718
|
`- Execution mode: ${prefs.executionMode}`,
|
|
565
719
|
`- Artifact store: ${prefs.artifactStore}${prefs.engramAvailable ? "" : " (Engram unavailable in this session)"}`,
|
|
566
|
-
`-
|
|
567
|
-
|
|
720
|
+
`- Delivery strategy: ${deliveryStrategy}`,
|
|
721
|
+
"- Delivery strategy domain: `ask-on-risk` | `auto-chain` | `single-pr` | `exception-ok`",
|
|
722
|
+
`- Review budget: ${prefs.reviewBudgetLines} changed lines (400 is the canonical threshold unless explicitly changed)`,
|
|
723
|
+
"- Chain strategy: deferred until chaining is selected.",
|
|
724
|
+
"- `exception-ok` is never inferred; it requires explicit acceptance of `size:exception`.",
|
|
568
725
|
...interactiveRules,
|
|
569
|
-
"-
|
|
726
|
+
"- Preserve human-controlled consent, authorization, security, destructive/publishing, ambiguous-scope, and `size:exception` gates.",
|
|
727
|
+
"- When review-budget risk requires a delivery decision, use `ask-on-risk` to pause and ask; do not invent a chain strategy or an exception.",
|
|
570
728
|
].join("\n");
|
|
571
729
|
}
|
|
572
730
|
|
|
573
731
|
export async function ensureSddPreflight(
|
|
574
732
|
ctx: ExtensionContext,
|
|
575
733
|
callbacks: SddPreflightCallbacks,
|
|
734
|
+
resolutionOptions: SddPreflightResolutionOptions = {},
|
|
576
735
|
): Promise<SddPreflightPreferences> {
|
|
577
736
|
const sessionKey = sddPreflightSessionKey(ctx);
|
|
578
737
|
const existing = sddPreflightBySession.get(sessionKey);
|
|
579
|
-
if (existing) return existing;
|
|
738
|
+
if (existing && !(resolutionOptions.promptFields?.length ?? 0)) return existing;
|
|
580
739
|
const inFlight = sddPreflightInFlight.get(sessionKey);
|
|
581
|
-
if (inFlight) return inFlight;
|
|
740
|
+
if (inFlight && !(resolutionOptions.promptFields?.length ?? 0)) return inFlight;
|
|
582
741
|
const promise = (async () => {
|
|
583
742
|
const engramAvailable = hasWritableEngramTool(callbacks.pi);
|
|
584
|
-
const
|
|
743
|
+
const persisted = resolutionOptions.persisted ?? readSddPreflightFromDisk(ctx.cwd);
|
|
744
|
+
const prefs = await collectSddPreflightPreferences(ctx, engramAvailable, {
|
|
745
|
+
...resolutionOptions,
|
|
746
|
+
persisted,
|
|
747
|
+
});
|
|
585
748
|
const result =
|
|
586
749
|
(await callbacks.installAssets?.(ctx.cwd)) ??
|
|
587
750
|
installSddAssets(ctx.cwd, false);
|
|
@@ -598,9 +761,9 @@ export async function ensureSddPreflight(
|
|
|
598
761
|
"Gentle AI SDD preflight complete.",
|
|
599
762
|
`Mode: ${prefs.executionMode}`,
|
|
600
763
|
`Artifacts: ${prefs.artifactStore}`,
|
|
601
|
-
`
|
|
764
|
+
`Delivery strategy: ${prefs.chainedPrStrategy}`,
|
|
602
765
|
`Review budget: ${prefs.reviewBudgetLines} changed lines`,
|
|
603
|
-
`Preference source: ${prefs.prompted ? "
|
|
766
|
+
`Preference source: ${prefs.prompted ? "explicit session choice" : "canonical default or persisted preference"}`,
|
|
604
767
|
`Global SDD assets ready: ${result.agents} agent(s), ${result.chains} chain(s), ${result.support} support file(s), ${result.skipped} already present.`,
|
|
605
768
|
modelRoutingLine,
|
|
606
769
|
].join("\n"),
|