the-whole-workflow.md markdown
1,138 lines 27.5 KB
Raw
sha256:07c10b6184a651841b238710523048c817c5c062f2435c64628f6e407e9d55e3 Fix stale 'under seven minutes' claim in THE POINT section Sonnet 5 minor ⚠ breaking 23 hours ago

Episode 01 --- The Whole Workflow

Working YouTube title:
The Whole Workflow --- Init, Branch, Diff, Merge, Push, Clone

Thumbnail thought:
the whole workflow. no cuts.

Target runtime: ~14 minutes


[0:00--0:15] COLD OPEN

[CAMERA --- same terminal, same empty prompt Episode 00 ended on. No recap, no "previously on."]

GABRIEL:

Welcome back to "Build with Muse" episode 01 - the whole workflow. As a refresher Muse is domain agnostic version control for multidimensional state. We have a The MuseDomainPlugin protocol which is the single seam between domain-specific knowledge and the Muse core engine. Implement six methods and any state space — MIDI, source code, genomics, 3D spatial, financial models, identity graphs — gets branching, merging, time-travel, typed diffs, conflict resolution, and semantic versioning for free. The engine handles the DAG; you handle the meaning.

[beat]

Enough imagining.

[TITLE CARD --- fast]

THE WHOLE WORKFLOW

[Music enters. Terminal cursor blinking.]


[0:15--1:00] INIT

In this episode we're going to build a Muse repository and explore the whole workflow.

[TERMINAL]

mkdir hello-muse
cd hello-muse
muse init

[ENTER.]

✅ Initialized Muse repository in /Users/gabriel/hello-muse/.muse

GABRIEL VO:

That's a real repository. Content-addressed object store, commit DAG, the works.

[CAMERA]

We're running the whole workflow, start to finish, for real. No cuts, no pre-baked demo repo.


[1:00--3:50] FIRST COMMIT

[TERMINAL]

cat > hello.py <<'PY'
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    print(greet("world"))
PY

[ENTER.]

muse status
On branch main

Untracked files:
  (use "muse code add <file>" to include in what will be committed)

        untracked file:      .museattributes
        untracked file:      .museignore
        untracked file:      hello.py

GABRIEL VO:

Two files I never created myself: .museattributes and .museignore. init writes those automatically --- merge-strategy overrides and snapshot-exclusion rules, ready to edit, not something you configure from scratch.

[beat]

GABRIEL:

Before I commit anything, let's actually look at what's about to go in.

muse diff
A  .museattributes
A  .museignore
A  hello.py
   └─ added function greet  L1–2

2 added, 1 modified files, 1 added symbol

GABRIEL VO:

Every symbol Muse tracks lives in one of four states, and they're color-coded the same way every time you see them this season: green A for added, red D for deleted, yellow M for modified, cyan R for moved or renamed.

[beat]

If you want the view you already know:

muse diff --text
--- /dev/null
+++ b/hello.py
@@ -0,0 +1,5 @@
+def greet(name):
+    return f"Hello, {name}!"
+
+if __name__ == "__main__":
+    print(greet("world"))

GABRIEL VO:

--text is the escape hatch. Same diff, rendered exactly like git diff would --- unified format, plus and minus lines, nothing symbol-aware about it. Muse doesn't force the structured view on you. It's the default because it's usually the more useful one, not because the other one stopped existing.

[beat]

GABRIEL:

Now let's actually commit it.

muse code add .
Staged 3 added files.

muse commit -m "Add greet function"
[main sha256:ac4b1df23c27d15fa8ee32f15b1e4e48738efe6fb4078fea103668533dfb0e30] Add greet function
 3 files changed (3 added)

Add.

Commit.

Feels exactly like Git, on purpose.

[beat]

The familiarity is the point. You're not learning a new tool from zero.

GABRIEL:

Now back to the 4 states a symbol can be in—A for added, red D for deleted, yellow M for modified, cyan R for moved or renamed. Here's all four, in this exact repo.

[TERMINAL]

cat > scratch.py <<'PY'
def one():
    return 1

def two():
    return 2
PY
muse code add scratch.py
Staged 1 added file.

muse commit -m "Add scratch.py for a quick diff demo"
[main sha256:0d25b58226ef06d39737f33c7e10f42ac18d6e98a38cf918c512236ff1c91ef3] Add scratch.py for a quick diff demo
 1 file changed (1 added)

