Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/sstack-demo
1 change: 1 addition & 0 deletions .claude/skills/sstack-demo
2 changes: 2 additions & 0 deletions .github/workflows/sstack-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ jobs:
cache: pip
cache-dependency-path: tools/sstack/requirements-dev.txt
- run: python -m pip install -r tools/sstack/requirements-dev.txt
- run: python -m playwright install --with-deps chromium
- name: Verify affected projects
env:
SSTACK_REQUIRE_BROWSER: '1'
BASE_REVISION: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
if [[ -z "$BASE_REVISION" || "$BASE_REVISION" =~ ^0+$ ]]; then
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Use /sstack plus a project goal for PM delivery. Read tools/sstack/skills/sstack
Resolve the target project, its instructions and configured PM board before posting.
Do not default to a previous customer, board, environment or credential.

Use /sstack-demo for demos. Demo work stays on demo/* branches, never merges to
the default branch and is never ticket completion evidence. It follows that
skill's own checks instead of delivery review and gates.

Use independent engineer/reviewer agents for meaningful changes. Parallel writers
need isolated Git worktrees and nonconflicting live resources. Routine scoped
validation, commits, pushes and PRs proceed within the authorized task. Respect
Expand Down
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

@AGENTS.md

Use /sstack for PM goals. Supporting skills: /sstack-engineer, /sstack-mode,
and /sstack-verify. Canonical files live in tools/sstack/skills.
Use /sstack for PM goals and /sstack-demo for demos. Supporting skills:
/sstack-engineer, /sstack-mode, and /sstack-verify. Canonical files live in
tools/sstack/skills.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ their tools, credentials, conversation history and running agents remain separat
| Skill | Purpose |
| --- | --- |
| `sstack` | PM intake, Jira breakdown, dispatch and delivery coordination |
| `sstack-demo` | Scripted, honestly labeled demos and their promotion to tickets |
| `sstack-engineer` | Implement a scoped ticket and return tested PR evidence |
| `sstack-mode` | Research, design, prototypes and direct project work |
| `sstack-verify` | Validate behavior and maintain project verification recipes |
Expand Down
7 changes: 4 additions & 3 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
config = importlib.util.module_from_spec(CONFIG_SPEC)
CONFIG_SPEC.loader.exec_module(config)

SKILLS = ("sstack", "sstack-engineer", "sstack-mode", "sstack-verify")
SKILLS = ("sstack", "sstack-demo", "sstack-engineer", "sstack-mode", "sstack-verify")
START = "<!-- sstack:begin -->"
END = "<!-- sstack:end -->"
IGNORE_BLOCK = """# sstack generated files
Expand All @@ -25,8 +25,9 @@
"""
BLOCK = f"""{START}
Use /sstack for PM goals. Read tools/sstack/skills/sstack/SKILL.md and resolve
project scope and the configured PM board before dispatch. Supporting skills:
sstack-engineer, sstack-mode, sstack-verify. Canonical skills: tools/sstack/skills/.
project scope and the configured PM board before dispatch. Use /sstack-demo for
demos. Supporting skills: sstack-engineer, sstack-mode, sstack-verify.
Canonical skills: tools/sstack/skills/.
Configure a board with python tools/sstack/control.py board set URL --project-key KEY.
Run python tools/sstack/control.py board show before starting a new goal.
{END}
Expand Down
8 changes: 8 additions & 0 deletions tools/sstack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ time-dependent or shared-state checks. See the
[delivery rules](skills/sstack/references/delivery.md#choose-the-shortest-sufficient-path)
for required risk escalation and independent review.

## UI evaluation

`ui_eval.py run` drives a JSON UI script in Chromium against one approved origin,
saving per-step screenshots, errors and captured values; `ui_eval.py review` binds
an agent's visual verdicts to those screenshots. See
[scripted UI evaluation](skills/sstack-verify/references/ui-eval.md). Playwright
is a development dependency; CI installs Chromium and requires the browser tests.

## Project coverage and evidence

`projects.json` registers owned paths, offline suites, external acceptance recipes
Expand Down
2 changes: 1 addition & 1 deletion tools/sstack/check_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import yaml

ROOT = Path(__file__).resolve().parents[2]
SKILLS = ("sstack", "sstack-engineer", "sstack-mode", "sstack-verify")
SKILLS = ("sstack", "sstack-demo", "sstack-engineer", "sstack-mode", "sstack-verify")
STACK = Path("tools/sstack")
FENCE = re.compile(r"^ {0,3}(`{3,}|~{3,})")
# Inline destinations, including angle brackets, an optional title and one
Expand Down
1 change: 1 addition & 0 deletions tools/sstack/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ruff==0.12.12
PyYAML==6.0.2
playwright==1.56.0
88 changes: 88 additions & 0 deletions tools/sstack/skills/sstack-demo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
name: sstack-demo
description: Build a presentable demo quickly from an agreed script, with scripted self-checks and an honest faked list. Use for demos and stakeholder previews, not for tickets, PRs, delivery, or technical prototypes.
---

# SStack demo

A demo presents an agreed story to an audience reliably and honestly. It is not
delivery: skip Jira, the delivery controller, frozen eval plans, independent code
review, `verify changed`, CI and merge; this loop's checks replace them. Delivery
requests go to `sstack`; do not load its delivery or Jira references here. To
settle a technical uncertainty, use the `sstack-mode` Prototype workflow instead.
Root AGENTS.md secret, customer-data and approval rules still apply.

## Boundaries

- Work on a `demo/<name>` branch with the script and assets under `demos/<name>/`.
The branch never merges and is never completion evidence for a ticket.
- Faked steps are labeled disposable prototypes under engineering rule 1 in
[engineering principles](../sstack-mode/references/engineering.md).
- Use synthetic data only. Read
[platform](../sstack-verify/references/platform.md) before live Kizen
interaction. Record writes go only to a sandbox the user approved for this demo;
any Kizen configuration change follows platform.md's dry-run and approval rules.
Without an approved sandbox, fake the step or ask for access.
- Never present a step as real without proof that it ran against the real system.

## 1. Script before building

Write `demos/<name>/script.md` with the audience, time budget, and 3–7 steps:

```markdown
| # | Action | Visible result | Real or faked | Proof or fake detail |
| --- | --- | --- | --- | --- |
| 1 | Enter $6,000 stocks and $4,000 bonds | Chart shows 60% / 40% | Real | Sandbox record readback |
| 2 | Refresh prices | Prices update | Faked | Hardcoded price fixture |
```

Confirm the script with the requester when intent is unclear; otherwise start.
Script changes from stakeholder feedback are expected, not violations.

## 2. Build and self-check

Reuse existing app code, templates and seed data before writing new ones. Start from
a clean app state with a fresh run marker; leave earlier sandbox records in place
rather than deleting them. Encode the script as a
[UI script](../sstack-verify/references/ui-eval.md) and run `ui_eval.py run`
against the approved origin; add `--video` for a recording. It checks each step,
saves a screenshot per step, and fails on console errors or failed requests.

Then do the visual check: view every screenshot, with computer use or by opening
the image, against the script's visible result and record verdicts with
`ui_eval.py review`. Fix and rerun until the review passes.

## 3. Honesty check

- Each real step has evidence it ran: for Kizen, read back the record it wrote,
matched by a marker unique to this run. A step without proof is relabeled faked.
- Every mock, stub or hardcoded response in the branch's diff from the default
branch appears in the faked column, and every faked entry exists in the code.
- No real customer data, credentials or unapproved writes.

## 4. Rehearse

Run `ui_eval.py run` twice more with fresh markers within the time budget. Both runs
must pass automated checks and visual review. For a customer-facing demo, optionally
ask a fresh agent to review only the recording and script: does it tell the story,
and what would confuse the audience?

Report the branch, URL or launch command, recording or screenshots, rehearsal
results and the faked list. Stakeholder feedback returns to step 1.

## 5. Promote or archive

When a stakeholder approves, hand off to `sstack` to promote the demo into tickets:

- Each real step becomes an acceptance ID with an eval case: its input, expected
business values (for example `{"stocks_pct": 60}`, not the displayed text),
phase `premerge` or `postmerge`, and surface `local`, `live` or `ui` for the
proof delivery will collect.
- Each faked step becomes scope with its own acceptance criteria and eval cases.
- Link the script and recording as the agreed user journey. They are references,
not result evidence; design still follows the `sstack` Kizen-first principles.
- The engineer builds to delivery standards and may borrow demo code, but must
replace every faked-list item with a real path.

Archive a rejected demo by pushing its branch unchanged, so the script and
recording links stay valid. Nothing enters Jira.
3 changes: 2 additions & 1 deletion tools/sstack/skills/sstack-mode/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ gate with `control.py verify changed --base <actual-base> --jobs 2`; avoid
duplicating lint and tests that the selected suites already include.

For a project goal/backlog, use `sstack`; for a dispatched issue use
`sstack-engineer`. Ordinary questions and small direct edits need no new hierarchy.
`sstack-engineer`; for a demo use `sstack-demo`. Ordinary questions and small
direct edits need no new hierarchy.
Read skills at `tools/sstack/skills/<name>/SKILL.md` when not auto-discovered.

1. Restate the user outcome and observable acceptance. Read affected source and
Expand Down
3 changes: 2 additions & 1 deletion tools/sstack/skills/sstack-verify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ around existing suites does not establish their completeness.
Reproduce the actual user input/action. Assert observable values/writes/no-writes
for pure logic. For platform work inspect current state, exact execution and child
runs, and the corresponding persisted business result. For UI work drive the
documented user path with available browser tools and inspect rendered output.
documented user path with [scripted UI evaluation](references/ui-eval.md) or
available browser tools, and view every rendered step, not only its assertions.
Capture the trigger and outcome, not just a final screen. Use synthetic records.

Separate local, live Kizen, UI, and untested paths. A build is not live proof;
Expand Down
75 changes: 75 additions & 0 deletions tools/sstack/skills/sstack-verify/references/ui-eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Scripted UI evaluation

`tools/sstack/ui_eval.py` drives a JSON UI script in Chromium, screenshots every
step, and records console errors, page errors, failed requests and captured
values in one `report.json`. An agent then views each screenshot and records a
visual verdict. It is built for web UIs such as the Kizen UI but has not yet been
exercised against a live Kizen login (SSO, MFA, session expiry). It does not call
the Kizen API or prove that a record was persisted.

Install `tools/sstack/requirements-dev.txt` (includes Playwright) and, where no
Chromium is preinstalled, run `python -m playwright install chromium`.

## Script

```json
{"schema_version": 1, "base_url": "https://sandbox.example.test",
"viewport": {"width": 1440, "height": 900}, "ignore_console": ["^Known noise$"],
"steps": [
{"id": "open", "action": "goto", "path": "/records"},
{"action": "fill", "selector": "role=textbox[name=\"Name\"]", "value": "Demo {run_marker}"},
{"id": "save", "action": "click", "selector": "role=button[name=\"Save\"]"},
{"action": "expect_text", "selector": "role=main", "text": "Demo {run_marker}"},
{"id": "chart", "action": "capture", "selector": "#allocation", "case": "mixed-holdings",
"key": "stocks_pct", "as": "number"}
]}
```

Actions: `goto` (path relative to `base_url`), `click`, `fill`, `press`,
`wait_for`, `expect_text` and `capture`. Selectors use Playwright syntax; prefer
role and visible-text selectors over generated class names. `{run_marker}` is
replaced by a value unique to each run, so readback can match this run's records.
The first failed step stops the run; later steps are reported as skipped.
`capture` with `"as": "number"` needs exactly one standalone number in the text;
ranges, dates and numbers with attached units such as `5px` are rejected.
`ignore_console` patterns only suppress known console noise; they never hide page
errors or failed requests. Failed requests are network failures and 5xx responses
on the approved origin, plus 4xx responses for its top-level pages.

## Run and review

```sh
python tools/sstack/ui_eval.py run SCRIPT.json --approved-origin https://sandbox.example.test \
--storage-state /outside/repo/ui-state.json [--video] [--headed]
python tools/sstack/ui_eval.py review artifacts/sstack/ui/<run>/report.json VERDICTS.json
```

- `--approved-origin` is the human-approved UI host. The script's `base_url` must
match it and `goto` paths must stay under it. Top-level navigation elsewhere,
including links, server redirects at any hop, delayed scripts and popups, is
aborted before the request is sent and fails the run. The tool fetches top-level
pages itself and turns each same-origin redirect into a new checked navigation;
chains over 20 hops and 307/308 redirects of form posts fail. The run waits
briefly after the last step to catch late navigation. A login redirect to another
host fails the run, so start from a saved session. UI actions can write data, so
the origin needs the same approval as any other write there; for Kizen follow
[platform](platform.md) and use the confirmed UI host, which may differ from the
API host.
- `--storage-state` is a saved login session and must live outside the repository.
Create one with `npx playwright codegen --save-storage=<path> <ui-host>` and log
in as the intended role. Never commit or share it.
- Visual review: view every `step-NN-<id>.png` yourself, with computer use or by
opening the image, and compare it to the step's expected visible result. Check
layout, labels, values and error states that the automated checks cannot see.
Write `{"reviewer": "<id>", "steps": {"<step id>": {"verdict": "pass", "note":
"..."}}}` covering every screenshotted step, then run `review`. It fails on any
`fail` verdict or failed automated check, blocks on missing verdicts, and refuses
screenshots changed since the run. Verdicts are the reviewer's declarations; the
tool binds them to the screenshot hashes but cannot authenticate them.

`report.json` `eval_cases` already has the result-case shape (`id`, `observed`,
`evidence`) for `surface: "ui"` cases in the
[evaluation contract](../../sstack/references/jira-handoff.md#pm-owned-evaluation-sets).
Rendered values are UI proof only; persisted Kizen results still need readback.
Recorded URLs drop query strings and fragments. Reports and screenshots can still
show record data; use synthetic records and inspect them before sharing.
3 changes: 3 additions & 0 deletions tools/sstack/skills/sstack/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ description: Break requested project goals into Jira work, design with Kizen fir

# SStack PM

Demo requests go to `sstack-demo` instead; promote an approved demo using its
step 5.

Read [Jira handoff](references/jira-handoff.md) and
[delivery gates](references/delivery.md). Follow root AGENTS.md for shared
preferences and host switching. Use delivery.md's shortest sufficient path: one
Expand Down
2 changes: 1 addition & 1 deletion tools/sstack/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def install(self, **kwargs):

def test_install_preserves_instructions_and_rerun_is_noop(self):
result = self.install(board_url="https://boards.test/projects/PM", project_key="PM")
self.assertEqual(len(result["links"]), 8)
self.assertEqual(len(result["links"]), 2 * len(installer.SKILLS))
self.assertTrue((self.target / ".claude/skills/sstack/SKILL.md").is_file())
self.assertTrue((self.target / "AGENTS.md").read_text().startswith("# User instructions\n"))
self.assertFalse((self.target / "tools/sstack/untracked-secret.txt").exists())
Expand Down
Loading
Loading