pi-usereq 0.33.0 → 0.34.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.
@@ -383,11 +383,11 @@ PI-useReq is a TypeScript pi extension plus companion Node CLI and standalone ex
383
383
  - **REQ-152**: MUST render a persistent bottom-line description for the currently selected configuration entry.
384
384
  - **REQ-153**: MUST use scrollable configuration menus when entry count exceeds the visible row budget.
385
385
  - **REQ-154**: MUST wrap configuration-menu selection from last-to-first and first-to-last entries.
386
- - **REQ-321**: MUST render `Enable debug commands for tools` in `Debug` after `Log file` and before `Log on status`.
386
+ - **REQ-321**: MUST render `Enable debug commands for tools` in `Debug` after `Debug`, before `Log file`, and before `Log on status`.
387
387
  - **REQ-322**: MUST persist `DEBUG_TOOL_COMMANDS_ENABLED` through the `Debug` submenu with immediate-save, reset, and focus-preserving re-render behavior.
388
- - **REQ-323**: MUST register `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` only when `DEBUG_TOOL_COMMANDS_ENABLED=enable`.
389
- - **REQ-324**: MUST make `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` reuse the `compress`, `references`, `static-check`, and `tokens` runner outputs and write `content[0].text` to the editor.
390
- - **REQ-325**: MUST reject `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` execution when `DEBUG_TOOL_COMMANDS_ENABLED=disable`.
388
+ - **REQ-323**: MUST register `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` only when `DEBUG_TOOL_COMMANDS_ENABLED=enable`.
389
+ - **REQ-324**: MUST make `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` reuse the `compress`, `references`, `static-check`, `summarize`, and `tokens` runner outputs and write `content[0].text` to the editor.
390
+ - **REQ-325**: MUST reject `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` execution when `DEBUG_TOOL_COMMANDS_ENABLED=disable`.
391
391
 
392
392
  ## 4. Test Requirements
393
393
  - **TST-001**: MUST verify extension activation registers every documented prompt command, agent tool, and configuration command while omitting tool-name slash commands, `test-static-check`, and the removed standalone config-viewer command.
@@ -461,7 +461,7 @@ PI-useReq is a TypeScript pi extension plus companion Node CLI and standalone ex
461
461
  - **TST-027**: MUST verify harness inspection surfaces `files-compress` and `compress` descriptions covering parameters, line-number behavior, monolithic markdown output, and failure details.
462
462
  - **TST-028**: MUST verify `files-static-check` and `static-check` agent-tool outputs place the monolithic static-check report in `content[0].text` and restrict `details` to execution metadata.
463
463
  - **TST-029**: MUST verify harness inspection surfaces `files-static-check` and `static-check` descriptions covering parameters, monolithic output, selection rules, and failure details.
464
- - **TST-039**: MUST verify `.github/workflows/release-npm.yml` keeps the existing tag filter, gates downstream release work on `origin/master`, uses Node.js `24.15.0`, runs npm publication, and creates the GitHub Release from generated changelog text.
464
+ - **TST-039**: MUST verify `.github/workflows/release-npm.yml` parses as YAML when `PyYAML` is available, keeps the existing tag filter, gates downstream release work on `origin/master`, uses Node.js `24.15.0`, runs npm publication, and creates the GitHub Release.
465
465
  - **TST-042**: MUST verify `package.json` keeps `name` equal to `pi-usereq` so npm publication resolves to `https://www.npmjs.com/package/pi-usereq`.
466
466
  - **TST-044**: MUST verify `package.json` keeps npm provenance metadata aligned to the canonical GitHub repository, issues URL, and README homepage.
467
467
  - **TST-040**: MUST verify local and global configuration files omit derived static and dynamic path fields while runtime path context and status rendering still derive them correctly.
@@ -500,10 +500,10 @@ PI-useReq is a TypeScript pi extension plus companion Node CLI and standalone ex
500
500
  - **TST-066**: MUST verify `req-<prompt>` commands keep working when extension custom-tool registrations are removed from the runtime inventory.
501
501
  - **TST-059**: MUST verify every agent-tool registration defines custom `renderResult` and that compact rendering shows essential invocation parameters while expanded rendering avoids fallback raw-content display.