GABRIEL VO:

I'm committing that before I touch anything else --- not staging it, committing it. muse diff compares the working tree against HEAD, a real commit. Rename detection needs something on the other side of that comparison to recognize. Stage a file and rename it in the same breath, before it's ever been committed, and there's no "before" for Muse to compare against --- it just looks like a new file showed up under a different name.

[beat]

GABRIEL:

Now the rename, on a file that's actually got history.

muse mv scratch.py notes.py
mv: scratch.py → notes.py
muse diff
R  scratch.py → notes.py

1 renamed file

GABRIEL VO:

Cyan R, on its own. A pure rename --- Muse only calls it a rename when the content on both sides is still recognizably the same thing. Change too much at once and it'll honestly tell you "this looks like a new file and a deleted one" instead of guessing. I'll commit the rename by itself, so it stays a clean one.

muse commit -m "Rename scratch.py to notes.py"
[main sha256:c447f506c352542c47c88ca49b17255dccaef31b11d7d3210e593de88e9ba72f] Rename scratch.py to notes.py
 2 files changed (1 added, 1 removed)
cat > notes.py <<'PY'
def two():
    return 2, "louder"

def three():
    return 3
PY

[that overwrite removed one, kept and changed two, added three --- three real edits in one file write]

muse diff
M  notes.py
   ├─ removed function one  L1–2
   ├─ added function three  L4–5
   └─ function two (implementation changed)  L1–2

1 modified file, 1 added, 1 removed, 1 modified symbols

GABRIEL:

Red delete, green add, yellow modify --- all three in one file, one command, right after a clean cyan rename. Four states, two real commits.

Now to further explore common workflow commands let's reset. First run muse log --oneline to get the commit id that we want to reset back to—in. Whichever commit I want back, that's the ref. In this case the "Add greet function" commit. ex: sha256:251de5b990318464398d7fc200d607a0797ca951ba2d9fe4d661a16ef761f281

muse log --oneline
sha256:58edec088661854a49b45760d94444df791f667e4b58dd2d89b2365a4c9a404b (HEAD -> main) Merge branch 'feature/farewell' into main
sha256:474a34b45d2eba6aaa9202e8fae0d8b06612b6efa7878431e9171cc14b11850a Warmer greeting
sha256:251de5b990318464398d7fc200d607a0797ca951ba2d9fe4d661a16ef761f281 Add greet function
muse reset --hard sha256:251de5b990318464398d7fc200d607a0797ca951ba2d9fe4d661a16ef761f281 --force

HEAD is now at sha256:251de5b990318464398d7fc200d607a0797ca951ba2d9fe4d661a16ef761f281 Add greet function

GABRIEL VO:

reset --hard to the commit right before any of that happened. Two detour commits, gone, working tree back to exactly one file and one function. The repo doesn't remember I was ever there --- which is the whole point of using a real, disposable branch of history to prove a claim instead of just asserting it.


[3:50--5:15] STATUS, LOG, DIFF

[TERMINAL --- rapid-fire, no narration between commands]

muse status
On branch main. Clean.

muse log
commit sha256:1e8b1f5... (HEAD -> main)
Author: gabriel
Date:   2026-09-16 16:50:35 UTC
SemVer: PATCH

    Add greet function

muse diff
(nothing — working tree matches HEAD)

GABRIEL VO:

Status. Log. Diff.

The three questions you ask a hundred times a day.

[beat]

GABRIEL:

Everything I've just run, a human reads. Here's the same question, asked the way an agent asks it.

muse status --json | python3 -m json.tool
{
  "muse_version": "0.2.1rc4",
  "schema": 1,
  "exit_code": 0,
  "duration_ms": 1.886,
  "timestamp": "2026-09-16T17:20:23.081Z",
  "warnings": [],
  "branch": "main",
  "head_commit": "sha256:4892e386...",
  "clean": true,
  "dirty": false,
  "added": [],
  "modified": [],
  "deleted": [],
  "untracked": [],
  "conflict_count": 0
}

GABRIEL VO:

Every single Muse command has a --json flag. Not most. Not the popular ones. All of them --- and every one of those responses is wrapped in the same envelope: muse_version, schema, exit_code, duration_ms, a timestamp, a warnings list. Before you get to a single field that's specific to status, you already know what version produced this, whether it succeeded, and how long it took.

