docs: reframe feature guides and mark experimental surfaces (#1760)
* docs: reframe feature guides and mark experimental surfaces * fix: tighten task demo framing * fix: validate experimental page status * docs: anchor experimental office status * fix(ci): isolate Wrangler preview install * docs: complete focused camera example * fix(ci): use valid Wrangler package name
@@ -20,7 +20,7 @@ Use a Kandev checkout with its web E2E fixtures, Playwright dependencies, pnpm t
|
||||
5. Create a fresh bogus repository, workspace, workflow, tasks, sessions, and provider state through supported E2E/API methods.
|
||||
6. Seed only enough state to make the story legible. Dense believable data beats empty fixtures; excessive data hides the action.
|
||||
7. Open the intended UI and verify every visible label, control, and transition before recording.
|
||||
8. Hand the scenario, route, selectors, semantic target bounds, and cleanup command to `/product-video-capture`.
|
||||
8. Hand the scenario, route, selectors, semantic target bounds, complete intentional pointer journeys, and cleanup command to `/product-video-capture`.
|
||||
|
||||
## Safety Contract
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ Produce reusable clean masters first; derive presentation from them later.
|
||||
2. Create a unique writable `CAPTURE_ROOT`, for example with `mktemp -d "${TMPDIR:-/tmp}/kandev-product-capture.XXXXXX"`. Use it for every raw, proof, config, and staged delivery path.
|
||||
3. Review the seed handoff and rehearse the full native desktop/mobile story once.
|
||||
4. Record one continuous, unzoomed, high-resolution master per form factor. Use true physical pixels, not a padded Playwright video canvas.
|
||||
5. Record semantic action timestamps, target bounds, and pointer/touch journeys beside the raw file.
|
||||
5. Record semantic action timestamps, target bounds, and dense pointer/touch journeys beside the raw file. Include each intentional movement's start, intermediate samples, arrival, and visibility interval.
|
||||
6. Stop recording before capturing the clean poster.
|
||||
7. Inspect raw frames before post-production. Reject UI bugs, padding, double cursors, fixture text, dead waits, and unreadable states.
|
||||
8. Build a smooth post camera from semantic events. Camera follows story focus, not every cursor twitch.
|
||||
8. Build a smooth post camera from semantic events. Ignore micro-jitter, but keep every intentional pointer/touch journey inside the tested safe frame. Widen before long travel and ease toward the destination with the pointer.
|
||||
9. Encode WebM, MP4, and WebP through landing's tested camera/encoder scripts.
|
||||
10. Review fixed-fraction frames and playback on desktop, native mobile, and reduced motion.
|
||||
11. Copy only approved delivery assets into `public/product/loops/`; keep raw/proof files outside production unless requested.
|
||||
11. Copy only approved delivery assets into the owning production media directory; keep raw/proof files outside production unless requested.
|
||||
|
||||
Read [capture-pipeline.md](references/capture-pipeline.md) before recording and [camera-encoding.md](references/camera-encoding.md) before conforming media.
|
||||
|
||||
@@ -63,8 +63,8 @@ Follow [qa-checklist.md](references/qa-checklist.md). Do not ship until:
|
||||
- raw and delivery dimensions are proven by decoded pixels, not only container metadata;
|
||||
- cadence is constant and timestamps have no gaps;
|
||||
- 10/25/50/75/90% frames and full playback pass visual review;
|
||||
- camera motion is smooth, reaches intended depth, and settles on centered 1x before looping;
|
||||
- text, menus, diffs, pointer, and touch targets remain inside frame;
|
||||
- camera motion is smooth, reaches intended depth, and uses the profile's tested loop frame: centered 1x by default, or one identical focused start/end crop for a short docs clip when it removes irrelevant chrome or fixture-only detail;
|
||||
- text, menus, diffs, pointer, and touch targets remain inside frame; frame-by-frame pointer containment passes with a deliberate edge margin;
|
||||
- WebM, MP4, poster, responsive source selection, lazy loading, and reduced-motion behavior pass;
|
||||
- all capture processes, ports, temporary specs, and temp data are gone.
|
||||
|
||||
|
||||
@@ -20,9 +20,10 @@ Run focused tests from `$LANDING_REPO` before encoding. Current contract:
|
||||
- 25 fps constant cadence;
|
||||
- desktop delivery 2560x1600 from at least 3840x2400 source;
|
||||
- mobile delivery 1290x2796 from native 1290x2796 source;
|
||||
- desktop reaches 1.20x; mobile reaches 1.14x;
|
||||
- centered 1x opening and ending;
|
||||
- at least 240ms settled centered 1x before loop reset;
|
||||
- landing desktop and focused documentation clips reach 1.50x; native mobile reaches 1.18x;
|
||||
- centered 1x opening and ending by default;
|
||||
- an opt-in docs-only focused loop frame when the first, settled penultimate, and final camera frames match exactly;
|
||||
- at least 240ms on the settled loop frame before reset;
|
||||
- cosine-eased piecewise motion with per-frame smoothness checks;
|
||||
- one trim, no concat, no speed-ramp `setpts`;
|
||||
- muted VP9 WebM, H.264 MP4 with fast start, and WebP poster.
|
||||
@@ -31,17 +32,44 @@ Treat tests as source of truth if these values evolve.
|
||||
|
||||
## Camera Design
|
||||
|
||||
Design 4-7 keyframes from semantic story events:
|
||||
Design keyframes from semantic story events and the recorded pointer journey:
|
||||
|
||||
1. Start centered at 1x.
|
||||
1. Start centered at 1x unless a short docs clip intentionally uses one matched focused loop frame to exclude irrelevant chrome or fixture-only detail.
|
||||
2. Hold context briefly.
|
||||
3. Ease toward the first important target while the pointer travels.
|
||||
4. Hold or drift gently across related actions; do not chase every click.
|
||||
4. Hold or drift gently across related actions. Ignore micro-jitter, but never let intentional pointer travel leave the crop.
|
||||
5. Move focus only when story focus changes materially.
|
||||
6. Ease back to centered 1x before the final settled hold.
|
||||
6. Ease back to the opening loop frame before the final settled hold.
|
||||
|
||||
Use normalized centers from target bounds. Keep full menus/dialogs/diffs inside the crop at maximum zoom. Optical focus may differ from exact pointer center when surrounding context is important.
|
||||
|
||||
Pass normalized `pointerTrack` waypoints and a `pointerSafeMargin` to `createCameraTrack`. The camera module checks every output frame, not only click timestamps. Derive the margin from the complete rendered glyph around its hotspot; an asymmetric `{ top, right, bottom, left }` map is valid when the pointer orientation requires it. A failed containment check blocks delivery. Do not claim a clipped source cursor is safe merely because its hotspot remains in frame.
|
||||
|
||||
For long journeys between distant regions, do not pan a tight crop after the pointer has already left. Ease out far enough to contain both endpoints, begin the pan before or with the intentional movement, then ease back into the destination. A wide transition is preferable to an unexplained off-screen pointer.
|
||||
|
||||
Use `loopFrame: "focused"` only with the `docs` form factor. The opening, settled penultimate, and final camera keyframes must be identical, and the crop must still show enough context to identify the feature. This is an editorial framing tool, not a way to hide a product defect or misleading state. Landing desktop/mobile media must keep the standard wide loop reset.
|
||||
|
||||
The camera section for a focused docs clip has this shape; add story keyframes between the matching opening and settled pair:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"durationMs": 8000,
|
||||
"formFactor": "docs",
|
||||
"loopFrame": "focused",
|
||||
"pointerSafeMargin": 0.08,
|
||||
"pointerTrack": [
|
||||
{ "tMs": 0, "x": 0.48, "y": 0.46 },
|
||||
{ "tMs": 8000, "x": 0.48, "y": 0.46 }
|
||||
],
|
||||
"keyframes": [
|
||||
{ "tMs": 0, "zoom": 1.5, "x": 0.48, "y": 0.46 },
|
||||
// Story keyframes follow the recorded pointer journey.
|
||||
{ "tMs": 7760, "zoom": 1.5, "x": 0.48, "y": 0.46 },
|
||||
{ "tMs": 8000, "zoom": 1.5, "x": 0.48, "y": 0.46 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Example config shape. Replace `<capture-root>` with the unique resolved `CAPTURE_ROOT` before encoding:
|
||||
|
||||
```json
|
||||
@@ -58,11 +86,18 @@ Example config shape. Replace `<capture-root>` with the unique resolved `CAPTURE
|
||||
"camera": {
|
||||
"durationMs": 8400,
|
||||
"formFactor": "desktop",
|
||||
"pointerSafeMargin": 0.08,
|
||||
"pointerTrack": [
|
||||
{ "tMs": 0, "x": 0.5, "y": 0.5 },
|
||||
{ "tMs": 2700, "x": 0.62, "y": 0.38 },
|
||||
{ "tMs": 5900, "x": 0.56, "y": 0.61 },
|
||||
{ "tMs": 8400, "x": 0.5, "y": 0.5 }
|
||||
],
|
||||
"keyframes": [
|
||||
{ "tMs": 0, "zoom": 1, "x": 0.5, "y": 0.5 },
|
||||
{ "tMs": 450, "zoom": 1, "x": 0.5, "y": 0.5 },
|
||||
{ "tMs": 2700, "zoom": 1.2, "x": 0.57, "y": 0.43 },
|
||||
{ "tMs": 5900, "zoom": 1.2, "x": 0.55, "y": 0.57 },
|
||||
{ "tMs": 2700, "zoom": 1.5, "x": 0.57, "y": 0.43 },
|
||||
{ "tMs": 5900, "zoom": 1.5, "x": 0.55, "y": 0.57 },
|
||||
{ "tMs": 8100, "zoom": 1, "x": 0.5, "y": 0.5 },
|
||||
{ "tMs": 8400, "zoom": 1, "x": 0.5, "y": 0.5 }
|
||||
]
|
||||
@@ -81,7 +116,7 @@ The encoder probes source duration and rejects an overrun. It writes `<slug>.web
|
||||
|
||||
## Avoid Awkward Motion
|
||||
|
||||
Jitter usually comes from too many keyframes, short segment durations, pointer-perfect tracking, or camera changes on the same frame as UI transitions. Fix by reducing targets, lengthening easing, and focusing on semantic regions.
|
||||
Jitter usually comes from too many keyframes, short segment durations, pointer-perfect tracking, or camera changes on the same frame as UI transitions. Fix by reducing targets, lengthening easing, and focusing on semantic regions. Cursor containment does not require centering every frame; it requires keeping the intentional journey within the safe crop.
|
||||
|
||||
Time skips usually come from concatenating holds, removing waits, or speeding mock-agent gaps. Fix source timing and recapture. Do not disguise them with crossfades.
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ Use one capture-only overlay:
|
||||
- OS pointer disabled through `-draw_mouse 0`;
|
||||
- pointer overlay hidden for poster capture.
|
||||
|
||||
Keep the complete rendered glyph inside the raw viewport. Preserve the hotspot on the real target and switch to an edge-safe glyph orientation near the right or bottom edge instead of clipping the overlay. Record both the hotspot and glyph bounds so post-camera validation can use the visible footprint.
|
||||
|
||||
Move real input and overlay together. Use 10-12 manually timed samples over roughly 300ms with cubic easing. Record for every arrival:
|
||||
|
||||
```json
|
||||
@@ -73,7 +75,9 @@ Move real input and overlay together. Use 10-12 manually timed samples over roug
|
||||
}
|
||||
```
|
||||
|
||||
Bounds use CSS pixels. Keep targets inside viewport and store the raw-story start time separately from event story time.
|
||||
Bounds use CSS pixels. Keep targets inside viewport and store the raw-story start time separately from event story time. Persist enough waypoints to reconstruct the complete intentional journey: the previous settled position, movement start, intermediate samples or easing contract, arrival, and the next movement start. Click-only metadata cannot prove cursor containment.
|
||||
|
||||
Before camera work, normalize pointer coordinates against the exact camera source. For full-frame capture, divide CSS coordinates by the CSS viewport. For a physical-pixel ROI, first multiply CSS coordinates by DPR, subtract the ROI origin, then divide by ROI dimensions. Reject a static ROI when any visible pointer waypoint falls outside it; use a dynamic full-frame camera instead.
|
||||
|
||||
## Choreography
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
- [ ] Raw is continuous 1x with no crop, camera, concat, speed change, or internal cut.
|
||||
- [ ] OS cursor disabled; exactly one DOM cursor/touch treatment visible.
|
||||
- [ ] Semantic events include timestamps, target bounds, and smooth motion samples.
|
||||
- [ ] Every visible pointer/touch waypoint is normalized against the camera source and has an explicit visibility interval.
|
||||
- [ ] Poster captured after recorder stops with pointer/touch hidden.
|
||||
|
||||
## 2. Technical Probe
|
||||
@@ -44,7 +45,8 @@ Watch the complete loop at normal speed and at 0.5x. Check:
|
||||
- no pointer teleport, duplicate pointer, or click before arrival;
|
||||
- no state jump, cut, speed-up, dead wait, blank beat, or loader hold;
|
||||
- camera reaches useful depth without oscillation;
|
||||
- camera remains within safe bounds and returns to centered 1x;
|
||||
- every intentional pointer/touch journey stays inside the camera crop with its configured edge-aware glyph margin at every encoded frame;
|
||||
- camera remains within safe bounds and returns to its tested loop frame: centered 1x by default, or the same focused frame at both ends for an explicitly focused docs clip;
|
||||
- loop reset is calm rather than a snap;
|
||||
- readable copy remains stable long enough to understand.
|
||||
|
||||
@@ -99,6 +101,7 @@ Smoke-check desktop, mobile, and `prefers-reduced-motion` in a real browser. Con
|
||||
- [ ] Encode to staging first.
|
||||
- [ ] Compare old/new loops side by side.
|
||||
- [ ] Promote only approved WebM/MP4/WebP files.
|
||||
- [ ] If a docs clip uses a focused loop frame, confirm the first and final crops match, retain identifying feature context, and exclude only irrelevant chrome or fixture-only detail.
|
||||
- [ ] Record SHA-256, dimensions, duration, codec, size, source seed, and capture command.
|
||||
- [ ] Keep raw/proof bundle outside production assets unless repository policy requests it.
|
||||
- [ ] Do not delete previous accepted media until replacement passes build and browser smoke.
|
||||
|
||||
@@ -103,6 +103,13 @@ jobs:
|
||||
echo "::warning::Docs preview deploy is not configured. Add CLOUDFLARE_API_TOKEN as a repository secret and CLOUDFLARE_ACCOUNT_ID as a repository variable."
|
||||
fi
|
||||
|
||||
- name: Prepare isolated Wrangler environment
|
||||
if: steps.cloudflare.outputs.enabled == 'true'
|
||||
run: |
|
||||
mkdir -p landing/wrangler-deploy
|
||||
cd landing/wrangler-deploy
|
||||
npm init --yes
|
||||
|
||||
- name: Deploy preview to Cloudflare Pages
|
||||
id: deploy
|
||||
if: steps.cloudflare.outputs.enabled == 'true'
|
||||
@@ -110,9 +117,11 @@ jobs:
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
|
||||
workingDirectory: landing
|
||||
workingDirectory: landing/wrangler-deploy
|
||||
packageManager: npm
|
||||
wranglerVersion: 3.90.0
|
||||
command: >-
|
||||
pages deploy out
|
||||
pages deploy ../out
|
||||
--project-name=landing
|
||||
--branch=docs-pr-${{ github.event.pull_request.number }}
|
||||
--commit-dirty=true
|
||||
|
||||
@@ -17,6 +17,31 @@ Use source-relative links such as `[CLI](cli.md)`. Publication rewrites links be
|
||||
|
||||
State whether behavior is default, optional, platform-dependent, experimental, feature-flagged, internal, or still in progress. An ADR, spec, hidden route, or source package is not evidence that a feature is generally available.
|
||||
|
||||
### Mark experimental content
|
||||
|
||||
Use page-level status when the whole guide describes an experimental surface:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Kubernetes"
|
||||
description: "Deploy Kandev to Kubernetes."
|
||||
status: experimental
|
||||
---
|
||||
```
|
||||
|
||||
The docs header renders a visible status callout from that frontmatter. To invoke this experimental page indicator, use the exact value `experimental`; omit `status` for stable pages. Source validation rejects other page-status values. The separate `stable`, `beta`, and `experimental` values in `coverage.json` describe inventory stability and do not create page badges.
|
||||
|
||||
Use a section-level indicator when a stable guide contains one experimental capability. Keep this source syntax as Markdown so older renderers and GitHub still show an explicit callout; the publication layer upgrades it to the styled status component:
|
||||
|
||||
```markdown
|
||||
## Office status
|
||||
|
||||
> [!EXPERIMENTAL]
|
||||
> Office is disabled by default and its public workflow is not stable yet.
|
||||
```
|
||||
|
||||
Keep the indicator next to the relevant heading and explain the enabling condition, current limit, and supported alternative. The publication pipeline converts the callout to `FeatureStatus` and opts the generated page into MDX automatically.
|
||||
|
||||
## Add a page
|
||||
|
||||
1. Search existing pages and choose **Use Kandev** or **Contribute to Kandev**.
|
||||
|
||||
@@ -125,7 +125,8 @@ Only custom TUI agents can be deleted from the agent list. Built-in definitions
|
||||
|
||||
## Office status
|
||||
|
||||
Office team roles, reporting lines, budgets, concurrency, and governance add more fields to agent profiles only when the experimental Office feature is enabled. Office is disabled by default and remains in progress; its coordinator routing and approval policy are not a stable public workflow. Regular profiles, workflow approval steps, and task-mode automation provide the supported human-gated path today.
|
||||
> [!EXPERIMENTAL]
|
||||
> Office team roles, reporting lines, budgets, concurrency, and governance add more fields to agent profiles only when the Office feature is enabled. Office is disabled by default and remains in progress; its coordinator routing and approval policy are not a stable public workflow. Regular profiles, workflow approval steps, and task-mode automation provide the supported human-gated path today.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -198,7 +198,8 @@ The backend's `/mcp`, `/mcp/sse`, and `/mcp/message` routes currently have no Ka
|
||||
|
||||
## Office status
|
||||
|
||||
Office has separate coordinator agents, routines, governance approvals, and an Office MCP/agentctl interface only when its experimental feature flag is enabled. Office is disabled by default and remains in progress; do not treat its coordinator-led automation as a stable public contract. Workspace automations plus workflow approval/review steps are the supported human-gated mechanism today.
|
||||
> [!EXPERIMENTAL]
|
||||
> Office has separate coordinator agents, routines, governance approvals, and an Office MCP/agentctl interface only when its feature flag is enabled. Office is disabled by default and remains in progress; do not treat its coordinator-led automation as a stable public contract. Workspace automations plus workflow approval/review steps are the supported human-gated mechanism today.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ Kandev can coordinate several agents without turning every unit of work into a s
|
||||
|
||||
Shared files make handoff cheap but allow concurrent edits to collide. Separate environments isolate file state, but only the executor determines whether host processes and credentials are also isolated.
|
||||
|
||||
> **Office status:** Office is in progress and feature-flagged. Persistent coordinator teams, arbitrary-depth task trees, dependency properties, labels, named documents, reviewer/approver quorum, routines, and budgets are not stable regular-Kanban capabilities.
|
||||
## Office status
|
||||
|
||||
> [!EXPERIMENTAL]
|
||||
> Office is in progress and feature-flagged. Persistent coordinator teams, arbitrary-depth task trees, dependency properties, labels, named documents, reviewer/approver quorum, routines, and budgets are not stable regular-Kanban capabilities.
|
||||
|
||||
## Run parallel sessions in one task
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: "Kubernetes"
|
||||
description: "Deploy a single Kandev control plane to Kubernetes with explicit persistence, security, and lifecycle constraints."
|
||||
status: experimental
|
||||
---
|
||||
|
||||
# Kubernetes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Kandev feature-guide media capture notes
|
||||
|
||||
Candidate generated in disposable capture root. Promote only after final parent review.
|
||||
Reviewed delivery generated from disposable captures and cursor-tracked again on 2026-07-17.
|
||||
|
||||
## Provenance and isolation
|
||||
|
||||
@@ -10,10 +10,14 @@ Candidate generated in disposable capture root. Promote only after final parent
|
||||
- Providers/agent: mock-only. No real provider call, production credential, shared user database, or existing task workspace.
|
||||
- Encoding metadata: `metadata/capture.json`
|
||||
- Source bundle: `source/capture.spec.ts`, `source/encode.mjs`, `source/product-loop-camera-compact.mjs`, `source/qa.mjs`, `source/smoke.spec.ts`
|
||||
- Reframe source: full 3840x2400 masters, never the previously cropped delivery files.
|
||||
- Reframe profile: Landing `scripts/product-loop-camera.mjs` docs profile, 1.50x maximum zoom, cosine easing, and frame-by-frame pointer containment. The two task clips use identical focused start/end crops to keep the feature legible while excluding the fixture-only Files toolbar and host path.
|
||||
|
||||
## Commands
|
||||
|
||||
- Capture/encoding source and per-clip camera values are retained in the disposable source and metadata bundle listed above.
|
||||
- Camera validation: `createCameraTrack({ pointerTrack, pointerSafeMargin, ... })` checks every 25fps output frame and blocks a crop that loses the pointer.
|
||||
- Long pointer journeys widen before travel and ease back into the destination instead of panning a tight crop after the pointer has left it.
|
||||
- QA: `node "$CAPTURE_ROOT/source/qa.mjs" --visual-pass --teardown-confirmed`
|
||||
- Probe template: `ffprobe -v error -show_streams -show_format -of json <file>`
|
||||
- Cadence template: `ffprobe -v error -select_streams v:0 -show_entries frame=best_effort_timestamp_time,pkt_duration_time -of json <video>`
|
||||
@@ -30,22 +34,23 @@ Candidate generated in disposable capture root. Promote only after final parent
|
||||
|
||||
| Clip | Duration | WebM | MP4 | Poster |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| task-create | 7.32s | 173.7 KiB | 205.2 KiB | 8.6 KiB |
|
||||
| task-start-agent | 12.80s | 184.2 KiB | 191.4 KiB | 10.4 KiB |
|
||||
| plan-review-implement | 13.60s | 393.6 KiB | 423.4 KiB | 19.1 KiB |
|
||||
| parallel-named-sessions | 8.80s | 236.0 KiB | 250.4 KiB | 11.6 KiB |
|
||||
| diff-line-feedback | 12.80s | 424.1 KiB | 560.6 KiB | 23.2 KiB |
|
||||
| code-walkthrough | 8.80s | 481.2 KiB | 545.0 KiB | 23.3 KiB |
|
||||
| profile-executor-selection | 8.80s | 188.6 KiB | 250.4 KiB | 8.9 KiB |
|
||||
| scheduled-workflow-automation | 13.60s | 414.7 KiB | 469.3 KiB | 13.0 KiB |
|
||||
| task-create | 6.80s | 120.0 KiB | 225.4 KiB | 10.2 KiB |
|
||||
| task-start-agent | 8.80s | 72.8 KiB | 136.6 KiB | 3.2 KiB |
|
||||
| plan-review-implement | 8.64s | 233.1 KiB | 273.2 KiB | 12.6 KiB |
|
||||
| parallel-named-sessions | 8.80s | 265.6 KiB | 281.8 KiB | 10.1 KiB |
|
||||
| diff-line-feedback | 12.80s | 540.1 KiB | 840.0 KiB | 17.1 KiB |
|
||||
| code-walkthrough | 8.80s | 478.1 KiB | 504.2 KiB | 19.5 KiB |
|
||||
| profile-executor-selection | 8.80s | 216.7 KiB | 294.1 KiB | 7.7 KiB |
|
||||
| scheduled-workflow-automation | 13.60s | 432.4 KiB | 438.7 KiB | 9.5 KiB |
|
||||
|
||||
- Total docs payload: 5.38 MiB.
|
||||
- Total docs payload: 5.32 MiB.
|
||||
|
||||
## QA
|
||||
|
||||
- Technical result: PASS (0 failure(s), 0 warning(s)).
|
||||
- Stream checks: codec, 960x600 decoded dimensions, exact 25fps rate metadata, continuous 0.040s decoded timestamps, 6-15s duration, and no audio.
|
||||
- Visual samples: PASS — human-reviewed full-resolution samples and mosaics.
|
||||
- Cursor containment: PASS — semantic journeys remain inside an edge-aware safe frame at every encoded frame; long-distance movements widen before travel. The task clips' matched focused loops were also reviewed at full resolution to confirm `/tmp`, host paths, and the Files toolbar never enter the delivery frame.
|
||||
- Automated blank-frame screening: sampled frames require non-trivial standard deviation/entropy.
|
||||
- Browser playback: PASS for WebM, MP4, and WebP.
|
||||
- Inspect mosaics for clipping, duplicated cursor, browser chrome, fixture/debug text, local paths, private data, unreadable text, and misleading states.
|
||||
@@ -60,4 +65,4 @@ Candidate generated in disposable capture root. Promote only after final parent
|
||||
- Per-clip contact sheets: `mosaics/<slug>.png`
|
||||
- Browser playback contact sheet: `mosaics/browser-playback.png`
|
||||
- Machine-readable QA: `metadata/qa-report.json`
|
||||
- Candidate manifest: `metadata/manifest.candidate.json`
|
||||
- Published manifest: `manifest.json`
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"generated_at": "2026-07-17T01:31:31.597Z",
|
||||
"generated_at": "2026-07-17T15:32:00.000Z",
|
||||
"qa_status": "accepted",
|
||||
"delivery_contract": {
|
||||
"dimensions": {
|
||||
@@ -13,7 +13,12 @@
|
||||
"vp9-webm",
|
||||
"h264-mp4"
|
||||
],
|
||||
"poster_format": "webp"
|
||||
"poster_format": "webp",
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "frame-by-frame edge-aware validation",
|
||||
"profile": "cursor-tracked-v2"
|
||||
}
|
||||
},
|
||||
"clips": [
|
||||
{
|
||||
@@ -24,7 +29,7 @@
|
||||
"section": "Create a task"
|
||||
},
|
||||
"accessible_caption": "A focused task is entered while its repository, agent profile, worktree isolation, and start mode remain visible for review.",
|
||||
"duration_seconds": 7.32,
|
||||
"duration_seconds": 6.8,
|
||||
"dimensions": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
@@ -38,20 +43,27 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 177911,
|
||||
"bytes": 122862,
|
||||
"codec": "vp9",
|
||||
"sha256": "23eeb2b82befb302fb24656988296b1d5be4626b4e3468dc838bc6c00db65759"
|
||||
"sha256": "811a46756e7010ef7a782d53b4026600f963442be967982d84a63c4c33197b4e"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 210157,
|
||||
"bytes": 230793,
|
||||
"codec": "h264",
|
||||
"sha256": "f09e8220b897e06fef898921c85487678d9ce66a3623d5e669dfc886a922acbe"
|
||||
"sha256": "f12c351cd0aa874d1aa32d47417b5608448b33a1eca11c2c66fff352cf94375c"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 8824,
|
||||
"bytes": 10456,
|
||||
"codec": "webp",
|
||||
"sha256": "1b6c41a8cbc83b7bb5645771330eaee0d4c5bf4bfc4fa2560e57f4beed6eb20e"
|
||||
"sha256": "23c8a463000d6d332a5d4f97072f1113aa10f9e10d33d5501c4303921d68049f"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture",
|
||||
"loop_frame": "matched focused crop",
|
||||
"framing": "task composer only; host Files toolbar excluded"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -62,7 +74,7 @@
|
||||
"section": "Start a task"
|
||||
},
|
||||
"accessible_caption": "A prepared task starts its selected agent in the workbench and reaches a completed response.",
|
||||
"duration_seconds": 12.8,
|
||||
"duration_seconds": 8.8,
|
||||
"dimensions": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
@@ -76,20 +88,27 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 188571,
|
||||
"bytes": 74553,
|
||||
"codec": "vp9",
|
||||
"sha256": "dd5dd5728d4855d2311d684a6c2c11263eebdde3d39879fde56a2c7d33b7c578"
|
||||
"sha256": "127e1e10171e03c61f93b9d17c82e0e2fa679635d3ea28d77002ebebaefda6af"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 196034,
|
||||
"bytes": 139877,
|
||||
"codec": "h264",
|
||||
"sha256": "d88bbcf4b17f00eeac26eba4ac68aa69838178f4b1aecacd9f8974da932e27c6"
|
||||
"sha256": "44c6b20ac3b9633429fa6b46d8b40b14450ba433609d1152ca4dc237e8ac92a8"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 10634,
|
||||
"bytes": 3270,
|
||||
"codec": "webp",
|
||||
"sha256": "627571d40aeba843e88e45ff187b84592c6f243717cf8de4497dfc5b9bc0a000"
|
||||
"sha256": "72f064f427e22c3fd51412ed8ce698741242f1f992909121ed5a37188ad84473"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture",
|
||||
"loop_frame": "matched focused crop",
|
||||
"framing": "agent workbench only; host Files toolbar excluded"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -100,7 +119,7 @@
|
||||
"section": "Use the task plan"
|
||||
},
|
||||
"accessible_caption": "A plan step receives human feedback before the approved plan moves into implementation.",
|
||||
"duration_seconds": 13.6,
|
||||
"duration_seconds": 8.64,
|
||||
"dimensions": {
|
||||
"width": 960,
|
||||
"height": 600
|
||||
@@ -114,20 +133,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 403001,
|
||||
"bytes": 238729,
|
||||
"codec": "vp9",
|
||||
"sha256": "afb8d8b0f7f8cfeeafbdd3ed969156fd08e777817678ac7fc3cf9acd197a09e9"
|
||||
"sha256": "bacdb7c6ae1ae3cfa9fdfb7a0004a0c5ae324c371914cf75090b9af95e694451"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 433605,
|
||||
"bytes": 279788,
|
||||
"codec": "h264",
|
||||
"sha256": "562ce14bfeb2f792090ef9f2b36121a68c4f45335189ba27740c4a373ee5a649"
|
||||
"sha256": "bea9b5de1ae656b4a8e89816597c3adc79b10f72d9654661ad189ff9e59b5784"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 19520,
|
||||
"bytes": 12920,
|
||||
"codec": "webp",
|
||||
"sha256": "746692a71ad3040090b22eb486bb734a9167893e022984f5939fba5d0fa0cdce"
|
||||
"sha256": "98ddaa049f6c1412bb3dba97caa059e6a7618007d3781ef14bd2ef7db4f71eea"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -152,20 +176,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 241631,
|
||||
"bytes": 271939,
|
||||
"codec": "vp9",
|
||||
"sha256": "66838456749da7ae1e2811e2cb6ce4199c0ed03ed0cedb3cd4f338e3f6c2a43e"
|
||||
"sha256": "4220345bfbbd19df834d514b29f36aa48706ca5c77a54e51729057c64d275755"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 256388,
|
||||
"bytes": 288579,
|
||||
"codec": "h264",
|
||||
"sha256": "fd9b9813c007139db961ba0db167bbd37a139d8f593644ba741f0b4a00cbf14d"
|
||||
"sha256": "5845f1aea8fc8bf0c1011e3d34e4cca32f0eac9bd00436550659b623ab3b1358"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 11880,
|
||||
"bytes": 10328,
|
||||
"codec": "webp",
|
||||
"sha256": "41e4c395d8879f6a04a2d20515c738b35b0274eca16ec3581c4ea24c359242cf"
|
||||
"sha256": "f50de94baed66df81ad7dfe0dbae5cf9faf98069e15e442864b0c0bbbb4358c6"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -190,20 +219,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 434285,
|
||||
"bytes": 553107,
|
||||
"codec": "vp9",
|
||||
"sha256": "1c5da30e038a17bb7c3d625460e76db4f31f2bd2a1b146cf2db351f2a8d85c09"
|
||||
"sha256": "8112ea21664cb97d160d72e48ed13d26cbb8d890080d46f8405059582a9858af"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 574058,
|
||||
"bytes": 860171,
|
||||
"codec": "h264",
|
||||
"sha256": "fd7607a6292833c4484f3762672b7786c421e5983d9bf4c4c3485544bc98ba14"
|
||||
"sha256": "781dc368611571a9760c14f5178badb3b7e5d2ca2f5a5a5b4cca12ce3c630fdd"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 23770,
|
||||
"bytes": 17460,
|
||||
"codec": "webp",
|
||||
"sha256": "0b2ec3cc6cde93cd596af5ce0578eb2387b6131315271d6d7aeffef916b99396"
|
||||
"sha256": "3ea895b1cf74e2ec6402f1e3015ebb5d40ad94797b088239eb075dd4e2b746a7"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -228,20 +262,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 492774,
|
||||
"bytes": 489615,
|
||||
"codec": "vp9",
|
||||
"sha256": "6d5ce2d8e0863930ad89837f321bb9f8b62704d2f1424ac02dd4c3860087761e"
|
||||
"sha256": "2d261513c1cd9f9ba94b4e485ec0fe9f51d07dcf9761b29852bfa9d61a0a3866"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 558064,
|
||||
"bytes": 516332,
|
||||
"codec": "h264",
|
||||
"sha256": "aaab819933f8cbba536e3352d465e715dafe132dabb64c39d313fa81d99ca9c1"
|
||||
"sha256": "bc2cc9fcd160b7cd681c0a1028be37f3efce11ecc462272dfccf33ad103e30d8"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 23902,
|
||||
"bytes": 19932,
|
||||
"codec": "webp",
|
||||
"sha256": "df9742ce5c149ccd07f47ae46964c25234c83d97da596305b9300a07ca0d279f"
|
||||
"sha256": "70936f9e92354d8dda73e2014e81f21ad23e84721d989778e168cc44a843b004"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -266,20 +305,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 193080,
|
||||
"bytes": 221944,
|
||||
"codec": "vp9",
|
||||
"sha256": "f794b38e9afb250be1e7e43badfe4d714e68d3a8aca518ec275599058d7723c2"
|
||||
"sha256": "245a79f4ec696350c57303a74c6a1185a0fe89f382857000d0d0ee93827ded58"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 256392,
|
||||
"bytes": 301143,
|
||||
"codec": "h264",
|
||||
"sha256": "662efd32fc281a98386a29d0ca32c8474eb1c7b6416a71ec8fdbd915e414755e"
|
||||
"sha256": "41b31239432b1018aa1aca9b1543e7bab8a294e3d3931e56845f2a8801ded95b"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 9104,
|
||||
"bytes": 7932,
|
||||
"codec": "webp",
|
||||
"sha256": "bb800f6a806088afa3e2e632fa279f80023a2e2302e3595dc97a6c41f63ea1fc"
|
||||
"sha256": "a4c3cab2425b83c12051efcba0f59dff6ffaebd2f134423d85d637d0e02704d2"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -304,20 +348,25 @@
|
||||
"data_isolation": "Disposable worker-scoped Kandev E2E instance with a fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree root, ports, and browser profile. External providers and agent behavior are mock-only; no shared user database, existing task workspace, credential, or production service is used.",
|
||||
"files": {
|
||||
"webm": {
|
||||
"bytes": 424609,
|
||||
"bytes": 442820,
|
||||
"codec": "vp9",
|
||||
"sha256": "5b91ed825e6db5d11a052652c73dbbd337a074ea28471fa3752780a6fa38f2cb"
|
||||
"sha256": "a20fe293cc217c97c02320769448f6d70b7e1f79d5bcf579774ac9660bb5edc6"
|
||||
},
|
||||
"mp4": {
|
||||
"bytes": 480537,
|
||||
"bytes": 449247,
|
||||
"codec": "h264",
|
||||
"sha256": "56be990c64ebec5e0128ed6f9a96ec69aab5425f4d6ce690da303f9e8bdc5a91"
|
||||
"sha256": "7510585c1c89c10e2437a21dc405aef18192f58716b61bfb2c8d9115533fadf6"
|
||||
},
|
||||
"poster": {
|
||||
"bytes": 13312,
|
||||
"bytes": 9710,
|
||||
"codec": "webp",
|
||||
"sha256": "6a2748d4e26d558ba92142d7ba457b222d09746ce4bb1564cd29a1401ae0bfb8"
|
||||
"sha256": "e39eab0347596152e08b7537ffb09b5b04171aab776d965d6626e3c26b029c2e"
|
||||
}
|
||||
},
|
||||
"camera": {
|
||||
"max_zoom": 1.5,
|
||||
"pointer_containment": "validated at every encoded frame",
|
||||
"source_frame": "full 3840x2400 capture"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -21,7 +21,10 @@ A task is the unit of work. A workflow is the ordered process that task follows.
|
||||
|
||||
Workflow position and runtime state are different. Moving a card changes its workflow step; it does not prove that an agent ran, code was committed, review passed, or a pull request merged.
|
||||
|
||||
> **Office status:** Office is in progress and feature-flagged. Its persistent agents, arbitrary-depth task trees, blocker and label properties, named task documents, reviewer/approver quorum, routines, and budgets are not stable features of the regular Kanban task experience.
|
||||
## Office status
|
||||
|
||||
> [!EXPERIMENTAL]
|
||||
> Office is in progress and feature-flagged. Its persistent agents, arbitrary-depth task trees, blocker and label properties, named task documents, reviewer/approver quorum, routines, and budgets are not stable features of the regular Kanban task experience.
|
||||
|
||||
## Prepare a workspace
|
||||
|
||||
|
||||
@@ -803,6 +803,14 @@ function assertFrontmatter(file, markdown) {
|
||||
`${file} must start with YAML frontmatter containing title and description`,
|
||||
);
|
||||
}
|
||||
|
||||
const statusMatch = block.match(/^status:\s*(.*?)\s*$/m);
|
||||
if (statusMatch) {
|
||||
const status = statusMatch[1].replace(/^(["'])(.*)\1$/, "$2");
|
||||
if (status !== "experimental") {
|
||||
throw new Error(`${file} has unsupported page status: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -291,6 +291,37 @@ description: y
|
||||
await assert.doesNotReject(validatePublicDocs(dir));
|
||||
});
|
||||
|
||||
test("rejects unsupported page status frontmatter", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": validPage.replace(
|
||||
'description: "Start using Kandev."',
|
||||
'description: "Start using Kandev."\nstatus: beta',
|
||||
),
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
validatePublicDocs(dir),
|
||||
/index.md has unsupported page status: beta/,
|
||||
);
|
||||
});
|
||||
|
||||
test("accepts experimental page status frontmatter", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": validPage.replace(
|
||||
'description: "Start using Kandev."',
|
||||
'description: "Start using Kandev."\nstatus: experimental',
|
||||
),
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.doesNotReject(validatePublicDocs(dir));
|
||||
});
|
||||
|
||||
test("rejects published pages without title and description frontmatter", async () => {
|
||||
const dir = await createDocs(
|
||||
{ "index.md": "# Kandev\n" },
|
||||
|
||||