502
502
  - **TST-086**: MUST verify bundled prompt-backed `req-<prompt>` commands abort before prompt dispatch when the persisted execution-session header cwd or `process.cwd()` differs from the expected execution path, and abort before merge when persisted execution-session header metadata or verified worktree artifacts diverge, while stale pre-switch context probes alone do not abort.
503
- - **TST-113**: MUST verify default local configuration persists `DEBUG_TOOL_COMMANDS_ENABLED=disable`, and the `Debug` submenu renders `Enable debug commands for tools` before `Log on status`.
503
+ - **TST-113**: MUST verify default local configuration persists `DEBUG_TOOL_COMMANDS_ENABLED=disable`, and the `Debug` submenu renders `Enable debug commands for tools` before `Log file`.
504
504
  - **TST-114**: MUST verify the `Debug` submenu persists `DEBUG_TOOL_COMMANDS_ENABLED` through immediate-save, reset, and focus-preserving re-render flows.
505
- - **TST-115**: MUST verify extension activation registers `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` only when `DEBUG_TOOL_COMMANDS_ENABLED=enable`.
506
- - **TST-116**: MUST verify `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` write the same `content[0].text` as `compress`, `references`, `static-check`, and `tokens`, and reject execution when disabled.
505
+ - **TST-115**: MUST verify extension activation registers `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` only when `DEBUG_TOOL_COMMANDS_ENABLED=enable`.
506
+ - **TST-116**: MUST verify `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` write the same `content[0].text` as `compress`, `references`, `static-check`, `summarize`, and `tokens`, and reject execution when disabled.
507
507
 
508
508
  ## 5. Observed Component Model
509
509
 
@@ -888,7 +888,7 @@
888
888
  - `getProjectConfigPath(...)`: resolve config file path [`src/core/config.ts`]
889
889
  - `getDefaultConfig(...)`: build default config [`src/core/config.ts`]
890
890
  - `normalizeEnabledPiUsereqTools(...)`: canonicalize startup tools [`src/core/pi-usereq-tools.ts`]
891
- - `registerDebugToolCommands(...)`: register config-gated debug slash-command wrappers for `compress`, `references`, `static-check`, and `tokens` [`src/index.ts`]
891
+ - `registerDebugToolCommands(...)`: register config-gated debug slash-command wrappers for `compress`, `references`, `static-check`, `summarize`, and `tokens` [`src/index.ts`]
892
892
  - `registerAgentTools(...)`: register structured pi tools whose execute callbacks reuse internal runner families, the status-only references writer, token-optimized payload builders, and compact-plus-expanded custom result renderers [`src/index.ts`]
893
893
  - `ensureBundledResourcesAccessible(...)`: validate installation-owned bundled resources for tool executes that need bundled assets [`src/core/resources.ts`]
894
894
  - `getBundledResourceRoot(...)`: resolve installation-owned resource root [`src/core/resources.ts`]
@@ -1193,10 +1193,10 @@
1193
1193
  - `buildPiUsereqToolToggleChoices(...)`: serialize per-tool startup-toggle rows plus the value-less terminal reset action [`src/index.ts`]
1194
1194
  - `buildTerminalSettingsMenuChoices(...)`: append the canonical value-less `Reset defaults` row to the selector menu [`src/index.ts`]
1195
1195
  - `configureDebugMenu(...)`: interactive debug editor with in-place enable or disable toggles, tool-wrapper command registration, workflow filters, reset confirmation, and per-tool plus per-prompt selectors [`src/index.ts`]
1196
- - `buildDebugMenuChoices(...)`: serialize global debug controls, the `Enable debug commands for tools` row, workflow-event toggles, canonical tool and prompt toggles, and the value-less terminal reset row [`src/index.ts`]
1196
+ - `buildDebugMenuChoices(...)`: serialize global debug controls with `Enable debug commands for tools` before `Log file`, workflow-event toggles, canonical tool and prompt toggles, and the value-less terminal reset row [`src/index.ts`]
1197
1197
  - `buildDebugMenuChoice(...)`: dim and disable locked debug rows while global debug is off [`src/index.ts`]