[CAMERA]

That's not incidental. If an agent is going to be a first-class author of this repository, it needs a first-class way to ask it questions --- and "parse whatever text a human-facing command happened to print" was never going to be that.

[beat]

GABRIEL:

One line in that log entry isn't from Git at all: SemVer: PATCH.

[beat]

I never typed that. Muse looked at what actually changed --- one new function, nothing removed, nothing renamed, no existing signature touched --- and classified it itself. Add a function, that's a PATCH. Change a function's signature out from under its callers, that's a MAJOR, and Muse will tell you before you find out the hard way.

[CAMERA]

Semantic versioning isn't a discipline you have to remember to practice here. It falls out of the same symbol-level understanding that made that colored diff possible ten seconds ago. Same information, second use.


[5:15--6:45] BRANCH, TWICE

[TERMINAL]

muse switch -c feature/friendlier --intent "warmer greeting copy" --resumable
Switched to a new branch 'feature/friendlier'

[edit hello.py --- change the return string]

-    return f"Hello, {name}!"
+    return f"Hey there, {name}!"
muse code add hello.py
Staged 1 modified.

muse commit -m "Warmer greeting"
[feature/friendlier sha256:25765a8be21acb8ea6dca1a404980d0e488e5eda727ebedef5072051a8ecc58c] Warmer greeting
 1 file changed (1 modified)

muse switch main
Switched to branch 'main'

muse switch -c feature/farewell --intent "add a farewell function" --resumable
Switched to a new branch 'feature/farewell'

[append to hello.py --- a new, independent function]

+def farewell(name):
+    return f"See you, {name}!"
muse code add hello.py
Staged 1 modified.

muse commit -m "Add farewell function"
[feature/farewell sha256:9dd35d3c58eeb905c7857e6dbc36e98634c4a6ec1e34c2a73b5ed3ca6b998c54] Add farewell function
 1 file changed (1 modified)

GABRIEL:

Two branches off the same commit.

One changes what the greeting says.

The other adds something that didn't exist before.

[beat]

Completely independent edits to the same file.

[beat]

GABRIEL VO:

Two flags on that branch command that don't exist in Git: --intent and --resumable.

--intent is a one-line reason the branch exists --- readable by any agent that looks it up later, without reading a single commit. --resumable marks it safe for another agent to pick up mid-task.

[beat]

Neither is decoration. That's how a fleet of agents hands work off to each other without a stand-up meeting.

[beat]

GABRIEL:

But watch what happens if I just list branches the normal way.

muse branch -a
* feature/farewell
  feature/friendlier
  main

[beat]

Nothing. No intent, no resumable flag, nothing that looks different from Git at all. That's deliberate --- this view is git-idiomatic on purpose. Now ask the same question the way an agent actually would.

muse branch --json | python3 -m json.tool
[
  {
    "name": "feature/farewell",
    "current": true,
    "commit_id": "sha256:ba670da4...",
    "committed_at": "2026-09-16T19:10:00.251171+00:00",
    "last_message": "Add farewell function",
    "upstream": null,
    "intent": "add a farewell function",
    "resumable": true,
    "created_by": null
  },
  {
    "name": "feature/friendlier",
    "current": false,
    "commit_id": "sha256:b819baf8...",
    "committed_at": "2026-09-16T18:56:24.954014+00:00",
    "last_message": "Warmer greeting",
    "upstream": null,
    "intent": "warmer greeting copy",
    "resumable": true,
    "created_by": null
  },
  {
    "name": "main",
    "current": false,
    "commit_id": "sha256:da9465b0...",
    "committed_at": "2026-09-16T18:48:46.880968+00:00",
    "last_message": "Add greet function",
    "upstream": null,
    "intent": null,
    "resumable": false,
    "created_by": null
  }
]

GABRIEL VO:

There it is. muse branch --help says this outright: agents should pass --json for machine-readable output on every operation. The human-facing listing and the agent-facing one aren't the same feature with a flag bolted on --- they're two different answers to "what branches exist," and this is the one built for something that's going to parse it, not read it.


[6:45--7:25] DIFF BETWEEN BRANCHES

[TERMINAL]

