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
|
@@ -5,7 +5,6 @@ import test from "node:test";
|
|
|
5
5
|
|
|
6
6
|
const repoRoot = process.cwd();
|
|
7
7
|
const assetsAgentsDir = join(repoRoot, "assets", "agents");
|
|
8
|
-
const REVIEW_REFUTER_TOOLS = ["read", "grep", "find"];
|
|
9
8
|
const GENERIC_ROLE_TOOLS: Record<string, string[]> = {
|
|
10
9
|
"gentle-ai-explore.md": ["read", "grep", "find", "codegraph"],
|
|
11
10
|
"gentle-ai-worker.md": ["read", "grep", "find", "edit", "write", "bash", "mem_save"],
|
|
@@ -61,18 +60,19 @@ function assertGenericRoleBody(fileName: string, source: string): void {
|
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
const requiredToolsByAgent: Record<string, string[]> = {
|
|
64
|
-
"sdd-apply.md": ["read", "grep", "
|
|
65
|
-
"sdd-archive.md": ["read", "grep", "
|
|
66
|
-
"sdd-design.md": ["read", "grep", "
|
|
67
|
-
"sdd-explore.md": ["read", "grep", "
|
|
68
|
-
"sdd-init.md": ["read", "grep", "
|
|
69
|
-
"sdd-onboard.md": ["read", "grep", "
|
|
70
|
-
"sdd-proposal.md": ["read", "grep", "
|
|
71
|
-
"sdd-
|
|
72
|
-
"sdd-
|
|
73
|
-
"sdd-
|
|
74
|
-
"sdd-
|
|
75
|
-
"sdd-
|
|
63
|
+
"sdd-apply.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save", "mem_update"],
|
|
64
|
+
"sdd-archive.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save"],
|
|
65
|
+
"sdd-design.md": ["read", "grep", "find", "edit", "write", "mem_search", "mem_get_observation", "mem_save"],
|
|
66
|
+
"sdd-explore.md": ["read", "grep", "find", "edit", "write", "mem_save"],
|
|
67
|
+
"sdd-init.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save", "mem_update"],
|
|
68
|
+
"sdd-onboard.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save", "mem_update"],
|
|
69
|
+
"sdd-proposal.md": ["read", "grep", "find", "edit", "write", "mem_search", "mem_get_observation", "mem_save"],
|
|
70
|
+
"sdd-research.md": ["read", "grep", "find", "edit", "write", "mem_search", "mem_get_observation", "mem_save"],
|
|
71
|
+
"sdd-spec.md": ["read", "grep", "find", "edit", "write", "mem_search", "mem_get_observation", "mem_save"],
|
|
72
|
+
"sdd-status.md": ["read", "grep", "find", "bash", "mem_search", "mem_get_observation"],
|
|
73
|
+
"sdd-sync.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save", "mem_update"],
|
|
74
|
+
"sdd-tasks.md": ["read", "grep", "find", "edit", "write", "mem_search", "mem_get_observation", "mem_save"],
|
|
75
|
+
"sdd-verify.md": ["read", "grep", "find", "edit", "write", "bash", "mem_search", "mem_get_observation", "mem_save"],
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
test("SDD package agents declare role-appropriate tools as YAML arrays", () => {
|
|
@@ -123,25 +123,46 @@ test("generic non-SDD agents declare exact role tool allowlists", () => {
|
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
-
test("
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
test("sdd-verify phase text carries the verify-result envelope and validate-before-persist rule", () => {
|
|
127
|
+
// gentle-pi#535 row 5: the phase must produce a natively admissible report
|
|
128
|
+
// on its first persistence attempt without hunting the format elsewhere.
|
|
129
|
+
const envelopeFields = [
|
|
130
|
+
"schema: gentle-ai.verify-result/v1",
|
|
131
|
+
"evidence_revision: sha256:",
|
|
132
|
+
"verdict:",
|
|
133
|
+
"blockers:",
|
|
134
|
+
"critical_findings:",
|
|
135
|
+
"requirements:",
|
|
136
|
+
"scenarios:",
|
|
137
|
+
"test_command:",
|
|
138
|
+
"test_exit_code:",
|
|
139
|
+
"test_output_hash: sha256:",
|
|
140
|
+
"build_command:",
|
|
141
|
+
"build_exit_code:",
|
|
142
|
+
"build_output_hash: sha256:",
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const agentSource = readFileSync(join(assetsAgentsDir, "sdd-verify.md"), "utf8");
|
|
146
|
+
assert.match(agentSource, /```yaml\nschema: gentle-ai\.verify-result\/v1\n/);
|
|
147
|
+
for (const field of envelopeFields) {
|
|
148
|
+
assert.ok(agentSource.includes(field), `sdd-verify.md envelope must carry \`${field}\``);
|
|
149
|
+
}
|
|
150
|
+
assert.match(agentSource, /first non-empty content/);
|
|
151
|
+
assert.match(
|
|
152
|
+
agentSource,
|
|
153
|
+
/gentle-ai sdd-verify-validate --input <path\|-> --requirements <n> --scenarios <n>/,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const chainSource = readFileSync(join(repoRoot, "assets", "chains", "sdd-verify.chain.md"), "utf8");
|
|
157
|
+
assert.match(chainSource, /gentle-ai\.verify-result\/v1/);
|
|
158
|
+
assert.match(chainSource, /sdd-verify-validate/);
|
|
159
|
+
});
|
|
132
160
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"task",
|
|
140
|
-
"subagent",
|
|
141
|
-
"subagent_run",
|
|
142
|
-
"mem_save",
|
|
143
|
-
"mem_update",
|
|
144
|
-
]) {
|
|
145
|
-
assert.doesNotMatch(frontmatter, new RegExp(`^ - ${forbidden}$`, "m"));
|
|
161
|
+
test("the retired Pi adversarial role agents are not packaged", () => {
|
|
162
|
+
// gentle-pi#311 P5: the refuter and targeted validator verdicts execute
|
|
163
|
+
// through Go-owned pi processes via provider-rendered self-contained
|
|
164
|
+
// vectors; no Pi agent definition may reintroduce a Pi-authored verdict.
|
|
165
|
+
for (const retired of ["review-refuter.md", "review-validator.md"]) {
|
|
166
|
+
assert.ok(!existsSync(join(assetsAgentsDir, retired)), `${retired} must stay deleted`);
|
|
146
167
|
}
|
|
147
168
|
});
|
|
@@ -13,7 +13,10 @@ import { tmpdir } from "node:os";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import test from "node:test";
|
|
15
15
|
import {
|
|
16
|
+
collectSddPreflightPreferences,
|
|
17
|
+
DEFAULT_SDD_PREFLIGHT,
|
|
16
18
|
installSddAssets,
|
|
19
|
+
isSddPreflightTrigger,
|
|
17
20
|
readSddPreflightFromDisk,
|
|
18
21
|
sddPreflightDiskPath,
|
|
19
22
|
writeSddPreflightToDisk,
|
|
@@ -27,12 +30,44 @@ async function workspace(): Promise<string> {
|
|
|
27
30
|
const SAMPLE_PREFS: SddPreflightPreferences = {
|
|
28
31
|
executionMode: "auto",
|
|
29
32
|
artifactStore: "engram",
|
|
30
|
-
chainedPrStrategy: "auto-
|
|
33
|
+
chainedPrStrategy: "auto-chain",
|
|
31
34
|
reviewBudgetLines: 400,
|
|
32
35
|
engramAvailable: true,
|
|
33
36
|
prompted: true,
|
|
34
37
|
};
|
|
35
38
|
|
|
39
|
+
function preflightContext(cwd: string, hasUI: boolean, calls: string[] = [], answers: Record<string, string> = {}) {
|
|
40
|
+
return { cwd, hasUI, ui: { select: async (title: string) => (calls.push(`select:${title}`), answers[title]), input: async (title: string) => (calls.push(`input:${title}`), answers[title]), notify: () => {} } } as Parameters<typeof collectSddPreflightPreferences>[0];
|
|
41
|
+
}
|
|
42
|
+
function writeRawPreflight(cwd: string, chainedPrStrategy: string, prompted = true): string {
|
|
43
|
+
const path = sddPreflightDiskPath(cwd); mkdirSync(join(cwd, ".pi", "gentle-ai"), { recursive: true }); writeFileSync(path, JSON.stringify({ executionMode: "auto", artifactStore: "openspec", chainedPrStrategy, reviewBudgetLines: 400, engramAvailable: false, prompted })); return path;
|
|
44
|
+
}
|
|
45
|
+
test("production callers distinguish automatic defaults from explicit preflight prompts", () => {
|
|
46
|
+
const root = join(import.meta.dirname, ".."), gentleAi = readFileSync(join(root, "extensions", "gentle-ai.ts"), "utf8"), sddInit = readFileSync(join(root, "extensions", "sdd-init.ts"), "utf8");
|
|
47
|
+
assert.match(gentleAi, /function runSddPreflight\(\s*ctx: ExtensionContext,\s*promptFields: readonly SddPreflightField\[\] = \[\]\s*\)/s); assert.match(gentleAi, /if \(isSddAgent && !getSddPreflightPreferences\(ctx\)\) \{\s*await runSddPreflight\(ctx\);/s); assert.match(gentleAi, /applyModelConfig: async \(\) => applySavedModelConfig\(ctx\)\s*\},\s*\{\s*promptFields\s*\}\s*\);/s); assert.match(gentleAi, /handler: async \(_args, ctx\) => \{\s*await runSddPreflight\(ctx, SDD_PREFLIGHT_FIELDS\);/s); assert.match(sddInit, /applyModelConfig: \(\) => applySavedModelConfig\(ctx\)\s*\},\s*\{\s*promptFields: \[\]\s*\}\s*\);/s);
|
|
48
|
+
});
|
|
49
|
+
test("capability-constrained artifact selector elision", async () => {
|
|
50
|
+
const calls: string[] = [], prefs = await collectSddPreflightPreferences(preflightContext(await workspace(), true, calls), false, { promptFields: ["artifactStore"] });
|
|
51
|
+
assert.deepEqual(prefs, DEFAULT_SDD_PREFLIGHT); assert.deepEqual(calls, []);
|
|
52
|
+
});
|
|
53
|
+
test("headless/UI canonical-domain parity", async () => {
|
|
54
|
+
const calls: string[] = [], ui = await collectSddPreflightPreferences(preflightContext(await workspace(), true, calls), false), headless = await collectSddPreflightPreferences(preflightContext(await workspace(), false), false);
|
|
55
|
+
assert.deepEqual(ui, DEFAULT_SDD_PREFLIGHT); assert.deepEqual(headless, ui); assert.deepEqual(calls, []);
|
|
56
|
+
});
|
|
57
|
+
test("explicit UI selections override defaults when a field is genuinely unresolved", async () => {
|
|
58
|
+
const calls: string[] = [], prefs = await collectSddPreflightPreferences(preflightContext(await workspace(), true, calls, { "SDD execution mode": "interactive", "SDD artifact store": "engram", "SDD delivery strategy": "auto-chain", "SDD review budget lines": "700" }), true, { persisted: DEFAULT_SDD_PREFLIGHT, promptFields: ["executionMode", "artifactStore", "chainedPrStrategy", "reviewBudgetLines"] });
|
|
59
|
+
assert.deepEqual({ executionMode: prefs.executionMode, artifactStore: prefs.artifactStore, chainedPrStrategy: prefs.chainedPrStrategy, reviewBudgetLines: prefs.reviewBudgetLines }, { executionMode: "interactive", artifactStore: "engram", chainedPrStrategy: "auto-chain", reviewBudgetLines: 700 }); assert.equal(prefs.prompted, true); assert.equal(calls.length, 4);
|
|
60
|
+
});
|
|
61
|
+
test("legacy persisted strategies normalize to canonical values", async () => {
|
|
62
|
+
const mappings = { "auto-forecast": "ask-on-risk", "ask-always": "ask-on-risk", "single-pr-default": "single-pr", "force-chained": "auto-chain" } as const;
|
|
63
|
+
for (const [legacy, canonical] of Object.entries(mappings)) { const cwd = await workspace(); writeRawPreflight(cwd, legacy); assert.equal(readSddPreflightFromDisk(cwd)?.chainedPrStrategy, canonical); }
|
|
64
|
+
});
|
|
65
|
+
test("exception-ok requires narrow delivery-gate provenance", async () => {
|
|
66
|
+
for (const prompted of [false, true]) { const cwd = await workspace(); writeRawPreflight(cwd, "exception-ok", prompted); assert.equal(readSddPreflightFromDisk(cwd)?.chainedPrStrategy, "ask-on-risk"); }
|
|
67
|
+
const accepted = await collectSddPreflightPreferences(preflightContext(await workspace(), false), false, { persisted: { ...DEFAULT_SDD_PREFLIGHT, chainedPrStrategy: "exception-ok" }, acceptSizeException: true }); assert.equal(accepted.chainedPrStrategy, "exception-ok"); assert.equal(accepted.sizeExceptionAccepted, true);
|
|
68
|
+
const durable = await workspace(); writeSddPreflightToDisk(durable, accepted); assert.equal(readSddPreflightFromDisk(durable)?.chainedPrStrategy, "ask-on-risk");
|
|
69
|
+
});
|
|
70
|
+
|
|
36
71
|
test("sddPreflightDiskPath returns project-local .pi/gentle-ai/sdd-preflight.json", async () => {
|
|
37
72
|
const cwd = await workspace();
|
|
38
73
|
const path = sddPreflightDiskPath(cwd);
|
|
@@ -62,18 +97,11 @@ test("readSddPreflightFromDisk returns persisted prefs after write", async () =>
|
|
|
62
97
|
assert.deepEqual(loaded, SAMPLE_PREFS);
|
|
63
98
|
});
|
|
64
99
|
|
|
65
|
-
test("persisted
|
|
66
|
-
const cwd = await workspace();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Simulate a fresh process where in-memory Map is empty — only disk store exists
|
|
72
|
-
const loaded = readSddPreflightFromDisk(cwd);
|
|
73
|
-
assert.ok(loaded !== undefined);
|
|
74
|
-
assert.equal(loaded.artifactStore, "engram");
|
|
75
|
-
assert.equal(loaded.executionMode, "auto");
|
|
76
|
-
assert.equal(loaded.prompted, true);
|
|
100
|
+
test("persisted preferences are reused with zero prompts", async () => {
|
|
101
|
+
const cwd = await workspace(); writeSddPreflightToDisk(cwd, SAMPLE_PREFS);
|
|
102
|
+
const loaded = readSddPreflightFromDisk(cwd); assert.ok(loaded);
|
|
103
|
+
const calls: string[] = [], reused = await collectSddPreflightPreferences(preflightContext(cwd, true, calls), loaded!.engramAvailable, { persisted: loaded });
|
|
104
|
+
assert.deepEqual(reused, loaded); assert.deepEqual(calls, []);
|
|
77
105
|
});
|
|
78
106
|
|
|
79
107
|
test("readSddPreflightFromDisk returns undefined for corrupt JSON", async () => {
|
|
@@ -95,7 +123,7 @@ test("readSddPreflightFromDisk returns undefined for JSON with invalid fields",
|
|
|
95
123
|
assert.equal(readSddPreflightFromDisk(cwd), undefined);
|
|
96
124
|
});
|
|
97
125
|
|
|
98
|
-
test("readSddPreflightFromDisk normalizes unknown chainedPrStrategy to
|
|
126
|
+
test("readSddPreflightFromDisk normalizes unknown chainedPrStrategy to ask-on-risk", async () => {
|
|
99
127
|
const cwd = await workspace();
|
|
100
128
|
const path = sddPreflightDiskPath(cwd);
|
|
101
129
|
mkdirSync(join(cwd, ".pi", "gentle-ai"), { recursive: true });
|
|
@@ -110,7 +138,7 @@ test("readSddPreflightFromDisk normalizes unknown chainedPrStrategy to auto-fore
|
|
|
110
138
|
|
|
111
139
|
const loaded = readSddPreflightFromDisk(cwd);
|
|
112
140
|
assert.ok(loaded !== undefined);
|
|
113
|
-
assert.equal(loaded.chainedPrStrategy, "
|
|
141
|
+
assert.equal(loaded.chainedPrStrategy, "ask-on-risk");
|
|
114
142
|
});
|
|
115
143
|
|
|
116
144
|
test("writeSddPreflightToDisk is non-fatal when directory is not writable (no throw)", async () => {
|
|
@@ -224,3 +252,41 @@ test("forced asset refresh migrates only untouched v0.14 package contracts and p
|
|
|
224
252
|
rmSync(temporaryAgentHome, { recursive: true, force: true });
|
|
225
253
|
}
|
|
226
254
|
});
|
|
255
|
+
|
|
256
|
+
// gentle-ai calls the dual-store mode "hybrid"; this repo called the same
|
|
257
|
+
// operator-facing choice "both". Two names for one concept is how a caller ends
|
|
258
|
+
// up mapping between them by hand. gentle-ai owns the contract, so "hybrid" is
|
|
259
|
+
// canonical here too — but "both" is already persisted in operator preflight
|
|
260
|
+
// files on disk, so it must keep loading rather than fall back to the default.
|
|
261
|
+
test("a persisted legacy 'both' artifact store loads as hybrid", async () => {
|
|
262
|
+
const cwd = await mkdtemp(join(tmpdir(), "sdd-preflight-legacy-"));
|
|
263
|
+
mkdirSync(join(cwd, ".pi", "gentle-ai"), { recursive: true });
|
|
264
|
+
writeFileSync(
|
|
265
|
+
sddPreflightDiskPath(cwd),
|
|
266
|
+
JSON.stringify({ executionMode: "auto", artifactStore: "hybrid", chainedPrStrategy: "ask-on-risk", reviewBudgetLines: 400, engramAvailable: true, prompted: true }),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const loaded = readSddPreflightFromDisk(cwd, true);
|
|
270
|
+
assert.equal(loaded?.artifactStore, "hybrid", "legacy 'both' must normalize to the canonical name, not be discarded");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("a persisted canonical 'hybrid' artifact store loads unchanged", async () => {
|
|
274
|
+
const cwd = await mkdtemp(join(tmpdir(), "sdd-preflight-hybrid-"));
|
|
275
|
+
mkdirSync(join(cwd, ".pi", "gentle-ai"), { recursive: true });
|
|
276
|
+
writeFileSync(
|
|
277
|
+
sddPreflightDiskPath(cwd),
|
|
278
|
+
JSON.stringify({ executionMode: "auto", artifactStore: "hybrid", chainedPrStrategy: "ask-on-risk", reviewBudgetLines: 400, engramAvailable: true, prompted: true }),
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const loaded = readSddPreflightFromDisk(cwd, true);
|
|
282
|
+
assert.equal(loaded?.artifactStore, "hybrid");
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("slash SDD preflight trigger accepts the gentle-sdd command prefix", () => {
|
|
286
|
+
for (const text of ["/gentle-sdd-init", "/gentle-sdd-continue", "/gentle-sdd-status fix-rose --json", "/sdd", "/sdd:plan", "/sdd-plan this change"]) {
|
|
287
|
+
assert.equal(isSddPreflightTrigger(text), true, text);
|
|
288
|
+
}
|
|
289
|
+
for (const text of ["/gentle-sddx", "/gentle:sdd-preflight", "/gentle-status", "gentle-sdd-init"]) {
|
|
290
|
+
assert.equal(isSddPreflightTrigger(text), false, text);
|
|
291
|
+
}
|
|
292
|
+
});
|