1198
1198
  - `getDebugToolToggleNames(...)`: enumerate debuggable tool names in canonical grouped order [`src/index.ts`]
1199
- - `registerDebugToolCommands(...)`: register debug slash-command wrappers immediately when the tool-wrapper row transitions to `enable` [`src/index.ts`]
1199
+ - `registerDebugToolCommands(...)`: register debug slash-command wrappers including `debug-summarize` immediately when the tool-wrapper row transitions to `enable` [`src/index.ts`]
1200
1200
  - `selectDebugLogOnStatus(...)`: select one explicit workflow-state filter or `any` [`src/index.ts`]
1201
1201
  - `confirmResetChanges(...)`: require explicit approval before applying reset mutations [`src/index.ts`]
1202
1202
  - `resetDebugConfigToDefaults(...)`: restore the debug configuration subtree to documented defaults [`src/index.ts`]
package/src/index.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * @brief Declares the extension version string.
9
9
  * @details The value is exported for external inspection and packaging metadata alignment. Access complexity is O(1).
10
10
  */
11
- export const VERSION = "0.33.0";
11
+ export const VERSION = "0.34.0";
12
12
 
13
13
  import fs from "node:fs";
14
14
  import path from "node:path";
@@ -347,8 +347,8 @@ function executeDebugToolCommand(
347
347
  }
348
348
 