muse diff feature/friendlier feature/farewell

[SCREEN --- structured diff, not a raw patch]

M  hello.py
   ├─ added function farewell  L4–5
   └─ function greet (implementation changed)  L1–2

1 modified file, 1 added, 1 modified symbols

GABRIEL VO:

This is the same file on both sides. A line-based tool sees one overlapping mess waiting to happen.

Muse sees two changes that don't actually touch each other.

[CAMERA]

Which means the next step should be boring.


[7:25--8:45] MERGE

[TERMINAL]

muse switch main
Switched to branch 'main'

muse merge feature/friendlier
Updating sha256:936b78252352..sha256:25765a8be21a
Fast-forward  feature/friendlier → main
 1 file changed  (1 modified)

muse merge feature/farewell
Merge made by the three-way strategy.
  feature/farewell → main  sha256:9026f13d8e54
 1 file changed  (1 modified)

GABRIEL VO:

Two different messages, on purpose. friendlier was a fast-forward --- main hadn't moved since that branch was created, so Muse just slid the pointer forward, no merge needed. farewell actually needed one: main had already moved because friendlier just landed on it. Both of those ran on recursive, Muse's default strategy --- there's also overlay, snapshot, replay, ours, and theirs. More on merge strategies later this season.

[cat hello.py --- both changes present]

def greet(name):
    return f"Hey there, {name}!"

def farewell(name):
    return f"See you, {name}!"

if __name__ == "__main__":
    print(greet("world"))

GABRIEL:

Both changes. One file. Zero conflicts.

[beat]

Not because I got lucky. Because they never actually collided in the first place, and Muse could tell.

[beat]

GABRIEL:

Honest comparison, though --- let's actually run the same two branches through Git.

[SCREEN --- side terminal, plain git, same hello.py, same two edits]

git merge feature/friendlier
Updating e455e1e..577656a
Fast-forward
 hello.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

git merge feature/farewell
Auto-merging hello.py
Merge made by the 'ort' strategy.
 hello.py | 2 ++
 1 file changed, 2 insertions(+)

GABRIEL VO:

Also clean. No conflict. I tried to break this three different ways --- adjacent functions, a line-shifting reformat on one side --- Git's real merge algorithm handles disjoint edits fine. That's not a myth to bust.

[beat]

GABRIEL:

Here's the actual difference. Git succeeded silently. It applied two hunks that happened to land on different lines and moved on --- it never knew greet and farewell were two separate things. Muse told me they were independent symbols one command ago, before I ever typed merge. The merge succeeding isn't a surprise here. It's a confirmation of something I already knew.


[8:45--10:45] WHERE THEY ACTUALLY DIVERGE

[CAMERA]

That still leaves a real question --- is there a case where Git genuinely fails and Muse doesn't? Episode 05 named five independently mergeable dimensions in the code domain: structure, symbols, imports, variables, metadata. Let's use one on purpose.

[beat]

GABRIEL:

We're testing imports. Here's its actual definition, straight from the domain itself --- not my paraphrase.

muse domain-info --json | python3 -m json.tool
{
  "name": "imports",
  "description": "Import set. Tracks added / removed import statements as an unordered set — order is semantically irrelevant.",
  "schema": { "kind": "set", "element_type": "import", "identity": "by_content" },
  "independent_merge": true
}

GABRIEL VO:

"Unordered set" is a specific, testable claim, not a vibe. If it's true, two branches adding different imports should merge like two independent set insertions --- no collision, regardless of where each line physically landed. Let's hold Muse to its own definition.

[beat]

GABRIEL:

Same setup, run twice --- once in a real Git repo, once in a real Muse repo. Both start from the exact same three lines.

cat > app.py <<'PY'
import os
import re

def greet(name):
    return f"Hello, {name}!"
PY

[CAMERA]

Commit that as the shared starting point in both repos. Then, in each one, two branches --- same names, same intent, same divergence point.

git checkout -b feature/add-sys

[add one line to app.py --- import sys, right after import re]

git commit -am "Add sys import"

git checkout main
git checkout -b feature/add-json

GABRIEL VO:

Back on main first --- feature/add-json branches from the original three-line file, not from what add-sys just did. Neither branch has seen the other's change. That's what makes this a real test, not a staged one.

[add one line to app.py --- import json, in the exact same place, right after import re]

