Repository navigation
Releases: Confidee/flowreel
Release list
flowreel 0.6.0
Install: npm install --save-dev @confidee/flowreel@0.6.0 (npm, published by the release workflow with a provenance attestation).
Narration is now off unless you turn it on. flowreel's built-in voices are
the macOS system voices, which Apple licenses for personal, non-commercial
use only, so no video is narrated with one by default, and a run that uses
one says so. The demo video is re-voiced with Kokoro (Apache-2.0). Also:
the plugin is ready for Anthropic's plugin directory, and the README records
the Agent Skill's run in Claude Code without the plugin.
Added
- The plugin has an icon (
plugin/.claude-plugin/icon.png, the brand
avatar at 1024x1024) and thedocumentationUrlandsupportUrlthat
Anthropic's plugin directory shows. Its README says where it works:
everything in Claude Code; in claude.ai chat it can explain and plan, but
not run anything. MACOS_VOICE_NOTICEfrom@confidee/flowreel/voice: Apple's terms for
its system voices, which every narration line names when a macOS voice
spoke.
Changed
- BREAKING: narration is off unless you turn it on, for every run
(--narration onorFLOWREEL_NARRATION=on). Before, a tour at demo
pace was narrated by default. flowreel's only built-in voices are the
macOS system voices, and Apple licenses them for personal, non-commercial
use only (macOS licence, section 2F): a work, client or public video is
outside that licence, so no video is narrated with one unless someone
chooses to. A run narrated with a macOS voice now says so in its log and
insummary.md. The docs explain the terms and the alternatives
("Voices and licensing" indocs/narration-and-videos.md); the doctor's
check, the narrate and run skills and the example say the same. - The demo video (
assets/demo/flowreel-demo.mp4, linked from the README
and the npm page) is re-voiced with Kokoro (Apache-2.0): the same lines
at the same moments, the picture unchanged. Its first version used the
macOS voice Ava. The README now shows the demo's thumbnail and links to
the file.
flowreel 0.5.0
Install: npm install --save-dev @confidee/flowreel@0.5.0 (npm, published by the release workflow with a provenance attestation).
flowreel is now also a standard Agent Skill: one folder, skills/flowreel/,
that Cursor, GitHub Copilot in VS Code, Claude Code and other agents that
read the Agent Skills format can load. The Claude Code plugin and the npm
package work as before; the README's "Use with any Agent Skills agent" says
how to install the skill.
Added
skills/flowreel/: flowreel as a standard
Agent Skill, for Cursor, GitHub Copilot in
VS Code and any other agent that reads the format, besides Claude Code.
It is oneSKILL.md, written to the specification (only its six
frontmatter fields; passes its reference validator at commit69ef37e),
that routes to the plugin's workflows. Itsreferences/andscripts/
are generated fromplugin/bynpm run sync:agent-skill, and a
runtime test fails when they are stale orSKILL.mdbreaks the
specification (a strict subset of YAML, so it never passes what the
reference validator rejects). What the plugin's frontmatter enforced and
the specification cannot express (workflows that start only when asked,
read-only agents) is stated as rules in the skill and in each generated
file. The plugin and its/flowreel:*commands are unchanged, and
nothing changes in the npm package. The README's "Use with any Agent
Skills agent" says where each agent looks for it.
flowreel 0.4.1
Install: npm install --save-dev @confidee/flowreel@0.4.1 (npm, published by the release workflow with a provenance attestation).
A fix for the Claude Code plugin only: its prerequisite check failed with a
syntax error in 0.4.0. The npm package's code is unchanged from 0.4.0; its
version moves with the plugin's, which is what makes plugin users get the
fix. Update the plugin with /plugin marketplace update flowreel.
Fixed
- The plugin's prerequisite check (
plugin/scripts/check-prereqs.sh) ran
into a syntax error in 0.4.0 and exited with 2 (#29): the session-start
hook showed a bash error in every session of a flowreel project, and
/flowreel:doctorlost its summary line. An apostrophe inside a
single-quoted string, in theadviceline added in 0.4.0, ended the
string early. A test now parses every plugin shell script and runs the
check in both modes, under the shell onPATHand macOS's bash 3.2.
The npm package was not affected.
flowreel 0.4.0
Install: npm install --save-dev @confidee/flowreel@0.4.0 (npm, published by the release workflow with a provenance attestation).
Every request flowreel makes is bounded, and a failure says what it
means. The Microsoft 365 driver never follows a redirect and says whether
a failed send may have gone out; a mail wait rides out throttling and
brief network drops; the sign-in, session and persona requests have a time
limit; and the shared summary no longer carries Playwright's call log,
which could hold a live session cookie. Two security fixes: upgrade if
you use the Microsoft 365 driver or share summary.md.
Security
GraphMailDriverno longer follows redirects (#22). Affects 0.3.0
and earlier. The driver's requests usedfetch's default
redirect: "follow", and the fetch standard keeps the body on a 307 or
308: a 307 onsendMailsent the mail a second time (and reported the
send as successful), a redirect loop sent it up to 21 times, and a 307
on the token request re-sent the form body,client_secretincluded,
to the redirect target. Every Graph and token request now uses
redirect: "manual": a 3xx is aGraphHttpErrorthat sent nothing
(mayHaveSent: false), never followed and never retried. A project
that passes its ownfetchmust make one attempt per call and never
follow redirects.summary.md,summary.jsonandsummary.linesno longer carry
Playwright's call log (#26). Affects 0.3.0 and earlier. The summary
reporter copied up to 4000 characters of every error, and a failed
Playwright request's error goes on, after its first line, with a
"Call log" that names the full URL with its query string, every
request header (the persona's session cookie, a CSRF token) and a
response'sset-cookie(on a stalled sign-in, the fresh session
cookie): the file flowreel tells you to share could carry a live
session. The summary now keeps an error up to its call log and drops
everyhttp(s)://URL's query string and fragment; the HTML report
(where atoHaveURLfailure that differs only in its query still shows
the difference) and traces keep
the whole error and stay credential-bearing. flowreel's own sign-in
seam and session probes rethrow the first line of Playwright's error
only, without a cause, and so do the template and example adapters. If
you shared asummary.mdfrom a run that failed on a request, check it
for aCall log:block and revoke the sessions it names (a completed
teardown already revoked them).
Added
-
requestTimeoutMsforGraphMailDriver(@confidee/flowreel/mail-graph):
every Graph and token request, from the connection to the last byte of
the body, gets at most this long (default 30 s), and insidewaitForno
more than the wait has left. A whole number of milliseconds from 1 to
2147483647; anything else is aConfigError. -
GraphRequestTimeoutErrorfrom@confidee/flowreel/mail-graph
(timeoutMs, plusoperation,mayHaveSentandclientRequestIdfrom
GraphRequestError): what a request that got no answer throws. A
sendMailor a reply'ssendsetsmayHaveSent, says the mail may have
been sent and names itsclient-request-id; it is never retried. -
GraphRequestErrorfrom@confidee/flowreel/mail-graph: the base of
every error a Graph or token request ends in (operation,
mayHaveSent, always a boolean, andclientRequestId), with a
one-line message. A bad 2xx answer (a body that is not JSON, a token
answer withoutaccess_token) throws it as is, withmayHaveSent
false.GraphHttpError,GraphRequestTimeoutErrorand the new
GraphTransportErrorextend it (#17). -
GraphTransportErrorfrom@confidee/flowreel/mail-graph(code, such
asECONNRESETorUND_ERR_SOCKET; nocause): what a failure below
HTTP throws, instead of a plainError. The message is unchanged:
Graph <operation> failed: fetch failed (ECONNREFUSED)(#17). -
GraphHttpErrortakes an optional fifth argument, a details object
(mayHaveSent,clientRequestId,requestId,date,traceId,
correlationId,code), and exposes those fields; its four positional
arguments are unchanged (#17, #19). -
Request ids for
GraphMailDriver: every Graph and token request carries
a fresh randomclient-request-id, and the token request sends
return-client-request-id: true. Every error keeps it; a
GraphHttpErroralso keeps Graph'srequest-idanddate, and a token
error Entra'strace_idandcorrelation_id, when GUID-shaped. A token
error's message always names itstrace_idandcorrelation_idon the
detail line; a Graph request's message names its ids only when the mail
may have been sent (For Microsoft support: client-request-id …) (#19). -
requestTimeoutMsfor both seam HTTP clients
(@confidee/flowreel/rails-inertia), 30 s by default:
seamHttpFromRequest(request, { requestTimeoutMs })passes it as
Playwright's per-calltimeout(inside@playwright/testa request
context otherwise has none), andseamHttpFromFetch(baseURL, options)
now also takes{ fetch, requestTimeoutMs, setTimeout, clearTimeout }
besides thefetchfunction it took before. A seam request that runs
out is aborted and throws
Seam <METHOD> <path> got no answer within 30 s; raise requestTimeoutMs if a job drain legitimately takes longer
(#20). -
requestTimeoutMsfor flowreel's session requests (#24), 30 s by
default, validated like every flowreel request bound (a whole number of
milliseconds from 1 to 2147483647, else aConfigErrornaming the
option): onseamSignIn's options (@confidee/flowreel/rails-inertia),
onSessionProtocol(isSessionSignedOut;revokeSession, which never
throws, returnsrevoked: falsenaming the option, so teardown warns
loudly), and on
createPersonaApisandtourFixtures()for every persona request. It
is thetimeoutof each request context they open. -
requestFailure(error)from@confidee/flowreel/core: what may be
said of a failed Playwright request, the first line of its message
without a URL's query string, at most 300 characters. For a project's
own adapter (#26). -
/flowreel:doctoradvises whene2e/flowreel/adapter.tsopens request
contexts without a timeout (an adapter scaffolded before 0.4.0). It
only reads the file, with grep.
Changed
GraphMailDriver.waitFor: a temporary network failure on a mail read
or the token request is now re-polled instead of ending the wait (#23):
ECONNRESET,UND_ERR_SOCKET,EPIPE,ECONNABORTED,ETIMEDOUT,
EAI_AGAINand undici's connect, headers and body timeouts (an
AggregateErroronly when every inner code is listed), with the
token's back-off (5 s, then 10 s, 20 s … up to 60 s, never past the
wait's end, on top of the wait's own poll interval), counted apart for the token request and the reads. Only
once that endpoint has answered the driver: at first contact the same
error still ends the wait, so a firewall or broken DNS fails fast. Any
other code, or none, stays fatal (ECONNREFUSED,ENOTFOUND, TLS, a
customfetchthat throws a plain error), nothing that may have sent
mail is ever re-polled, and nothing is retried outsidewaitFor. A
wait that times out reports the step, the code and the count only
(Last observed: Graph read failed (ECONNRESET), 3 in a row, no mailbox read completed in this wait, retrying).GraphMailDriver.waitForre-polls a429,503or504only on a
GET read; a write's never was reachable there, and now cannot be.- The persona request contexts (
createPersonaApis, the tour'sas()
andpersonaApis) now have a 30 stimeoutof their own, which
overrides the project'sactionTimeoutfor them (15 s under flowreel's
createConfig); a request's owntimeoutstill wins. Set
requestTimeoutMsontourFixtures()for a longer one. - Global teardown (
signOutAll) now survives anAppAdapter.signOut
that throws, against its contract: that persona gets the loud "could
not sign out" warning (with the first line of the error) and the other
personas are still signed out. Before, one throw skipped every later
persona. seamSignInandisSessionSignedOutthrow
Test sign-in (POST <path>) failed: …and
Session probe GET <path> failed: …with the first line of
Playwright's error, instead of Playwright's error itself (#26).- The template adapter (
/flowreel:init) and the example's set a
REQUEST_TIMEOUT_MS(30 s) on every request context and onSESSION,
describe a failed request withrequestFailure(), andhealth()
returns a failed request's first line instead of throwing; the
template shipsunit/adapter.spec.ts, which checks both./flowreel:init
never rewrites an existing adapter: copy these lines into one you
scaffolded before (the doctor points it out). GraphMailDriver: a failedsendMailor replysendnow says whether
the mail may have been sent from everything observed, not only a
timeout. Any 5xx but 509 (503 included) and any transport failure that
does not prove the request never left (a reset or closed socket,
terminated,EPIPE, any code on areadorwrite, an unknown or
missing code) setmayHaveSent; a 3xx, a 4xx (429 included), 509 and
a DNS, connect or TLS failure (agetaddrinfoorconnectsyscall, or
a listed code on an error without a syscall) do not. An error status
whose body does not arrive within the bound stays a may-have-sent
timeout. A 2xx status now resolves the send at once, even when its empty
body then fails or never ends. Nothing is retried (#17).GraphMailDriver.waitFor: a token request that is throttled or meets a
server error (HTTP 429, 500, 502, 503, 504, or OAuth error
temporarily_unavailable) is now re-polled instead of ending the wait,
after ...
flowreel 0.3.0
Install: npm install --save-dev @confidee/flowreel@0.3.0 (npm, published by the release workflow with a provenance attestation).
A stopped run keeps its evidence. CI stop signals, a pipeline that dies
and the new --deadline all stop the running stage the way Ctrl-C does,
so its video, trace, sign-out and report survive, and a stopped run is
never a pass. Also: the npm page's logo reads on dark themes.
Added
--deadline(orFLOWREEL_DEADLINE) for the pipeline: a duration from
its start (25m,90s,1h, milliseconds) or a time (an ISO time with
a zone, epoch milliseconds). At the deadline the running stage is
stopped the way Ctrl-C stops it, no further stage starts
(not run (deadline reached)) and no combined video is made (one being
composed is stopped); exit 130, unless a FAIL (1) or an undo failure (4)
outranks it. A deadline already past at the start runs nothing (no
database copy, no stage). Stages get
FLOWREEL_DEADLINE_ATand skip their own video when less than 60 s
(FLOWREEL_COMPOSE_BUDGET_SEC) is left. No default. A bad value is a
usage error (64). New exports:parseDeadline,deadlineAt,
STOP_REASONS,ABORTED_REASON.- The stage guard,
installStageGuard()from@confidee/flowreel/reporter,
installed by the summary reporter first thing: SIGTERM (and Ctrl-Break
on Windows) stops a run the way Ctrl-C does (or exits 130 before
Playwright's runner listens); a stage whose pipeline is gone
(FLOWREEL_PIPELINE_PID) stops itself within about a second; a hangup
stops it gracefully when the pipeline runs in a terminal
(FLOWREEL_HUP=graceful); output to a pipe whose reader is gone no
longer crashes it. Only the first stop request becomes a Ctrl-C, so a
later one never cuts the teardown short. pipeline.jsonis written ahead, with astatus:runningfrom the
start (stagespending, verdictINCOMPLETE,finishedAtnull),
rewritten as each stage starts and ends,stoppingafter the first stop
request (withstoppedBy),finishedat the end andabortedafter a
second Ctrl-C. Every write is atomic. Any status butfinishedmeans
the pipeline did not finish; the explain skill and the run-explainer
agent say so.
Changed
- The npm page shows the logo on its own dark card
(assets/brand/flowreel-mint-banner.svg), so it reads on light and dark
pages alike; the dark-text logo was nearly invisible where the README is
shown on a dark theme, such as Socket (#12). - BREAKING: a stage now runs Playwright's own command-line entry
(@playwright/test/cli, resolved from the project) with the pipeline's
Node, notnpx;npx --no-install playwrightis the fallback when the
entry cannot be resolved, and the log says which.stageCommand()
returns Playwright's arguments (test …) without thenpxprefix, and
processIo()adds the launcher: a customPipelineIo.runChildmust do
the same.playwrightLauncher()andresolvePlaywrightCli()are new
exports. PipelineIotakes optionalinterruptChild,waitForChild,
killChild,clock,schedule,pidandinteractive, and its
onInterrupthandler receives the signal.PipelineDocumenthas
statusandstoppedBy, and itsfinishedAtis null until the
pipeline finished.createConfig()setshandleSIGTERM: falsein the browsers' launch
options (a project's own launch options keep it unless they set it): a
browser no longer closes itself when a CI signals the job's whole
process group, so the running test's video is kept; the runner closes it
in its graceful stop.- A stopped pipeline is never a pass: when a stop (a signal or the
deadline) came and the stages that ran passed (a--localrun, a stop
during the combined video), the pipeline isINCOMPLETEwith exit 130
and a reason naming the stop. - In a terminal, a hangup (SIGHUP) is a stop request for the pipeline and
the stage, and the pipeline ignores the closed terminal's output errors
so it can still writepipeline.json. Without a terminal SIGHUP keeps
its default.
Fixed
- A CI job that is cancelled or runs out of time no longer loses the run
(issue #11). The pipeline handles SIGINT, SIGTERM and, on Windows,
Ctrl-Break: the first stop asks the running stage to stop as Ctrl-C
does (on POSIX it sends the stage SIGINT, the one signal Playwright's
runner stops gracefully on), so the browser closes and the test's video
and trace are saved, the personas are signed out and the stage writes
its summary; no further stage starts and no combined video is made. The
same signal within 200 ms counts once; a SIGTERM after the first stop
never forces (GitHub Actions sends SIGINT, then SIGTERM 7.5 s later);
only a second Ctrl-C forces: the final pipeline files are written, the
stage gets 3 s to close before it is killed, the database copy is
dropped and the pipeline exits 130 (4 when a restore is still pending). - A stage whose pipeline was killed outright no longer runs on orphaned or
crashes on its next log line: it stops gracefully within about a second. - A stop signal during video composition ends it at once (ffmpeg, the
voice engine and the card renderer's browser are stopped) instead of
killing the run mid-encode; the summary says
video: not produced (video composition stopped (interrupted)). This
holds for a stage's video (the reporter listens from the start of the
run, so a signal that lands after Playwright's teardown, while the other
reporters finish, no longer kills the stage before its summary; a second
Ctrl-C a second later exits 130) and for the pipeline's combined video. - A run stopped during sign-in no longer leaves that session live: the
global teardown waits (up to 60 s) for sign-ins still in flight before
signing out. When the teardown was cut short, the summary reporter waits
for its sign-out, or signs the personas out itself (after waiting up to
20 s for pending restores) and says so loudly; the personas are never
signed out twice. - Ctrl-Break on Windows stops a run gracefully instead of killing it, and
on Windows the stage stops itself at the deadline (the pipeline cannot
signal it there). - A stage no longer goes through
npx, which cannot be spawned without a
shell on Windows (EINVALsince Node 20.12). There is no npx fallback on
Windows: a project without a resolvable@playwright/testgets a
message saying to install it.
flowreel 0.2.1
Install: npm install --save-dev @confidee/flowreel@0.2.1 (npm, published by the release workflow with a provenance attestation).
Documentation and release tooling only; the runtime's behaviour is
unchanged. The npm page now shows the logo, badges and the demo video's
thumbnail.
Added
- The release workflow creates the GitHub Release after the npm publish:
a third job (github-release, the only one that can write to the
repository, and it never sees the npm token) titles it
"flowreel " with an install line and the version's
CHANGELOG.mdsection as its notes (scripts/release-notes.mjs, with
unit tests). A release that already exists is left unchanged. - A 76-second narrated demo video at the top of the README, recorded with
flowreel 0.2.0 from npm. The video, its thumbnail and the run's report
are inassets/demo/; the leak tripwire allows the publisher's name in
that demo's README and run report only. - The npm page (the runtime README) shows the demo's thumbnail, linked to
the video on the project README (npm does not play videos); it appears
on npm with the next release. Its logo is now the full mint logo (the
symbol and the name, 320 px wide) instead of the small symbol alone. - Badges under the logo in the README and the npm page: npm version, CI,
license, Node and Claude Code plugin.
Changed
- CI can also be started by hand (Actions -> CI -> Run workflow).
- README: install from npm (
@confidee/flowreelis published); a clone
is only for trying an unreleased change. - Docs: they now say the package is on npm (no "pre-release" or "until the
runtime is on a registry"), and the example's setup offers the npm
install first. - Docs: exit 130 and the
INCOMPLETEverdict for an interrupted run are
documented everywhere the exit codes are listed. - Docs: the tour scanner's TypeScript range is stated (5.x and 6.x;
TypeScript 7 is not supported yet, issue #6), with a troubleshooting
entry. - Docs: links in the plugin's templates and README work outside the
repository; the changelog has version links. - Dependabot no longer proposes major versions of
typescript(TypeScript
7 has no JavaScript compiler API, which the tour scanner uses) or
@types/node(the type definitions follow the supported Node versions,
raised on purpose). Minor and patch updates still arrive.
flowreel 0.2.0
The first public release of flowreel: Playwright flows that are both your release gate and your narrated product videos.
You describe your app's features as plain-English plans; flowreel turns them into deterministic Playwright flows, runs them on a development target and then, only if they pass, on staging, and records each run as a branded, narrated video.
Install
The runtime, from npm:
npm install --save-dev @confidee/flowreel @playwright/testThe Claude Code plugin (it scaffolds, authors, runs and explains flows):
/plugin marketplace add Confidee/flowreel
/plugin install flowreel@flowreel
Start with the getting-started guide.
Highlights
- A development -> staging gate with exit codes CI can rely on: 0 pass, 1 fail, 2 staging not configured, 3 nothing verified, 4 a shared change could not be undone (manual action needed), 130 interrupted.
- Narrated, branded videos of every run, from the same flows that decide the verdict.
- A write-ahead undo ledger: every change a flow makes to shared settings is recorded before it is made and restored afterwards; anything not restored is listed first, with instructions.
- Adapters for Rails with Inertia (test seams that only answer from this machine, in development) and for Microsoft Graph mailboxes (send guards, redacted errors).
- A Claude Code plugin that never installs anything without asking; skills with side effects run only when you invoke them.
Quality
- 755 unit tests; coverage at 99.9% of lines and 98.6% of branches, with a 95% minimum enforced in CI and in the release build.
- CI on Node 20, 22, 24 and 26.
- Published from GitHub Actions through npm trusted publishing, with a provenance attestation. Check it with
npm audit signatures.
Fixed since the internal 0.1.0
- A Ctrl-C after development passed now ends as INCOMPLETE (exit 130), never PASS.
- An undo ledger that cannot be read now fails closed (exit 4) instead of reading as nothing to restore.
- The combined tour video is no longer lost when a summary holds a malformed feature entry.
Full list: CHANGELOG.md.
Security reports: please use private vulnerability reporting (see SECURITY.md).