349
349
  /**
350
- * @brief Registers config-gated debug slash-command wrappers for selected built-in analysis tools.
351
- * @details Registers `debug-compress`, `debug-references`, `debug-static-check`, and `debug-tokens` as extension commands that reuse the same runner paths as the corresponding agent tools and write the resulting monolithic text into the editor instead of the LLM content channel. Re-registering the same commands is idempotent because pi keeps the latest same-extension command definition per name. Runtime is O(1) for registration; handler cost depends on the selected runner. Side effects include command registration.
350
+ * @brief Registers config-gated debug slash-command wrappers for selected project analysis tools.
351
+ * @details Registers `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, and `debug-tokens` as extension commands that reuse the same runner paths as the corresponding agent tools and write the resulting monolithic text into the editor instead of the LLM content channel. Re-registering the same commands is idempotent because pi keeps the latest same-extension command definition per name. Runtime is O(1) for registration; handler cost depends on the selected runner. Side effects include command registration.
352
352
  * @param[in] pi {ExtensionAPI} Active extension API instance.
353
353
  * @return {void} No return value.
354
354
  * @satisfies DES-015, REQ-323, REQ-324, REQ-325
@@ -382,6 +382,11 @@ function registerDebugToolCommands(pi: ExtensionAPI): void {
382
382
  "Run static-check and write the tool output into the editor",
383
383
  (projectBase, config) => executeMonolithicTool(() => runProjectStaticCheck(projectBase, config)),
384
384
  );
385
+ registerDebugToolCommand(
386
+ "debug-summarize",
387
+ "Run summarize and write the tool output into the editor",
388
+ (projectBase, config) => executeMonolithicTool(() => runSummarize(projectBase, config)),
389
+ );
385
390
  registerDebugToolCommand(
386
391
  "debug-tokens",
387
392
  "Run tokens and write the tool output into the editor",
@@ -1643,20 +1648,20 @@ function buildDebugMenuChoices(config: UseReqConfig): PiUsereqSettingsMenuChoice
1643
1648
  },
1644
1649
  buildDebugMenuChoice(
1645
1650
  {
1646
- id: "debug-log-file",
1647
- label: "Log file",
1648
- value: config.DEBUG_LOG_FILE,
1649
- description: "Edit the JSON debug log file path. Relative paths resolve against the original project base.",
1651
+ id: "debug-tool-commands-enabled",
1652
+ label: "Enable debug commands for tools",
1653
+ value: normalizeDebugToolCommandsEnabled(config.DEBUG_TOOL_COMMANDS_ENABLED),
1654
+ values: ["enable", "disable"],
1655
+ description: "Enable or disable slash-command wrappers for `compress`, `references`, `static-check`, `summarize`, and `tokens`.",
1650
1656
  },
1651
1657
  debugEnabled,
1652
1658
  ),
1653
1659
  buildDebugMenuChoice(
1654
1660
  {
1655
- id: "debug-tool-commands-enabled",
1656
- label: "Enable debug commands for tools",
1657
- value: normalizeDebugToolCommandsEnabled(config.DEBUG_TOOL_COMMANDS_ENABLED),
1658
- values: ["enable", "disable"],
1659
- description: "Enable or disable slash-command wrappers for `compress`, `references`, `static-check`, and `tokens`.",
1661
+ id: "debug-log-file",
1662
+ label: "Log file",
1663
+ value: config.DEBUG_LOG_FILE,
1664
+ description: "Edit the JSON debug log file path. Relative paths resolve against the original project base.",
1660
1665
  },
1661
1666
  debugEnabled,
1662
1667
  ),
@@ -842,6 +842,7 @@ test("extension registers prompt and config commands while exposing tool capabil
842
842
  "debug-compress",
843
843
  "debug-references",
844
844
  "debug-static-check",
845
+ "debug-summarize",
845
846
  "debug-tokens",
846
847
  "pi-usereq-show-config",
847
848
  "test-static-check",
@@ -869,7 +870,7 @@ test("extension registers prompt and config commands while exposing tool capabil
869
870
 
870
871
  /**
871
872
  * @brief Verifies debug tool wrapper commands register only when the local debug flag is enabled.
872
- * @details Activates the extension once from a default-disabled fixture and once from a fixture whose local config enables debug tool wrapper commands, then asserts the four debug slash commands appear only in the enabled runtime. Runtime is dominated by fixture setup and extension registration. Side effects are limited to temporary filesystem mutation and process cwd changes.
873
+ * @details Activates the extension once from a default-disabled fixture and once from a fixture whose local config enables debug tool wrapper commands, then asserts the five debug slash commands appear only in the enabled runtime. Runtime is dominated by fixture setup and extension registration. Side effects are limited to temporary filesystem mutation and process cwd changes.
873
874
  * @return {void} No return value.
874
875
  * @throws {AssertionError} Throws when command registration ignores the local debug tool wrapper flag.
875
876
  * @satisfies TST-115
@@ -881,6 +882,7 @@ test("debug tool commands register only when enabled in local config", () => {
881
882
  "debug-compress",
882
883
  "debug-references",
883
884
  "debug-static-check",
885
+ "debug-summarize",
884
886
  "debug-tokens",
885
887
  ];
886
888
 
@@ -1606,7 +1608,7 @@ test("static-check tools return monolithic text payloads with minimal execution
1606
1608
 
1607
1609
  /**
1608
1610
  * @brief Verifies debug tool wrapper commands mirror tool content and reject when disabled.
1609
- * @details Enables debug tool wrapper commands for one fixture project, compares each debug slash-command editor payload against the matching tool `content[0].text`, then disables the flag and verifies the already-registered wrapper rejects further execution. Runtime is dominated by tool execution plus temporary git fixture setup. Side effects are limited to temporary filesystem mutation, editor-text capture, and process cwd changes.
1611
+ * @details Enables debug tool wrapper commands for one fixture project, compares the five debug slash-command editor payloads against the matching tool `content[0].text`, then disables the flag and verifies the already-registered wrappers reject further execution. Runtime is dominated by tool execution plus temporary git fixture setup. Side effects are limited to temporary filesystem mutation, editor-text capture, and process cwd changes.
1610
1612
  * @return {Promise<void>} Promise resolved when the verification completes.
1611
1613
  * @throws {AssertionError} Throws when a debug wrapper output diverges from the wrapped tool text or disabled execution is accepted.
1612
1614
  * @satisfies TST-116
@@ -1643,6 +1645,9 @@ test("debug tool wrapper commands mirror tool content and reject when disabled",
1643
1645
  const staticCheckToolResult = await executeRegisteredTool(pi, "static-check", projectBase) as {
1644
1646
  content?: Array<{ type: string; text?: string }>;
1645
1647
  };
1648
+ const summarizeToolResult = await executeRegisteredTool(pi, "summarize", projectBase, {}) as {
1649
+ content?: Array<{ type: string; text?: string }>;
1650
+ };
1646
1651
  const tokensToolResult = await executeRegisteredTool(pi, "tokens", projectBase, {}) as {
1647
1652
  content?: Array<{ type: string; text?: string }>;
1648
1653
  };
@@ -1659,6 +1664,10 @@ test("debug tool wrapper commands mirror tool content and reject when disabled",
1659
1664
  await pi.commands.get("debug-static-check")!.handler("", staticCheckCtx);
1660
1665
  assert.equal(staticCheckCtx.__state.editorText, staticCheckToolResult.content?.[0]?.text ?? "");
1661
1666
 
1667
+ const summarizeCtx = createFakeCtx(projectBase);
1668
+ await pi.commands.get("debug-summarize")!.handler("", summarizeCtx);
1669
+ assert.equal(summarizeCtx.__state.editorText, summarizeToolResult.content?.[0]?.text ?? "");
1670
+
1662
1671
  const tokensCtx = createFakeCtx(projectBase);
1663
1672
  await pi.commands.get("debug-tokens")!.handler("", tokensCtx);
1664
1673
  assert.equal(tokensCtx.__state.editorText, tokensToolResult.content?.[0]?.text ?? "");
@@ -1666,12 +1675,20 @@ test("debug tool wrapper commands mirror tool content and reject when disabled",
1666
1675
  writeProjectConfigOverrides(projectBase, {
1667
1676
  DEBUG_TOOL_COMMANDS_ENABLED: "disable",
1668
1677
  });
1669
- const disabledCtx = createFakeCtx(projectBase);
1670
- await assert.rejects(
1671
- async () => pi.commands.get("debug-tokens")!.handler("", disabledCtx),
1672
- /disabled by Debug > Enable debug commands for tools/u,
1673
- );
1674
- assert.equal(disabledCtx.__state.editorText, "");
1678
+ for (const commandName of [
1679
+ "debug-compress",
1680
+ "debug-references",
1681
+ "debug-static-check",
1682
+ "debug-summarize",
1683
+ "debug-tokens",
1684
+ ]) {
1685
+ const disabledCtx = createFakeCtx(projectBase);
1686
+ await assert.rejects(
1687
+ async () => pi.commands.get(commandName)!.handler("", disabledCtx),
1688
+ /disabled by Debug > Enable debug commands for tools/u,
1689
+ );
1690
+ assert.equal(disabledCtx.__state.editorText, "");
1691
+ }
1675
1692
  } finally {
1676
1693
  process.chdir(previousCwd);
1677
1694
  fs.rmSync(projectBase, { recursive: true, force: true });
@@ -1968,6 +1985,10 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
1968
1985
  const debugCalls = ctx.__state.selectCalls.filter((call) => call.title === "Debug");
1969
1986
  assert.match(renderedInitialDebugMenu, /<dim>Log file/);
1970
1987
  assert.match(renderedInitialDebugMenu, /<dim>Enable debug commands for tools/);
1988
+ assert.ok(
1989
+ renderedInitialDebugMenu.indexOf("<dim>Enable debug commands for tools")
1990
+ < renderedInitialDebugMenu.indexOf("<dim>Log file"),
1991
+ );
1971
1992
  assert.match(renderedInitialDebugMenu, /<dim>Log on status/);
1972
1993
  assert.match(renderedInitialDebugMenu, /<dim>Status changes/);
1973
1994
  assert.match(renderedInitialDebugMenu, /<dim>Workflow events/);
@@ -1981,6 +2002,7 @@ test("debug menu dims locked rows and persists debug settings with focus-preserv
1981
2002
  assert.ok(pi.commands.get("debug-compress"));
1982
2003
  assert.ok(pi.commands.get("debug-references"));
1983
2004
  assert.ok(pi.commands.get("debug-static-check"));
2005
+ assert.ok(pi.commands.get("debug-summarize"));
1984
2006
  assert.ok(pi.commands.get("debug-tokens"));
1985
2007
  assert.deepEqual(persistedConfig.DEBUG_ENABLED_TOOLS, ["files-tokens"]);
1986
2008
  assert.deepEqual(persistedConfig.DEBUG_ENABLED_PROMPTS, ["req-analyze"]);
@@ -2023,8 +2045,8 @@ test("debug menu rows derive from canonical tool and prompt inventories", async
2023
2045
 
2024
2046
  assert.deepEqual(ctx.__state.selectCalls[1]?.items ?? [], [
2025
2047
  "Debug",
2026
- "Log file",
2027
2048
  "Enable debug commands for tools",
2049
+ "Log file",
2028
2050
  "Log on status",
2029
2051
  "Status changes",
2030
2052
  "Workflow events",
@@ -1,4 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
+ import { spawnSync } from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import test from "node:test";
@@ -16,6 +17,61 @@ function readReleaseWorkflow(): string {
16
17
  );
17
18
  }
18
19
 
20
+ /**
21
+ * @brief Verifies the release workflow YAML parses successfully via Python.
22
+ * @details Resolves a project-local `.venv/bin/python` first, then falls back to `python` and `python3`, executes an inline `yaml.safe_load(...)` check against `.github/workflows/release-npm.yml`, accepts the `PyYAML not available` skip path, and fails on non-zero exit status or `YAML ERROR` output. Runtime is dominated by subprocess probing and one Python execution. Side effects are limited to subprocess creation and filesystem reads performed by the Python snippet.
23
+ * @return {void} No return value.
24
+ * @throws {AssertionError} Throws when no usable Python interpreter exists or the workflow YAML fails to parse.
25
+ * @satisfies TST-039
26
+ */
27
+ function assertReleaseWorkflowYamlSyntax(): void {
28
+ const workflowPath = path.join(
29
+ process.cwd(),
30
+ ".github",
31
+ "workflows",
32
+ "release-npm.yml",
33
+ );
34
+ const pythonCandidates = [
35
+ path.join(process.cwd(), ".venv", "bin", "python"),
36
+ "python",
37
+ "python3",
38
+ ];
39
+ const pythonCommand = pythonCandidates.find((candidate) => {
40
+ if (candidate.includes(path.sep) && !fs.existsSync(candidate)) {
41
+ return false;
42
+ }
43
+ const probe = spawnSync(candidate, ["--version"], { encoding: "utf8" });
44
+ return !probe.error && probe.status === 0;
45
+ });
46
+ assert.ok(pythonCommand, "python executable not found for release workflow validation");
47
+
48
+ const pythonCode = [
49
+ "from pathlib import Path",
50
+ "import sys",
51
+ `p = Path(${JSON.stringify(workflowPath)})`,
52
+ "text = p.read_text()",
53
+ "try:",
54
+ " import yaml",
55
+ "except Exception as error:",
56
+ " print(f'PyYAML not available: {error}')",
57
+ " sys.exit(0)",
58
+ "try:",
59
+ " yaml.safe_load(text)",
60
+ " print('YAML OK')",
61
+ "except Exception as error:",
62
+ " print(f'YAML ERROR: {error}')",
63
+ " sys.exit(1)",
64
+ ].join("\n");
65
+ const result = spawnSync(pythonCommand, ["-c", pythonCode], {
66
+ cwd: process.cwd(),
67
+ encoding: "utf8",
68
+ });
69
+ const output = `${result.stdout}${result.stderr}`;
70
+
71
+ assert.equal(result.status, 0, output || result.error?.message || "unknown python execution failure");
72
+ assert.doesNotMatch(output, /YAML ERROR:/u);
73
+ }
74
+
19
75
  /**
20
76
  * @brief Describes the package manifest fields asserted by npm release tests.
21
77
  * @details Constrains the parsed `package.json` shape to the publication identity and provenance metadata required by the release workflow tests. The interface is compile-time only and introduces no runtime side effects.
@@ -75,6 +131,11 @@ function assertCanonicalPackageProvenanceMetadata(): void {
75
131
  assert.equal(manifest.homepage, "https://github.com/Ogekuri/PI-useReq#readme");
76
132
  }
77
133
 
134
+ test(
135
+ "release workflow remains valid YAML when parsed by Python",
136
+ assertReleaseWorkflowYamlSyntax,
137
+ );
138
+
78
139
  test(
79
140
  "release workflow keeps the current tag trigger and gates release work on origin/master ancestry",
80
141
  () => {