git commit -am "Add json import"

git checkout main

GABRIEL:

Two branches, one common ancestor, two different single-line additions, both landing at the same spot in the file. Now merge them, one at a time.

[TERMINAL --- two branches, each adding a different import to the same file, no reordering]

git merge feature/add-sys
Updating 599c9e9..a3575db
Fast-forward
 app.py | 1 +
 1 file changed, 1 insertion(+)

git merge feature/add-json
Auto-merging app.py
CONFLICT (content): Merge conflict in app.py
Automatic merge failed; fix conflicts and then commit the result.
import os
import re
<<<<<<< HEAD
import sys
=======
import json
>>>>>>> feature/add-json

GABRIEL:

Real conflict. Two developers, two different imports, same insertion point --- one of the single most common merge conflicts that exists. Git has no concept of "these are two unordered additions to the same set." It just sees one line, two edits.

[beat]

GABRIEL:

Same two branches, in Muse --- identical setup, different repo.

muse init
cat > app.py <<'PY'
import os
import re

def greet(name):
    return f"Hello, {name}!"
PY
muse code add . && muse commit -m "Initial imports"

muse switch -c feature/add-sys

[add import sys after import re]

muse code add app.py && muse commit -m "Add sys import"

muse switch main
muse switch -c feature/add-json

[from the original three-line file again, exactly like the Git side --- add import json in the same spot]

muse code add app.py && muse commit -m "Add json import"

muse switch main

GABRIEL VO:

Same shape as the Git repo, on purpose: one shared ancestor, two branches that never saw each other's change, both adding one import in the same place. Now the merges.

muse merge feature/add-sys

Updating sha256:67a8c010cc95..sha256:d0c061342b71
Fast-forward  feature/add-sys → main
 1 file changed  (1 modified)
muse merge feature/add-json

Merge made by the three-way strategy.
  feature/add-json → main  sha256:c6d34f27f9e7
 1 file changed  (1 modified)
import os
import re
import sys
import json

GABRIEL VO:

Clean. Zero conflict, zero manual resolution. imports is one of those five dimensions --- an unordered set, independently mergeable, exactly the way Episode 05 described it. Two agents adding two different imports isn't a collision. It's a union. Git can't see that; it only sees lines. Muse was built to see the set.

[beat]

GABRIEL:

One honest caveat --- reorder those same imports on one branch first, then add a new one on the other, and both tools currently conflict. The set model doesn't cover every shape of change yet. That's a real edge, not a footnote to hide.


[10:45--11:45] PUSH

[CAMERA]

One thing before we push. There's no origin in Muse --- that's a Git name. init already configured three real remotes for me.

muse remote -v

local     	https://localhost:1337/gabriel/hello-muse (fetch)
local     	https://localhost:1337/gabriel/hello-muse (push)
production	https://musehub.ai/gabriel/hello-muse (fetch)
production	https://musehub.ai/gabriel/hello-muse (push)
staging   	https://staging.musehub.ai/gabriel/hello-muse (fetch)
staging   	https://staging.musehub.ai/gabriel/hello-muse (push)

GABRIEL VO:

I'll push to local --- my own MuseHub instance, running right here. Same commands work against staging or production; only the URL changes.

[TERMINAL]

muse hub repo create --name hello-muse --visibility public
muse push local main

[ENTER. Real network call.]

❌ Push rejected — remote 'local/main' has diverged.
   Pull first (muse pull) or use --force to override.

[beat]

GABRIEL:

Stop --- that's real, and it's not me. That message shows up on every freshly created repo's first push, every time, on this instance and on staging. I traced it: muse hub repo create seeds a placeholder "Initial commit" server-side so the repo is browsable immediately, and that seeded commit is missing its snapshot entirely. It's not valid. Cloning it proves it:

⚠️ apply_mpack: malformed commit — skipped: snapshot_id must be a
   non-empty str, got ''

[beat]

Not a real divergence. A broken placeholder standing where an empty repo should be. Filed as staging#220 --- the fix is either make that placeholder a valid commit, or stop creating it from the CLI at all. For today, the workaround is exactly what Git's own --force means: I know there's nothing on the remote worth keeping.

muse push local main --force
✅ Pushed 4 commit(s), 6 object(s) to local/main (sha256:d7ec67751706b65cbcd454d1dbb42263a27206d504168dae236dc5fa8105dd1a)

