@dailephd/my-dev-kit-lab 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +5 -2
- package/dist/src/tutorial/scenarioValidation.js +11 -0
- package/dist/src/tutorial/tutorialActions.js +14 -0
- package/dist/src/tutorial/tutorialSession.js +3 -0
- package/dist/src/tutorial/types.js +1 -0
- package/examples/tutorial-browser/index.html +5 -0
- package/examples/tutorial-browser/scenario.json +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to my-dev-kit-lab are documented here.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.4.9] - 2026-09-20
|
|
8
|
+
|
|
9
|
+
Semantic native-select option selection for declarative browser tutorials.
|
|
10
|
+
|
|
11
|
+
- Added the value-only `select-option` declarative tutorial action, anchored to one existing `TutorialLocatorV1` and identifying a native HTML `<select>` option by its stable HTML `value`.
|
|
12
|
+
- Executed the action through Playwright `Locator.selectOption({ value })` using the existing tutorial timeout model, with no retry.
|
|
13
|
+
- Verified the returned selection exactly: the action passes only when the browser reports one selected value equal to the requested value; empty, mismatched, and multiple returned values fail.
|
|
14
|
+
- Added no keyboard fallback: `ArrowDown`/`Enter` emulation, click fallback, `page.evaluate`, and event dispatch are all absent, and `press` remains real keyboard input.
|
|
15
|
+
- Applied closed validation: `locator` and a non-empty trimmed `value` are required, timeouts use the existing contract, and unknown fields — including `label`, `index`, and `values` — are rejected rather than ignored.
|
|
16
|
+
- Moved the synthetic cursor to the select element before execution without fabricating a click ripple or animating the native popup menu.
|
|
17
|
+
- Extended the generic fixture (`examples/tutorial-browser/`) with a visible native `Operation` select whose ordinary `change` handler updates visible application state, and extended the canonical scenario to ten steps.
|
|
18
|
+
- Proved the action against real Chromium, including test-owned live DOM inspection of the selected value and the resulting visible state.
|
|
19
|
+
- Proved the action from the exact packed npm candidate installed into a clean consumer through the new `TUTORIAL_SELECT_OPTION` gate, preserving installed-package, packaged-example, and target immutability.
|
|
20
|
+
- Validated on Windows, macOS, and Linux across Node 24 and Node latest through the existing CI matrix.
|
|
21
|
+
- Confirmed bounded read-only downstream `my-frontend-observer` compatibility: representative native selects are expressible through the generic value-only contract, with no Observer values added to lab production code and no Observer files changed.
|
|
22
|
+
- Kept `TutorialScenarioV1`, `TutorialTargetContractV1`, `TutorialRunResultV1`, and `TutorialManifestV1` schemas at version `1.0.0`.
|
|
23
|
+
|
|
5
24
|
## [0.4.8] - 2026-09-19
|
|
6
25
|
|
|
7
26
|
Locator-anchored pointer gestures for declarative browser tutorials.
|
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@ my-dev-kit-lab is the experiment, audit, and evidence companion for [my-dev-kit]
|
|
|
4
4
|
|
|
5
5
|
my-dev-kit provides local repository indexing and graph-guided retrieval. my-dev-kit-lab supplies the controlled benchmarks, agent adapters, metrics, security checks, and reports needed to evaluate when that retrieval is useful. Results are evidence for a specific target and configuration; they do not guarantee token savings or security.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Whole-ecosystem workflows are centralized in [my-dev-kit/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md). Its [command-surface compatibility map](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md#915-command-surface-compatibility-map) documents which Lab outputs can safely seed my-dev-kit investigation, Observer reference work, or Orchestrator evidence, and which look-alike artifacts are not direct inputs.
|
|
8
|
+
|
|
9
|
+
The latest release is v0.4.9 (semantic native-select option selection for declarative browser tutorials, adding the value-only `select-option` action backed by Playwright `Locator.selectOption({ value })`). The previous release, v0.4.8, added locator-anchored pointer gestures (`pointer-click` and `pointer-drag`) using normalized locator-relative fraction positions while existing `drag` remains element-to-element. The v0.4.5 context-integrity evaluation remains intentionally frozen against the published `@dailephd/my-dev-kit@1.10.4` and `@dailephd/my-dev-kit-orchestrator@1.2.3` contracts; those versions are historical validation baselines, not a statement that they are the ecosystem's current releases. See [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md).
|
|
8
10
|
|
|
9
11
|
v0.4.6 adds a supported `my-dev-kit-lab` installed CLI router (`--help`, `--version`, `security validate`, `audit`, the `experiment` family, `report render`, `plots generate`, `gallery build`, `demo final`, and the historical direct final-demo invocation form), a writable lab workspace model kept separate from the installed package and the inspected target, and a permanent packed-tarball installation/execution acceptance gate (`npm run verify:packed-package`). See [docs/ROADMAP.md](docs/ROADMAP.md) and [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md) for status detail and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the runtime path model. The "Installed CLI" section below documents the shipped command surface; the source-checkout `npm run` workflow in Quickstart remains available for contributors.
|
|
10
12
|
|
|
@@ -15,7 +17,7 @@ v0.4.6 adds a supported `my-dev-kit-lab` installed CLI router (`--help`, `--vers
|
|
|
15
17
|
- **Validate CLI/package security:** inspect dependencies, package contents, path and subprocess boundaries, malformed inputs, optional static scanners, and bounded fuzz targets.
|
|
16
18
|
- **Validate Android projects:** run nineteen static checks by default, with Gradle operations, external tools, and network access available only through explicit opt-in flags.
|
|
17
19
|
- **Review evidence:** generate JSON and HTML reports, SVG plots, optional screenshots, visualization demos, and a static gallery.
|
|
18
|
-
- **Generate declarative browser tutorials:** validate and execute bounded local browser scenarios with WebM recording, step screenshots, SRT/VTT subtitles, Markdown, and a tutorial manifest. Locator-anchored pointer gestures (`pointer-click` and `pointer-drag`) operate using normalized fraction coordinates inside a single located surface, while existing `drag` remains element-to-element. Tutorial execution requires a compatible locally installed Chromium; see [docs/COMMANDS.md](docs/COMMANDS.md) and [docs/TUTORIAL.md](docs/TUTORIAL.md) for details.
|
|
20
|
+
- **Generate declarative browser tutorials:** validate and execute bounded local browser scenarios with WebM recording, step screenshots, SRT/VTT subtitles, Markdown, and a tutorial manifest. Locator-anchored pointer gestures (`pointer-click` and `pointer-drag`) operate using normalized fraction coordinates inside a single located surface, while existing `drag` remains element-to-element and `press` remains real keyboard input. `select-option` performs semantic native HTML `<select>` option selection by stable HTML value, executed through Playwright `Locator.selectOption({ value })` rather than keyboard navigation. Tutorial execution requires a compatible locally installed Chromium; see [docs/COMMANDS.md](docs/COMMANDS.md) and [docs/TUTORIAL.md](docs/TUTORIAL.md) for details.
|
|
19
21
|
- **Evaluate stage-context strategies:** compare the two legacy strategies against six additional bounded stage-context strategies — `architecture-context-only`, `architecture-plus-implementation-refresh`, `architecture-plus-implementation-and-test-refresh`, `full-workflow-library`, `bounded-workflow-instruction-packet`, and `combined-bounded-stage-context` — selected through programmatic configuration, not CLI flags. Each strategy's evidence is reported through bounded `report.json`, `report.html`, and `report.txt` output with an explicit `available`/`unavailable`/`not-applicable` metric-availability model and no composite score, grade, ranking, or winning strategy.
|
|
20
22
|
- **Evaluate the producer-readiness bridge (v0.4.4):** optionally extend `combined-bounded-stage-context` with the frozen my-dev-kit-orchestrator supplemental implementation/test-context packet and retrieval-report documents plus an observed readiness result, all supplied programmatically (there is no CLI flag), to measure owner, allocation, truncation-cause, supplemental/raw agreement, readiness-agreement, and criticality-overlay evidence without reimplementing upstream owner-selection, allocation, producer-parity, or readiness policy.
|
|
21
23
|
- **Evaluate context integrity (v0.4.5):** compare condition-aware producer evidence from my-dev-kit v1.10.4 (role condition coverage, allocation/spillover, required-evidence-loss) against my-dev-kit-orchestrator v1.2.3 run-integrity evidence (run-integrity gate, judge integrity, final-report eligibility, artifact lifecycle state), reporting agreement or contradiction between them rather than re-deriving a verdict. Evaluation runs against a frozen, hash-verified regression fixture pair — a byte-exact real historical failed run and a hand-distilled corrected-replay counterpart representing the same validated contracts — and is programmatic/test-driven only. `npm run report:context-integrity-smoke` renders both fixtures' reports for manual inspection; it takes no arguments and is a developer convenience, not a configurable evaluation CLI.
|
|
@@ -210,6 +212,7 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
|
|
|
210
212
|
- The v0.4.5 context-integrity evaluation is released. It has no CLI flags, no plots/screenshot/gallery integration, and produces no composite score, grade, ranking, or winner. Its corrected-replay fixture is a hand-distilled representation of the validated my-dev-kit v1.10.4 and my-dev-kit-orchestrator v1.2.3 contracts, not a live capture of a complete ten-stage workflow. The orchestrator agreement evidence does not read a literal upstream `promptMode` field; `stageMayRenderNormalPrompt`, derived from structured blocked-stage evidence, is the bounded substitute used instead.
|
|
211
213
|
- v0.4.7 added declarative browser tutorial validation and execution with bounded actions/assertions, persistent Chromium sessions, WebM, screenshots, SRT/VTT, Markdown, and `tutorial-manifest.json`. Chromium is a separate local runtime prerequisite; the package does not download browser binaries automatically. Gallery consumption remains future scope.
|
|
212
214
|
- v0.4.8 added `pointer-click` and `pointer-drag` for normalized fraction positions inside a single located element. Existing `drag` remains element-to-element between source and target locators.
|
|
215
|
+
- v0.4.9 `select-option` identifies an option by HTML value only: label selection, index selection, multi-select arrays, and automatic selector fallback are not supported, and there is no keyboard (`ArrowDown`/`Enter`) fallback. Native popup/menu traversal is not animated. Chromium remains a separately installed local runtime prerequisite; neither package installation nor tutorial execution downloads it automatically.
|
|
213
216
|
|
|
214
217
|
---
|
|
215
218
|
|
|
@@ -266,6 +266,17 @@ export function validateAction(errors, location, value) {
|
|
|
266
266
|
validateLocator(errors, `${location}.target`, value.target);
|
|
267
267
|
validateOptionalTimeout(errors, `${location}.timeoutMs`, value.timeoutMs);
|
|
268
268
|
break;
|
|
269
|
+
case "select-option":
|
|
270
|
+
// Closed to `value` alone: `label`, `index` and `values` are unknown
|
|
271
|
+
// fields here, so a scenario cannot reach a selection mode the runtime
|
|
272
|
+
// does not implement by guessing at Playwright's wider API.
|
|
273
|
+
rejectUnknownKeys(errors, location, value, ["type", "locator", "value", "timeoutMs"]);
|
|
274
|
+
validateLocator(errors, `${location}.locator`, value.locator);
|
|
275
|
+
if (!isNonEmptyString(value.value)) {
|
|
276
|
+
errors.add(`${location}.value`, `expected a non-empty option value string after trimming, received ${describeValue(value.value)}.`);
|
|
277
|
+
}
|
|
278
|
+
validateOptionalTimeout(errors, `${location}.timeoutMs`, value.timeoutMs);
|
|
279
|
+
break;
|
|
269
280
|
case "pointer-click":
|
|
270
281
|
rejectUnknownKeys(errors, location, value, ["type", "locator", "position", "coordinateSpace", "timeoutMs"]);
|
|
271
282
|
validateLocator(errors, `${location}.locator`, value.locator);
|
|
@@ -99,6 +99,18 @@ async function performAction(page, action, context) {
|
|
|
99
99
|
await runWithMouseDown(page.mouse, () => page.mouse.move(end.x, end.y, { steps: POINTER_DRAG_MOVE_STEPS }));
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
+
case "select-option": {
|
|
103
|
+
const locator = resolveTutorialLocator(page, action.locator);
|
|
104
|
+
const selected = await locator.selectOption({ value: action.value }, { timeout: timeoutFor(action.timeoutMs) });
|
|
105
|
+
// Playwright resolves to the values it actually selected. A mismatched or
|
|
106
|
+
// empty result means the intended option was not chosen, so it is a
|
|
107
|
+
// failure rather than a silently-accepted no-op; more than one value
|
|
108
|
+
// would mean a multi-select, which this contract does not express.
|
|
109
|
+
if (selected.length !== 1 || selected[0] !== action.value) {
|
|
110
|
+
throw new Error(`expected exactly one selected value ${JSON.stringify(action.value)}, but the select reported ${JSON.stringify(selected)}`);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
102
114
|
case "wait-for":
|
|
103
115
|
await resolveTutorialLocator(page, action.locator).waitFor({
|
|
104
116
|
...(action.state !== undefined ? { state: action.state } : {}),
|
|
@@ -159,6 +171,8 @@ function describeActionError(action, error) {
|
|
|
159
171
|
return `pointer-click on ${describeTutorialLocator(action.locator)} at fraction ${describeFractionPoint(action.position)} failed: ${message}`;
|
|
160
172
|
case "pointer-drag":
|
|
161
173
|
return `pointer-drag on ${describeTutorialLocator(action.locator)} from fraction ${describeFractionPoint(action.from)} to ${describeFractionPoint(action.to)} failed: ${message}`;
|
|
174
|
+
case "select-option":
|
|
175
|
+
return `select-option on ${describeTutorialLocator(action.locator)} for value ${JSON.stringify(action.value)} failed: ${message}`;
|
|
162
176
|
case "click":
|
|
163
177
|
case "fill":
|
|
164
178
|
case "press":
|
|
@@ -239,6 +239,9 @@ export function cursorLocatorForAction(action) {
|
|
|
239
239
|
case "fill":
|
|
240
240
|
case "press":
|
|
241
241
|
case "hover":
|
|
242
|
+
// The cursor rests on the select itself; the native popup is OS-drawn and
|
|
243
|
+
// is deliberately not animated through.
|
|
244
|
+
case "select-option":
|
|
242
245
|
case "pointer-click":
|
|
243
246
|
case "pointer-drag":
|
|
244
247
|
return action.locator;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
button { font: inherit; padding: 8px 16px; border-radius: 6px; border: 1px solid #5a6270; background: #fff; cursor: pointer; }
|
|
13
13
|
button:hover { background: #eef1f5; }
|
|
14
14
|
input { font: inherit; padding: 8px; border: 1px solid #b8bec8; border-radius: 6px; width: 100%; box-sizing: border-box; }
|
|
15
|
+
select { font: inherit; padding: 8px; border: 1px solid #b8bec8; border-radius: 6px; }
|
|
15
16
|
.dropzone { min-height: 72px; border: 2px dashed #9aa3af; border-radius: 8px; padding: 12px; }
|
|
16
17
|
.card { background: #dde7ff; border: 1px solid #5f7fd6; border-radius: 6px; padding: 10px; width: 140px; }
|
|
17
18
|
#pointer-surface { display: block; width: 400px; height: 240px; background: #eef4ff; box-shadow: inset 0 0 0 2px #7890c2; touch-action: none; user-select: none; }
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
<div class="panel"><p id="intro-text">Welcome to the generic tutorial fixture.</p><button id="primary-action" data-testid="primary-action" data-state="idle">Activate</button> <span id="activation-count" data-testid="activation-count">0</span></div>
|
|
27
28
|
<div class="panel"><label for="name-field">Your name</label><input id="name-field" data-testid="name-field" aria-label="Your name" /><p id="greeting" data-testid="greeting"></p></div>
|
|
28
29
|
<div class="panel"><button id="hover-target" data-testid="hover-target">Hover me</button><p id="hover-readout" data-testid="hover-readout">not hovered</p></div>
|
|
30
|
+
<div class="panel"><label for="operation-select">Operation</label> <select id="operation-select" data-testid="operation-select" aria-label="Operation"><option value="inspect">Inspect</option><option value="move">Move</option><option value="resize">Resize</option><option value="remove">Remove</option><option value="preserve">Preserve</option></select> <span id="selected-operation" data-testid="selected-operation" data-selected-value="inspect">inspect</span></div>
|
|
29
31
|
<div class="panel"><div id="status-banner" data-testid="status-banner" class="hidden">Delayed banner is now visible</div></div>
|
|
30
32
|
<script>
|
|
31
33
|
(() => {
|
|
@@ -37,6 +39,9 @@
|
|
|
37
39
|
nameField.addEventListener("input", () => { greeting.textContent = nameField.value ? `Hello, ${nameField.value}` : ""; });
|
|
38
40
|
nameField.addEventListener("keydown", event => { if (event.key === "Enter") greeting.textContent = `Submitted: ${nameField.value}`; });
|
|
39
41
|
document.getElementById("hover-target").addEventListener("mouseenter", () => { document.getElementById("hover-readout").textContent = "hovered"; });
|
|
42
|
+
const operationSelect = document.getElementById("operation-select");
|
|
43
|
+
const selectedOperation = document.getElementById("selected-operation");
|
|
44
|
+
operationSelect.addEventListener("change", () => { selectedOperation.textContent = operationSelect.value; selectedOperation.setAttribute("data-selected-value", operationSelect.value); });
|
|
40
45
|
const dragCard = document.getElementById("drag-card");
|
|
41
46
|
const dropZone = document.getElementById("drop-zone");
|
|
42
47
|
dragCard.addEventListener("dragstart", event => event.dataTransfer.setData("text/plain", "drag-card"));
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
{ "id": "drag-card", "narration": "Drag the card onto the drop target.", "action": { "type": "drag", "source": { "kind": "test-id", "testId": "drag-card" }, "target": { "kind": "test-id", "testId": "drop-zone" } }, "highlight": { "kind": "test-id", "testId": "drop-zone" }, "screenshot": { "id": "dropped" }, "assertions": [ { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "drop-zone" }, "name": "data-dropped", "expected": "true" } ] },
|
|
15
15
|
{ "id": "pointer-click-surface", "narration": "Click a deliberate position inside the pointer gesture surface.", "action": { "type": "pointer-click", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "position": { "x": 0.25, "y": 0.25 }, "coordinateSpace": "fraction" }, "highlight": { "kind": "test-id", "testId": "pointer-surface" }, "screenshot": { "id": "pointer-clicked" }, "assertions": [ { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-pointer-click-count", "expected": "1" }, { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-last-pointer-click-zone", "expected": "upper-left" } ] },
|
|
16
16
|
{ "id": "pointer-drag-surface", "narration": "Drag across two distinct positions inside the same pointer gesture surface.", "action": { "type": "pointer-drag", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "from": { "x": 0.20, "y": 0.25 }, "to": { "x": 0.80, "y": 0.75 }, "coordinateSpace": "fraction" }, "screenshot": { "id": "pointer-dragged" }, "assertions": [ { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-pointer-drag-count", "expected": "1" }, { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-pointer-drag-start-zone", "expected": "upper-left" }, { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-pointer-drag-end-zone", "expected": "lower-right" }, { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "pointer-surface" }, "name": "data-pointer-drag-intermediate", "expected": "true" } ] },
|
|
17
|
+
{ "id": "select-operation", "narration": "Select the preserve operation from the native selector.", "action": { "type": "select-option", "locator": { "kind": "role", "role": "combobox", "name": "Operation" }, "value": "preserve" }, "highlight": { "kind": "role", "role": "combobox", "name": "Operation" }, "screenshot": { "id": "operation-selected" }, "assertions": [ { "type": "text-equals", "locator": { "kind": "test-id", "testId": "selected-operation" }, "expected": "preserve" }, { "type": "attribute-equals", "locator": { "kind": "test-id", "testId": "selected-operation" }, "name": "data-selected-value", "expected": "preserve" } ] },
|
|
17
18
|
{ "id": "wait-banner", "narration": "Wait for the delayed status banner to appear.", "action": { "type": "wait-for", "locator": { "kind": "text", "text": "Delayed banner is now visible" }, "state": "visible", "timeoutMs": 5000 }, "callout": { "text": "The banner appears after a short delay." }, "screenshot": { "id": "banner-visible" }, "assertions": [ { "type": "element-visible", "locator": { "kind": "test-id", "testId": "status-banner" } } ] }
|
|
18
19
|
]
|
|
19
20
|
}
|