Releases: enloop-md/enloop
Release list
Enloop v0.17.0
Extension 0.17.0 · plugin 0.20.1 · grammar 0.0.13 · daemon 0.1.0
Running cases
- Withdraw a question. A question sent by mistake — the wrong text
pasted, the wrong step asked from — no longer has to be answered.
Withdraw sits beside the waiting line for as long as an answer is
owed; it writes awithdrawnflag into the question's directory, the
daemon stops its backend within seconds (the CLI child is killed, the
API call cancelled) and writes nothing, and a serve pass skips the
question or drops the answer it was about to write. The card folds to
Withdrawn with the text still readable; an answer that lands anyway
stays folded under it. Additive on protocol 1. - Skip to this step. A run restarted after a dirty one no longer has
to be clicked through: open any step ahead and ⤵ Skip to this step
marks every undecided step before it in one go. Those steps carry a
jumped over badge andjumpedOver: trueinrun.json; the report
says so per step, andfeedback.mdleaves them out of Steps the tester
skipped — a jump is not a vote against the steps under it, and the
check skill is told as much. A verdict given later clears the mark. - Screenshot tools hidden on test runs. A guide run shows them as
before; a test run no longer has capture buttons, the delayed capture
and unfilled photo slots under every step. Show 📷 in the run's
status bar, the box in front of Start, or Screenshots during runs in
Settings turns them on — one extension-wide setting. Pictures already
taken always show, the runner's### Photospecs still fire, and the
shortcut and context-menu capture keep working. - Capture, changeable mid-run. Options in the run's status bar
unfolds the console/requests capture boxes and the screenshot-tools
box without leaving the run; the reload notice sits under it, since a
page loaded before capture was on is wrapped only from its next load.
Both servers are now pointed at the run'sconsole.jsonlwhen a
question is asked — the daemon's brief names it, the serve skill reads
it — and told that a log with requests but no console lines means the
page was never wrapped, not that it logged nothing.
Enloop v0.16.0
Extension 0.16.0 · plugin 0.20.0 · grammar 0.0.13 · daemon 0.1.0
Writing cases
- Environments before cases.
fullandquickread the project's
environments before any source, and when there are none they stop and
ask once — set them up now, or continue with local only — and never go
on without an answer. Setting up is the environment master, which
/enloop:setup environmentsalso runs on its own: local is recorded
from the repo without a question, then each further deployment from what
you paste — atshline, a command, an address, a sentence — and one
more question, the address, only when the paste did not give one. A
name likeprodmarks it production; a preview name makes it
temporary. Each environment may carry a reach, how an agent gets to
its data: Teleport (tsh), a command that exits 0 when the deployment
answers, or a sentence for a human. The validator probes a reach as
soon as it is recorded and keeps the result either way;
enloop-case.mjs reachprobes again.tsh loginis the one step left
to you, shown as the exact line to run. Nothing written is a secret — a
pasted connection URL loses its password before it is recorded — and
the panel only shows the reach; it never opens a tunnel. - A value left to the environment must exist somewhere. With the data
folder in view, the linter refuses a case whose environment-provided
variable or domain has a value in no environment of its project — that
run would have to ask — and warns, naming them, when some environments
have it and others do not. Where the value differs per deployment and
the environment has a reach, the skills record a lookup — a
read-onlyselectkept on the environment — andenloop-case.mjs lookup … --recordruns it through the tunnel and writes the answer into the
file. Production answers only with--production, and never records:
what is found there stays with the tester.
Running cases
- Temporary environments. A Shipyard or ArgoCD preview that exists
for one branch has a home: Add temporary under Settings →
Environments, or--temporary/--until YYYY-MM-DDfrom the skills.
They live inenvironments.local.jsonbesideenvironments.json,
git-ignored, and each expires — the end of today unless a date says
otherwise — after which it is gone from the picker, the screen and the
file. The picker lists one aspr-42 · until Sep 11, a production one
asprod · production; a temporary environment is never the default. A
card with a reach shows it in one read-only line, with the last probe's
verdict. - The editor opens over the page. Edit no longer opens a tab of its
own: the editor is framed into the tab under test, full size, and over
the side panel when that page cannot be scripted. A tab of its own took
the connected folder's grant with it when it closed, and the save
failed; a frame does not. Drawn shapes can now be selected, moved,
resized, recoloured and deleted after the fact, with undo and redo
over the whole history — a runner-drawn callout and a hand-drawn one
alike. - A capture that keeps the page's focus. A click on the panel takes
focus from the page, and the dropdown you opened there closes before
the picture is taken. So every capture button also fires when you
hover it with Ctrl+Shift held, and there is a delayed capture that
counts down three seconds on the button while you click back into the
page and open what should be in the picture.
Guides
- A guide comes last. The docs and the guide skill now say what
a guide is for and when to write it: it is for the end user, so it
carries less than a case — selectors, notes, test data, verdicts and
comments never reach the reader — and it is written once, on the final
version of the feature, right before the push that ships it, because
its screenshots are the UI at the moment of the run. The skill looks at
the tree first and, when UI files are still uncommitted, asks once
whether to write now or after the last change lands. export-guide
ends by saying that a change to the feature means a new run and a new
export, never an edit to the images.
Enloop v0.15.0
Extension 0.15.0 · plugin 0.19.0 · grammar 0.0.13 · daemon 0.1.0
Writing cases
Via:— the address is never the only way to a page. A step that
moves to a new page also says how it is reached from the app's own
menus (Via: Settings → Users → the row), shown under the address in
the panel, the viewer and the downloaded page. The linter requires it
on a move and acceptsVia: link onlywhen the UI genuinely has no
path — a deep link, a redirect — provided the step says where the link
comes from.enloop.md/is the in-repo data folder. The skills offer to create
<repo>/enloop.md/;enloop/,test-cases/and.enloop/are still
recognised.
Running cases
- ↗ Bring me to the tab. A question card shows the link whenever the
tab you asked from is not the one in front of you — an answer takes a
while, and you were in another tab by the time it came. It brings that
very tab forward, not a fresh copy of its address, and disappears once
you are back. Remembered for the browser session, with the page's
address as the fallback. - ⚒ Prompt to fix this. On the current step and every decided one:
one click gathers the step's text, your comments and their audiences,
the question thread, earlier findings in the run, the console and
network output captured while the step was current, and which project
the fix belongs in — the case's@projectand the repo it was authored
from — into one Markdown prompt, copied and shown. Paste it into Claude
Code in the app's repo. Works with no agent watching the folder. - Drop a case on the panel. A dashed block on the Connect screen and
at the top of the Library takes a case file — dropped, or picked with a
click — and makes it a case you can run. With no folder connected the
file lands in the browser's own storage, listed as Inbox (this
browser), so a case handed to you is one drop from a run with nothing
set up. Several files at once are fine; a file that is not a case is
refused by name. - The values you type come back next run. A value typed under Start
run — a record id, the account a bug needs, the address of a branch
deployment — is kept for the next run of that case and shown from your
last run beside the field. Only typed values: a generated one is fresh
at start, and an address that follows the open tab keeps following it.
↺ back to auto forgets it, and picking an environment that answers
for the same name drops the remembered value rather than running one
deployment against another's address. Kept in this browser, not in the
folder. - One slash between a domain and its route. An address that ends in
/— pasted from the address bar, typed into an environment card,
written as aDefault:— loses it where a route follows, so
%DOMAIN%/ordersishttps://app.test/ordersand never
https://app.test//orders, which most routers treat as a different path
and answer with a 404 mid-run. Everywhere: the panel, the run's frozen
case, the report, the viewer and a downloaded page as its values are
edited. Nothing is added —%DOMAIN%?next=/xis left as written. - Every folder says which project it is. Connected folders are listed
by the name in their ownproject.json—{ "name": "Acme Shop" }at
the data folder root — with the directory name under it, in the
reconnect list, the Library's storage picker and Settings. Four repos
that each keep their cases in anenloop.md/are no longer four
identical rows. Nobody has to write the file: on connect, and on every
refresh, a folder with no name recorded is named after the@project
its cases agree on, else the repository directory you picked, and the
answer is written back so it travels with the repo. Rename in
Settings edits the same file. A folder holding several projects' cases
keeps its directory name — the Library groups those by@project
already. - Comment audiences are back in view. The closed disclosure 0.14.0 put
over the "This comment is for" checkboxes is gone; the row shows as it
did before. - The daemon is off the menu for now. Every "no agent connected"
state points at one/enloop:servepass in Claude Code; the enloopd
daemon stays in the repo until it is fixed and debugged. - Screenshots. Every step of every run — and every free run — has
📷 Screenshot and 📷 Screenshot & edit, and the run header a
📷 for the current step or the run itself. Chrome photographs a tab
only after the extension is invoked on it, so the first picture on a
tab is Alt+Shift+S or the page's Take an Enloop screenshot
menu item; from then on the buttons and the runner work there. Site
access stays per site — nothing asks for every site. Each one is a thumbnail
under its step with a caption, ✎ Edit, ↺ Original, Move to
another step, and ✕. Edit opens the picture in its own tab: Crop,
Blur, Line, Arrow, Rect and numbered Callout in seven colours, Undo,
keys1–6,Esc,Ctrl+Z,Ctrl+Enterto save; the capture itself
is never touched, so Original is always exact. Pictures land in
screenshots/besiderun.json—01.source.pngas captured,
01.pngas edited — andreport.mdlists them under their steps. In a
free run each capture drops a%PHOTO_n%into the notes at the caret.
Same per-site grant as Highlight; on a page Enloop cannot see, the
buttons are the grant notice. - Photos the runner takes. A step's
### Photoblock says what the
picture is —Crop:the container,Mark:a box,Point:an arrow,
Callout:a numbered disc with a legend,Blur:a region, each a
selector — and the runner takes it when the step becomes current
(Take: before) or when you give the verdict (Take: after), finds
the elements on the page, draws the marks and drops the result where
the author wrote%PHOTO_1%.Mode: confirmshows it first with
Keep · Retake · Edit · Discard;Mode: autokeeps it with a
two-second toast;Take: manualleaves a 📷 Photo n button. A
selector that matches nothing is skipped and the step says n not
found; a page with no grant says Photo n not taken and the mark
still lands. - ⬇ Download guide. A finished run with at least one screenshot, any
finished run of a guide, and a finished free run with screenshots offer
one HTML file with every picture inlined: the steps in run order with
their photos, captions and callout legends, You should see where the
case had Expected, and none of the selectors, scripts, verdicts or
comments. Opens offline; mail it as it is.
Guides
@kind guide. A header line beside@projectthat says the case's
reader is an end user: the verdict buttons read Done / Could not,
the Expected block You should see, the Library shows a guide
badge, and the linter stops asking forKind: quick. Nothing else
changes — a guide is a case, run in the same panel, with the same
contract behind it. Grammar 0.0.13 also brings### Photoand the
reserved%PHOTO_n%placeholder, with linter rule10over them./enloop:guide. Writes a guide from the app's source the way
full writes a case: routes, labels and selectors read in the
session, validated with the real parser, landed withwrite— in
second person, one action per step, no internal names, with a photo
spec on every step that changes the screen. Run it in the panel and the
runner takes the pictures. A bare/enloop:guidegets the same
confirm-scope question asquickandfull./enloop:export-guide. Picks the finished run (one closed question
when there are several), and writes<data folder>/guides/<slug>/—
README.mdwithimages/,index.htmlwith the pictures inlined, or
both — through the validator's newlist-guidesandexport-guide
commands, which need nothing butnode. Fixes tester-voice sentences in
the exported file and never in the case.runs/stays git-ignored;
guides/is the deliverable.
Enloop v0.14.0
Extension 0.14.0 · plugin 0.17.0 · grammar 0.0.11 · daemon 0.1.0
The manifesto
MANIFESTO.md states the principle everything else serves: a human
verifying a flow puts in zero effort — never asked to decide, provide a
value, or look anything up. PLAN-MANIFESTO.md is the alignment plan;
this release carries its first pass.
Writing cases
-
A case is a goal.
Goal:andYou will:are header lines, one
line each and required by the linter;# You will needlists what must
be in the tester's hands before step 1. The case screen shows all three
above Start, the run screen pins the goal under its title for the whole
run, and the viewer, the downloaded page and the readable export carry
them. -
The contract is enforced. Missing
### Expected, a UI step with no
Selector:, no entry point in a case that names addresses, and
%DOMAIN%with no@locationsare errors now, not warnings. A vault
reference in a prerequisite warns: a test account's password is an
environment value typed by the panel. The shipped example passes. -
Every skill run ends with a link. The
writecommand appends the
viewer-link comment to the case and prints the link; the report gives
it first, then the two extension steps. The project name is derived
from the repo's manifest before anyone is asked. -
%DOMAIN%needs no declaration. Every app address is written
%DOMAIN%/route. The placeholder is empty by default and a run fills it
with the tab it starts from — a branch, a review app, a local server —
unless the tester types an address or picks an environment; a case
guesses no host, so a wrong guess can no longer make it unrunnable.
%BASE_URL%keeps working as an alias, and a declared## APPmain
domain from the previous convention runs unchanged.# Domainsis now
for a second host only. The skills write%DOMAIN%in new cases and
record the app under test asDOMAINinenvironments.json. -
@locations:says where a case is meant to run. A header line of
comma-separated host globs —localhost:8080, *.acme.com. It gates
nothing: every address the run screen, the online viewer and a
downloaded page build is shown green when its host fits one and red
when it fits none, and the link opens either way. TheDOMAINfield on
the case screen shows the same verdict before the run starts. The first
entry without a*is what the viewer and a downloaded copy use for
%DOMAIN%, so they stay clickable with noDefault:line. -
A value the run produces never goes in an address. A placeholder
nobody can fill —%DOMAIN%/user.php?user=%USER_ID%for a user the
case creates — is a linter error with a specific answer: say where the
tester clicks and give the address shape in backticks. The run screen no
longer offers Go on an address still holding a placeholder, and the
downloaded page does not link it. The step contract gains the rule and
the by-eye check for aDefault:invented to pass the linter.
Running cases
- A run never asks. Start is one button — the quick path when the
case marks one, Full beside it — and it stays disabled while any value
is empty, with the values block saying where each one comes from.
Comment audiences and the step rating sit under a closed disclosure;
a comment with nobody ticked is routed at triage. - Point the extension at the repo. Connecting a directory with no
test-cases/finds the case folder up to two levels down, so "install
and point it at the repo" is the whole instruction. - Simplified links. The share row gains a second link that opens the
viewer in the simplified view — no selectors, no scripts — for someone
who will follow the case by hand.
Enloop v0.13.0
Extension 0.13.0 · plugin 0.15.0 · grammar 0.0.9 · daemon 0.1.0
Writing cases
- Domains and environments. A case declares every deployment it touches
under# Domains(## APP,## ADMIN), each with aDefault:origin
and an optionalMatch:glob, and uses them as address prefixes:
Where: %APP%/admin/reports. An environment is a named set of domain
addresses and variable values — local, staging, prod — kept in
environments.jsonbeside the cases and picked before a run; pick none
and the main domain follows the open tab. The legacyBASE_URLvariable
still parses; the linter asks for it to become the main domain. - Variables are never asked of the tester. Every variable resolves
before the run from aDefault:, aGenerator:or the environment; one
with none of the three is a linter error, not a prompt. - Step groups. A case covering a broad change is written as concerns:
# Steps: Log in,# Steps: Restore password, each opening with its
goal — what its steps prove together — before the first step. Groups
head the step list in the panel with a running tally;report.mdand
feedback.mdopen with a By group summary. The linter requires the
goal and refuses an empty or duplicated group (rule 9). - Ratings feed the next case.
enloop-case.mjs ratingsaggregates
every rated case and step for a project, with the frozen step text and
the tester's comments; the authoring procedure reads it, and the check
skill treats a poorly rated step as a defect to fix. - Where and Note reach the panel. A step's
Where:address and
### Notewere parsed and frozen but dropped on the way into the run
screen. Both show now, with the Go control on the address.
Running cases
- Star ratings. Rate a step, or the whole case, one to five stars —
independent of pass or fail. Ratings land inrun.json,report.md
andfeedback.md, where four- and five-star steps are listed as the
shape to write in and one- and two-star steps as the shape to avoid. - Comments, faster. The audience row is condensed to names, with a
What do these mean? toggle for the legend that remembers its state.
Add comment lights up the moment there is text and clears the ticks
after. A one-tap Combine with previous step chip adds the standard
note to the test writer; the check skill merges the two steps in the
next version. - Comments for all steps. A finished run shows its feedback text —
every comment, rating and failure, grouped by audience — with Copy
and Download .md, so a tester with no agent on their machine can
hand the run to someone who has one. The check skill accepts that file
pasted in place of a run folder. - The agent says what it is doing. While a question is being
answered, the panel shows the server's own status line — Reading
ResetForm.tsx, Found it — the step names a renamed button, Writing
the answer — and how long ago it changed, instead of one unchanging
"working on the answer". The serve skill writesprogress.jsonas it
goes; the daemon reports every file the model opens and asks the model
to narrate through aprogresstool, and drives headless Claude Code
with streamed output so its tool calls are read live.
Plugin and daemon
- The plugin's
setupskill writes environments, the authoring skills
read this project's ratings, andbrieflists rule 9. - The daemon stamps its version into its watcher file and warns once per
folder when the extension's heartbeat speaks a different channel
protocol.
🤖 Generated with Claude Code
Enloop v0.5.9
Capture, asked in front of the run
The Capture console output and Capture failed requests checkboxes now sit directly above Start run on a case screen, and at the top of a free run. They were previously only in Settings — a screen away from the run they apply to, and away from the moment anyone actually thinks "I should keep the console for this".
They are the same setting in all three places, not three settings: capture is a browser-wide content-script registration, so ticking a box applies to every run from then on. The panel says so, and — while either box is on — says the other thing worth knowing: leave them off when you are not using them, because every console call and every request on a granted site then runs through a wrapper.
The "reload the page to start capturing" notice gained a ⓘ explaining why that is a technical limitation rather than a preference: Chrome installs the wrappers only at the very start of a page load, before the page's own scripts, so it cannot reach a page that has already loaded. Turning capture off needs no reload.
Two fixes came with it: pressing Reload page in Settings now clears the notice (nothing re-checked before, so it stayed up looking stuck), and a run already in progress follows the setting instead of holding its own copy.
Also in this build, from the two commits since v0.5.8: the case grammar spec now ships inside the plugin, with a linter and a validator CLI, so the authoring skills no longer need a clone of this repo.
Install
Enloop is not in the Chrome Web Store. Download enloop-0.5.9.zip, unzip it somewhere you intend to keep, then chrome://extensions → Developer mode → Load unpacked → select the unzipped folder (the one with manifest.json directly inside it).
Upgrading: replace the contents of your existing folder with this zip's, then press Reload on the Enloop card at chrome://extensions and reopen the side panel. Settings → About should read v0.5.9.
Enloop v0.5.8
Two changes since v0.5.6.
Capture the page's console during a run
The console is where the cheapest evidence of a bug lives and where it is
invisible by default — an uncaught TypeError behind a button that appears to
do nothing, a 401 logged by a fetch wrapper. Settings → Capture during runs
now makes it part of the run, with two independent toggles, both off by
default: console output, and failed requests (method, URL, status, duration —
never headers, never bodies, query strings redacted). Off by default because
console text carries tokens and customer data, and runs are written to a folder
people commit.
Turning capture on needs a page reload; turning it off does not. The wrapper
has to be installed before any page script runs, or it misses the load — usually
the interesting part — and Chrome can only guarantee that from the next
navigation. The panel says so and offers a Reload page button. Capture
covers the sites you have already granted Enloop access to, and no others.
A run folder gains console.jsonl (the record, appended live) and console.md
(the same thing rendered for a person at finish, grouped by step), and
run.json gains per-step counts. Whether any of it reaches an agent is a
second, separate decision made in the finish bar — what gets attached is a
deduplicated digest, not the raw log.
Shared links go in the fragment, compressed
A viewer link carried the case in ?c=, so every shared case travelled to
GitHub Pages on its way to the reader. Nothing was ever uploaded — the page has
always decoded on the reader's own device — but the host still saw the bytes go
past, and cases name internal URLs and staging logins. The case now rides in the
fragment, which browsers never send to a server.
It is also deflate-compressed, which brings a case link down to a third to a
half of its old length: a long case that had to be pasted now fits in a link.
Links written before this still open.
Install: download enloop-0.5.8.zip, unzip it somewhere you intend to
keep, then chrome://extensions → Developer mode → Load unpacked → select
the unzipped folder, the one with manifest.json directly inside it.
Upgrading: replace that folder's contents with the new zip's, then press
Reload on the Enloop card. A new build alone does not refresh an already
loaded extension.
Enloop v0.5.6
First release since v0.3.0 — 22 commits.
Connect several folders at once
The Library lists cases from every connected folder together, with a filter to
narrow to one, so a folder can live inside the app repo it tests and be
committed with the code. Enloop writes a .gitignore there so run history
stays local. Settings → Storages manages them, and a folder whose Chrome
permission lapsed degrades to a banner rather than emptying the Library.
Share a case with someone who does not have the extension
Share vN on the case screen: four exports — Markdown or HTML, full or
simplified — plus Copy link. The HTML export is one self-contained file
with steps you can tick off, values you can copy and variables you can fill,
working offline with no extension installed.
Every case file the panel writes now ends with a comment carrying its own
viewer link, so someone handed the raw .md can open it in a browser.
The online viewer
https://enloop-md.github.io/enloop/ reads a case out of a link — the case
travels inside the URL, so nothing is uploaded and there is no server behind
the page. Drop a .md file on it to open one, or build a case from a form
without an agent at all.
A bundled example that tours the whole panel
Thirteen steps covering Go, Highlight, typed values, an automated step, the
quick/full split, notes and tasks, the run comment, the exports and the
storages list. Load it from an empty Library.
Also
An icon, at last. And the skills — installed separately — gained a Codex
build, split authoring into /enloop:quick and /enloop:full, and now
resolve the data folder per repo instead of assuming one for every project.
Install: download enloop-0.5.6.zip, unzip it somewhere you intend to
keep, then chrome://extensions → Developer mode → Load unpacked → select
the unzipped folder, the one with manifest.json directly inside it.
Upgrading: replace that folder's contents with the new zip's, then press
Reload on the Enloop card. A new build alone does not refresh an already
loaded extension.
Enloop v0.3.0
First packaged build. Chrome extension, side panel, cases as Markdown in a folder you pick.
Install
- Download
enloop-0.3.0.zipbelow and unzip it somewhere you intend to keep — Chrome loads an unpacked extension from that path on every start, so moving or deleting the folder disables it. - Open
chrome://extensionsand switch on Developer mode (top right). - Load unpacked → select the unzipped folder, the one with
manifest.jsondirectly inside it. - Open the side panel from the toolbar icon, and connect a folder for your cases.
Installing asks for no site permissions. Access to a page is requested per site, once, at the moment a step first needs it.
In this build
- Optional, per-origin site access instead of
<all_urls>at install time. - "Blocked" is told apart from "not found": Chrome's own pages, the Web Store and ungranted sites each say what they are, and an ungranted site carries the grant.
- An empty folder offers a runnable example case.
- The panel remembers its screen across closes, and the Library resumes a run left open.
- Errors say what happened and what to do; a folder-access failure carries a Reconnect button.
- The Library groups by
@project. - A case can be downloaded as its Markdown, or simplified for a person to read.
Cases are written by the Claude Code skills — see the README for the loop.