[SCREEN --- MuseHub repo page, live, showing the commits that just landed.]

GABRIEL VO:

That's not a mock server. That's MuseHub, running for real, and that push just happened live, bug and workaround included.

[CAMERA]

We'll get to how the signing and the wire protocol actually work. Later episodes, promise.

Right now I just want you to see the round trip --- warts included.


[11:45--12:15] CLONE

[TERMINAL --- a different directory]

cd /tmp
muse clone https://localhost:1337/gabriel/hello-muse

[ENTER.]

✅ Cloned into 'hello-muse' — 4 commit(s), 3 blob(s), domain=code, branch=main
cat hello-muse/hello.py

[Output matches, byte for byte, what we just built.]

GABRIEL:

Same repository. Different machine, different directory, could easily be a different person, or a different agent.

[beat]

That's the whole point of a content-addressed object store. It either reconstructs exactly, or it fails loudly. There's no in-between.


[12:15--12:50] SPEEDRUN RECAP

[SCREEN --- stopwatch, still running. Terminal history scrolls fast in a picture-in-picture, replaying everything at high speed.]

GABRIEL VO [fast]:

Init.

Add.

Commit.

Branch.

Branch again.

Independent edits.

Structured diff.

Merge, clean.

Push, real network.

Clone, byte-identical.

[stopwatch stops]

[CAMERA]

That's the entire core workflow. On camera. In real time.


[12:50--13:25] THE POINT

[CAMERA]

None of this required you to know anything about content addressing, Ed25519 signatures, or the six-method domain protocol.

[beat]

That's deliberate.

The primitives from Episode 00 --- commit, branch, diff, merge, push, fetch --- aren't theoretical. They're the exact commands you just watched run, in order, with real output, start to finish.

[ON SCREEN]

IT'S NOT A PITCH. IT'S A CLOCK.


[13:25--13:50] OUT

[TERMINAL --- back in hello-muse, main branch, five commits deep.]

ls .muse

GABRIEL VO:

We built this whole history without once looking at what's actually sitting inside that .muse directory.

[beat]

Next episode, we tear it open. Objects, snapshots, the commit DAG, every hash --- what's really in there, and why.

[CAMERA.]

See you inside.

[CUT TO BLACK]

musehub.ai


Production Notes

Episode 01 should feel like a demo with the safety net removed. No narration should imply anything was staged, trimmed, or run twice off camera. If a command is going to be shown, it runs, live, in the edit's real time or as close to it as pacing allows.

Opening

Pick up literally where Episode 00 stopped --- same terminal, same muse init about to run. No "hey everyone, welcome back." The continuity itself is the hook: last episode ended on a cliffhanger, this one resolves it in the first fifteen seconds.

The Clock Is a Prop, Not a Gimmick

The on-screen stopwatch starting at INIT and stopping at CLONE is the spine of the episode. It's what makes the title a promise instead of a number. If the real take runs long, cut narration, never cut commands --- the workflow has to stay complete and unbroken.

Show Real Muse Constantly

Every command in this script is real and expected to actually run this way against a real main branch of a real, disposable hello-muse repo pushed to a real MuseHub instance. If anything here doesn't work exactly as written when the demo script is built, fix the demo, not the script's honesty.

Don't Explain, Demonstrate

This is the one episode in the season with almost no lecture. Episode 00 made the claims. Episode 01's entire job is to cash them. Resist the urge to explain content addressing, signing, or the domain protocol here --- there are whole episodes coming for each of those. Every time the urge to explain hits, cut to the next command instead.

The Seed

The viewer arrives at this episode still a little skeptical:

Okay, but does it actually work, end to end, or is that a highlight reel?

They should leave asking:

Wait --- what's actually in that .muse folder?

That's Episode 02, and the last shot of this episode should point directly at it.

File History 2 commits
sha256:07c10b6184a651841b238710523048c817c5c062f2435c64628f6e407e9d55e3 Fix stale 'under seven minutes' claim in THE POINT section Sonnet 5 minor 23 hours ago
sha256:09264fa85007556b21298030c0b199f4cfd62912a27b1838f586c31b8908beed Rename Episode 01 to 'The Whole Workflow' Sonnet 5 2 days ago