Repository navigation
One zip any agent can read: install, a plant shaped like yours, shadow mode, and an honest page on migration - #173
Merged
Merged
Conversation
The folder the next commits add tells a person what to type. A page that says `fsmes demo bottlingg` wastes somebody's afternoon and makes the project look careless, and markdown does not compile, so this is the compiler: every `fsmes ...` in the folder, out of the inline code spans and the shell-ish fenced blocks, resolved against the typer app sub-command by sub-command, then its options, then how many arguments it will take. Written first, before a word of the folder, because it is the piece that keeps the folder true after everyone has forgotten it exists. Two things it cannot do are written down in the test itself rather than left for somebody to discover. An argument's *value* is not a CLI name, so `fsmes demo bottling stattus` is well formed as far as click is concerned. And a page that quotes what the computer printed must label the fence `text`, or the check reads the output as input. Typer vendors its own click core, so `isinstance(x, click.Group)` is False for every group in this app and a resolver written that way silently descends into nothing and passes everything. It duck-types instead: a command with a dict of `commands` is a group, and a param whose `opts` all start with a dash is an option. Signed-off-by: scottkalw <scottkalwei@gmail.com>
Plain markdown a person hands to whatever AI assistant they already use — in a chat window is fine, it needs no access to their machine — and says "help me install FactorySemantics MES". They run every command and paste back what it printed. `SKILL.md` opens with five rules, above the stage table, because an agent reads the top of a file and then starts being helpful: it runs nothing, it never asks for data, nothing leaves the building because of the folder, it says what is not built, and it works one stage at a time. Then the four stages, each with the same four parts — what you need before you start, the commands in order with what to paste back, what the output means, and done with this stage when: 1. Install, Windows words first because that is where the first person was: the PATH warning pip prints verbatim, `py -m fsmes` as the way out, the half-dead plant and the 0.3.0 line that `stop` cannot fix it and Task Manager is the way. 2. A plant shaped like theirs, over two machines because the MES box often has no Python. Stage 2 is the `plant-from-your-mes` folder, and the page hands over to it rather than summarising it. 3. Shadow mode beside their own MES: what to ask IT and Engineering for, how to tell shadow mode is really on, orders in by file, and the scorecard. Its footer says `first-plant.md` is written and untried, because it is. 4. Migration, with no commands on it at all: what they have when the scorecard matches, what is built, what is not built, and three questions to send us. Asked whether they can switch the old MES off, the answer on that page is no. Signed-off-by: scottkalw <scottkalwei@gmail.com>
…es in it Stage 2 of the assistant *is* `plant-from-your-mes`, so that folder has to be inside the one a person downloads. Two things it must not be: a second copy of those forty-one files in git, which would drift, and a symlink, which a zip delivers to Windows as a text file with a path in it. So the relationship is a line of registry. `fsmes.skills.INCLUDES` says the assistant carries that folder; `build_zip` and `unpack` both read it and nest it from the one copy in `skills/plant-from-your-mes/`. A test asserts `skills/assistant/plant-from-your-mes` does not exist and that nothing in the folder is a link. `RELEASED` is the other new line: which skills a release attaches, and under what name. The skill is `assistant`, which is what a person types after `fsmes skills unpack`; the download and the top folder inside the zip are `fsmes-assistant`, because a zip lands beside other people's downloads and has to say whose it is. The release workflow loops `RELEASED` instead of `SKILLS`, so one zip is attached and `plant-from-your-mes.zip` is retired — it is inside this one now, and two downloads of the same files would be a question about which to take. The skill stays in `SKILLS`: still checked, still unpackable by name, still what `fsmes shape plant`'s hint names. `fsmes skills-zip` with no name now checks every skill and each skill's includes, which is what CI runs; `--out` refuses when it would stand for more than one zip and says which names it means. Signed-off-by: scottkalw <scottkalwei@gmail.com>
The first line of this handoff's definition of done, checked the way a person checks it: a fresh virtual environment with the built wheel in it, the network off afterwards, `fsmes skills list` naming `assistant` and saying the folder came out of the installation rather than out of a checkout lying above the working directory, then `fsmes skills unpack assistant ./x` writing forty-eight files with `plant-from-your-mes/` among them, every one byte-for-byte the checkout's, and no symlink anywhere. A first step in `wheel-plant-from-shape` rather than a job of its own: the wheel is already built on both runners there, and a Windows round costs a day. Cheap and first, so a missing `force-include` line fails in a minute instead of after the half-hour shape run. The venv and network-off helpers are imported from `plant_from_shape.py` beside it rather than copied, for the same reason the skill folder has one copy. Signed-off-by: scottkalw <scottkalwei@gmail.com>
`plant-from-your-mes.zip` is not attached to a release any more, so the page about that folder says where it is instead — inside `fsmes-assistant.zip`, or out of an installation with `fsmes skills unpack`, which needs no download at all. `first-plant.md` gains a short paragraph saying that it is stage 3 of a folder you can hand to an assistant, for people who would rather be walked through it than read an afternoon's guide. The changelog's Unreleased section carries the line. Signed-off-by: scottkalw <scottkalwei@gmail.com>
|
Thanks for the first pull request. Checks: every commit signed off ( |
The steward's live run (a fresh no-tools assistant, the pages pasted in, him playing the first user on Windows) found the page's cheapest fix is the one release nobody has yet. `py -m fsmes` arrived in #172 and ships in 0.3.1; on 0.3.0 it prints `No module named fsmes.__main__; 'fsmes' is a package and cannot be directly executed`. The assistant in that run said "that error isn't on the page's list" and moved on, which is the right instinct and a worse afternoon than the page saying it. So way 1 now carries the 0.3.0 line, the same way the `stop` section already does, and the two places that reached for `py -m fsmes --version` or `fsmes --version` to settle "is it installed at all" reach for `py -m pip show factorysemantics-mes` instead: it answers on every release, with PATH broken or not, and prints the version anyway. Signed-off-by: scottkalw <scottkalwei@gmail.com>
Merged
kalwei
added a commit
that referenced
this pull request
Oct 15, 2026
The first-run fixes (#172), the shape file that starts a plant (#170), the assistant folder (#173) and `fsmes doctor` (#174), cut as 0.3.0 was cut. `src/fsmes/__init__.py` is the one place the version is written and hatchling reads it, so the wheel, the sdist and the container tag follow. `CITATION.cff` is the one a person types, and the release workflow refuses a tag whose citation disagrees. `## [Unreleased]` becomes `## [0.3.1] — 2026-10-15` with a fresh empty Unreleased above it, and the compare link at the bottom moves to v0.3.1. One entry is new rather than rolled: **#170**, the shape file that starts a plant, landed the morning after 0.3.0 was tagged and wrote no changelog entry at all. It is in this release whether or not anything says so, so it is written down — with what it has never done (a real SQL Server) beside what it does. `docs/releases/0.3.1.md` is the body of the GitHub Release, composed by `.github/scripts/release_notes.sh`: 119 lines, 5,744 of the 125,000 characters the API allows, led by what a Windows user sees differently. `docs/operate/ upgrade.md` gets a `## 0.3.0 to 0.3.1` section that says there are no migrations — `d4e8a1c93b70` is head on both versions — and the releases index gets a row. The 0.3.0 row said "not yet tagged" beside it, three days after the tag; it now says 2026-10-12. The date stamp is the UTC date, which is what the Release body will carry: `date -u` fills it in when the tag is pushed. Signed-off-by: scottkalw <scottkalwei@gmail.com>
kalwei
added a commit
that referenced
this pull request
Oct 15, 2026
The release half of 0.3.1. It changes the version, the dates, the changelog, the release page, the upgrade page and the README's status line, and nothing else. **Stacked on #175** (the GIF and the assistant pages): until #175 merges, this PR's diff shows its two commits too. Merge #175 first and this one becomes the eight files below. ## What is in 0.3.1 Four pull requests since 0.3.0: #170 (a plant shaped like your own, from the shape file), #172 (the first run on a Windows PC recovers itself), #173 (the assistant folder) and #174 (`fsmes doctor`). ## The files | File | What changes | |---|---| | `src/fsmes/__init__.py` | `0.3.0` → `0.3.1`. Hatchling reads this line, so the wheel, the sdist and the container tag follow | | `CITATION.cff` | `version: 0.3.1`, `date-released: "2026-10-15"`. The release workflow refuses a tag whose citation disagrees | | `CHANGELOG.md` | `## [Unreleased]` → `## [0.3.1] — 2026-10-15`, a fresh empty Unreleased above it, the compare link moved to `v0.3.1`, and **one new entry** (below) | | `docs/releases/0.3.1.md` | new — the body of the GitHub Release | | `docs/releases/index.md` | a row for 0.3.1; 0.3.0's row said "not yet tagged" three days after its tag and now says 2026-10-12 | | `docs/operate/upgrade.md` | a `## 0.3.0 to 0.3.1` section | | `README.md` | the status line: 0.3.1, the date, and one sentence of what is new | | `mkdocs.yml` | 0.3.1 in the Releases nav | ## One changelog entry that is new rather than rolled **#170 wrote no changelog entry at all.** It merged the morning after 0.3.0 was tagged, so the `[Unreleased]` section covered #172, #173 and #174 and said nothing about the command that turns a shape file into a running plant — which is the largest thing in this release. It is written down now, in the 0.3.1 section, with what it has never done (a real SQL Server) beside what it does. Nothing else in the section was edited; the rest is the Unreleased text as it stood on `main`. ## The release page `docs/releases/0.3.1.md` is **119 lines**, and `.github/scripts/release_notes.sh 0.3.1` composes a body of **5,744 of the 125,000 characters** the API allows. It is led by what a Windows user sees differently — `py -m fsmes`, a half-dead plant named process by process by `status`, `stop` with no flag — then the shape file that starts a plant, then the assistant folder, then `fsmes doctor`, then what 0.3.1 still cannot do. **No migrations.** `d4e8a1c93b70` is head on 0.3.0 and on 0.3.1, so the upgrade section is a `pipx upgrade` and a version check, and there is no Honesty entry to read first. ## How it is verified Run against this branch, before the tag: - `python -m build --wheel` → **`factorysemantics_mes-0.3.1-py3-none-any.whl`, 2,337,001 bytes (2.23 MB)**, under the 15 MB line. - That wheel installed into an **empty venv** (Python 3.13, nothing else in it): `fsmes --version` says `fsmes 0.3.1`. - From that venv **with the network cut** (`unshare -rn`): `fsmes skills unpack assistant ./assistant` writes the folder — 48 files, including `plant-from-your-mes/`. - From that venv **on a home with no plant in it**: `fsmes doctor --out doctor.md` writes the file — 48 lines, 0 plants, `leak check: passed`. - `python -m ruff check .` clean; `mkdocs build --strict` clean; the nine tests in `tests/test_the_release_body_github_will_accept.py` green, which is what asserts the body's length in the `build` job that a rehearsal also runs. - The default suite is green apart from `test_the_calibration_the_supervisor_records_is_on_the_gauges_own_register`, which compares a UTC date against local `date.today()` and so fails on this machine between 19:00 and midnight CDT. It is red on `main` at this hour too and green on CI, which runs in UTC. ## The date `2026-10-15` is the UTC date, and `release_notes.sh` fills the published body's "Released:" line from `date -u` — so a tag pushed before 2026-10-15 23:59 UTC makes the changelog, the index row, the README and the Release body agree. A tag after that needs the three stamps moved on; they are one `sed` each and the handoff's `## Result` says where. ## Not done here, on purpose No tag, no release, no PyPI, no workflow run: the `workflow_dispatch` rehearsal of `release.yml` on this branch is the maintainer's to trigger. Signed-off-by: scottkalw <scottkalwei@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
One download,
fsmes-assistant.zip, that a person hands to whatever AIassistant they already use — Claude, ChatGPT, Copilot, a local model, in a chat
window in the cloud or on the box — and says "help me install
FactorySemantics MES". It is plain markdown and nothing else. The agent needs
no access to the machine: the person runs every command and pastes back what it
printed.
Four stages, one at a time:
was — until
fsmes demo bottlinganswers on 127.0.0.1:8010.plant-from-your-mesfolder,nested inside this one as stage 2, ending at
fsmes demo from-shape.docs/operate/first-plant.mdturned intoan agent's script, ending at the shadow scorecard.
built, and how to tell us. It stops there. No commands on that page.
It comes out of an installation with no download and no internet:
The boring piece first: the folder cannot drift from the CLI
tests/test_every_command_the_assistant_folder_tells_you_to_run_exists.pypulls every
fsmes …command out of the markdown — inline code spans andshell-ish fenced blocks,
\continuations folded,py -m fsmesand.venv\Scripts\fsmesspellings included — and resolves each one against thetyper app: sub-command by sub-command, then the options, then the number of
arguments. 59 commands across the pages today (23 of them in
1-install.md), and4-migration.mddeliberately carries none.Proof it goes red, from breaking one command in
1-install.md:What it cannot catch is written down in the test itself
(
test_what_this_check_cannot_catch_is_written_down): an argument's value isnot a CLI name, so
fsmes demo bottling stattusis well-formed as far as clickis concerned. A page quoting computer output must label its fence
text, or thecheck reads the output as input — also written down, and
test_output_quoted_on_a_page_is_not_read_as_a_commandholds it.One copy in git, nested at build time
Stage 2 is
plant-from-your-mes, so that folder has to be inside the one aperson downloads. It is not a second copy in git and it is not a symlink — a
zipped symlink arrives on Windows as a text file with a path in it. The
relationship is one line of registry,
fsmes.skills.INCLUDES, whichbuild_zipandunpackboth read, so the nesting is made from the single copyin
skills/plant-from-your-mes/. Two tests hold that: the folder has noskills/assistant/plant-from-your-mesin it, and nothing anywhere in it is asymlink.
The two names. The skill is called
assistant— that is what a persontypes after
fsmes skills unpack. The download and the top-level folder insidethe zip are called
fsmes-assistant, because a zip lands in a folder besideother people's downloads and has to say whose it is. One dict,
fsmes.skills.RELEASED, says which skills a release attaches and under whatname, and the release workflow loops that instead of
SKILLS.plant-from-your-mes.zipis retired from the upload list — it is inside thisone now; two downloads of the same files would be a question about which to
take. The skill itself stays in
SKILLS: still checked, still unpackable byname, and
fsmes shape plant's hint text still names it.Checked
.github/scripts/assistant_folder_from_the_wheel.py, a first step in theexisting
wheel-plant-from-shapejob so no new Windows leg is paid for: afresh venv with the built wheel, the network off,
fsmes skills listnamesassistant,fsmes skills unpack assistant ./xwrites 48 files withplant-from-your-mes/among them, every one byte-for-byte the checkout's,no symlink anywhere. Ran green here on Linux; Windows is this PR's job.
fsmes skills-zip --checknow covers every skill and each skill's includes;CI runs it bare.
folder outside
plant-from-your-mes/824 lines), and the page-one rulesbeing the first thing in
SKILL.mdall have tests.ruff check .clean; the default pytest tier green.Honest caveats
first-plant.mdis writtenand untried, and stage 3 says so in its own footer rather than only here.
reading and vague about the rest; it cannot see the person's screen, so a
command that half-worked reads to it as a command that worked unless the
person pastes the whole output; it will not know which of the five rules it
is breaking if its own system prompt tells it to be helpful by running
things. The rules are first in the file for that reason, and
0-how-to-help.mdtells it to name the page it is reading from when it is unsure.
security policy, whether their IT department will trust a certificate, what
their MES calls its tables, whether their 0.3.0 install is the one the page
describes. Every "what the output means" section is the three or four things
that actually went wrong for the one person who has done this, plus what the
code prints today.
answer is no, with the list of what is missing — cutover, history migration,
PLC write-back beyond the recommendation queue, confirmations to an ERP in
charge, and the plain fact that none of this has run in production.
fresh assistant, the zip uploaded, somebody playing the first user on
Windows. That is the real test of a folder like this and it has not happened
yet.
No release in this PR.
CHANGELOG.md## [Unreleased]carries the line.After the steward's live run
The steward ran this before merge the way it is meant to be used: a fresh
no-tools assistant, the seven pages pasted in as the zip, him playing the first
user on Windows 11. It passed — one command per turn, the paste asked for, the
production data pasted "in case it helps" refused,
taskkillfor the half-deadplant, and a plain no to "can we switch the old MES off". It found one real
gap, now fixed in
1-install.mdand0-how-to-help.md: the cheapest way outof
not recognizedispy -m fsmes, which arrived in #172 and ships in 0.3.1,so on 0.3.0 — the only release anybody has — it prints
No module named fsmes.__main__. The page says that now, and the two places that usedfsmes --versionto settle "is it installed at all" usepy -m pip show factorysemantics-mes, which answers with PATH broken or not.