Docs audit fixes: kiosk code drift, PowerShell chains, broken links, leaks
Some checks failed
CI / backend (push) Successful in 1m45s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 8s
CI / migrations-mysql (push) Failing after 8s

From the Fable/Opus documentation audit (8 confirmed + verified
lab-drift the run's session limit had cut short):
- HIGH: the lab's kiosk _kiosk_find_item block showed the pre-stage-17
  row-id resolver as current; replace with the shipped gagelabtag /
  numeric-tail resolver, fix the stale 'resolved by row id' prose and
  the 'stage-7 code is corrected' note.
- MED: the badge _external_lookup block used dict-only row access that
  breaks on a tuple cursor; use the tuple-or-dict form shipped. Split
  '&&' command chains (fail in PowerShell 5.1) in the lab.
- LOW/link: the Windows note's [DEVELOPMENT-SETUP] link dropped the .md
  and 404'd in four docs; fix. Correct the stage-6a->16a comment and
  the lab-stage tag range (..16 -> ..17).
- Leaks: drop /home/camp path from ADR-006, the internal gitea host
  from PLUGINS.md.
- Windows: add an mklink junction note for the external-plugin symlink
  dev loop.
- CI: prime root to mysql_native_password so pymysql connects to the
  MySQL 8 service without the cryptography package (and its kit wheel).
This commit is contained in:
cproudlock
2026-07-17 18:05:47 -04:00
parent aba588cc07
commit efb879d44a
7 changed files with 53 additions and 31 deletions

View File

@@ -75,10 +75,16 @@ jobs:
python-version: '3.12'
cache: pip
- run: pip install -r requirements.txt
- name: Force utf8mb4 on the CI database
- name: Prime the CI database (utf8mb4 + native auth for pymysql)
# MySQL 8 defaults root to caching_sha2_password, which pymysql can
# only speak with the 'cryptography' package. Rather than add that
# dependency (and its offline wheel), switch root to native auth here
# via the mysql CLI, so the app's pymysql connections work as-is.
run: |
mysql -h 127.0.0.1 -uroot -proot -e \
"ALTER DATABASE shopdb_ci CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
"ALTER DATABASE shopdb_ci CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; \
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root'; \
FLUSH PRIVILEGES;"
- name: Fresh core upgrade from empty
run: flask db upgrade
- name: Install every bundled plugin (runs its chain)

View File

@@ -10,7 +10,7 @@ is required for v1 (pip distribution is deferred to v2 per ADR-003).
> **Windows / VS Code:** command examples use the Linux venv path
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
If you have not written a plugin before, start with
@@ -77,6 +77,11 @@ git clone https://github.com/ge-aero/wjsf-shipping.git
cd shopdb-flask
ln -s ../../wjsf-shipping plugins/shipping
# (use an absolute path if you prefer: ln -s "$(pwd)/../wjsf-shipping" plugins/shipping)
#
# Windows: use a directory junction instead of ln -s. In an ADMIN prompt
# (or with Developer Mode on) from the shopdb-flask dir:
# mklink /D plugins\shipping ..\..\wjsf-shipping
# The plugin loader treats a junction the same as a real directory.
# 3. Set up the framework as usual.
python3 -m venv venv

View File

@@ -8,7 +8,7 @@ way it does by walking the shipped code of the exemplar plugin.
> **Windows / VS Code:** command examples use the Linux venv path
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
`measuringtools` was chosen as the exemplar on purpose. It is the first plugin
built after the framework matured (ADR-005 scoped it; ADR-008 changed how plugin

View File

@@ -17,7 +17,7 @@ decision records: `docs/proposals/printedparts-plugin.md`.
> **Windows / VS Code:** command examples below use the Linux venv path
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
Know before you start
@@ -262,7 +262,7 @@ def downgrade():
See it work:
```
flask plugin install printedparts && flask plugin enable printedparts
flask plugin install printedparts; flask plugin enable printedparts
mysql> SHOW TABLES LIKE 'printed%'; -- both tables
mysql> SELECT * FROM alembic_version_printedparts; -- printedparts0001baseline
flask plugin upgrade-all -- printedparts: ok
@@ -299,7 +299,7 @@ printedparts_bp = Blueprint('printedparts', __name__)
@printedparts_bp.route('/items', methods=['GET'])
@jwt_required(optional=True) # stage 6a tightens this to view-gated
@jwt_required(optional=True) # stage 16a tightens this to view-gated
def list_items():
"""List printed items, paginated; search + low-stock filter."""
page, per_page = get_pagination_params(request)
@@ -629,10 +629,11 @@ def _external_lookup(kind, digits):
f'WHERE {column} = %s', (digits,))
row = cursor.fetchone()
if row:
sso = str(row['SSO'])
name = f"{(row['First_Name'] or '').strip()} " \
f"{(row['Last_Name'] or '').strip()}".strip()
return sso, name
# pymysql may return a tuple or a dict cursor - handle both.
sso = str(row[0] if not isinstance(row, dict) else row['SSO'])
first = row[1] if not isinstance(row, dict) else row['First_Name']
last = row[2] if not isinstance(row, dict) else row['Last_Name']
return sso, f"{(first or '').strip()} {(last or '').strip()}".strip()
except Exception:
logger.exception('HR directory lookup failed for %s %s', kind, digits)
finally:
@@ -798,17 +799,25 @@ catch you (below).
def _kiosk_find_item(itemcode):
"""Resolve a scanned or typed code to an active item.
Accepts the full code (WJRP0042) or bare digits from the touch keypad -
the digits in a minted code ARE the row id, so id lookup keeps working
even for labels printed under an older prefix."""
itemcode = (itemcode or '').strip()
Matches the internal code OR the gage-lab tag exactly; bare keypad
digits match the numeric tail of EITHER identifier, and only when
exactly one active item matches (see stage 17)."""
scanned = (itemcode or '').strip().upper()
item = PrintedItem.query.filter(
PrintedItem.itemcode == itemcode,
or_(PrintedItem.itemcode == scanned,
PrintedItem.gagelabtag == scanned),
PrintedItem.isactive == True).first()
if not item and itemcode.isdigit():
candidate = db.session.get(PrintedItem, int(itemcode))
if candidate and candidate.isactive:
item = candidate
if not item and scanned.isdigit():
wanted = int(scanned)
matches = []
for candidate in PrintedItem.query.filter_by(isactive=True).all():
for value in (candidate.itemcode, candidate.gagelabtag):
tail = ''.join(ch for ch in (value or '') if ch.isdigit())
if tail and int(tail) == wanted:
matches.append(candidate)
break
if len(matches) == 1:
item = matches[0]
return item
@@ -920,7 +929,8 @@ function onWedgeEnter() {
```
Manual fallbacks use the TouchKeypad: badge entry is digits (an SSO), and
item entry is bare digits resolved by row id server-side - no alphanumeric
item entry is bare digits matched server-side against the numeric tail of
either the internal code or the gage-lab tag (unique match only) - no alphanumeric
on-screen keyboard needed. Success screen auto-resets after a few seconds.
Full component (~250 lines) at the tag.
@@ -1093,7 +1103,7 @@ failure. Summaries here; complete diffs at the tags.
hit the deny policy. Lesson: anything resolving PEOPLE must honor the
site's directory mode (the stage-5 code above is the corrected version).
- **16 - touchscreen findings** (`lab-stage-16`): the focus-steal guard and
keypad-driven manual entry (the stage-7 code above is the corrected
keypad-driven manual entry (the kiosk resolver above is the stage-17
version).
- **17 - the gage-lab asset tag** (`lab-stage-17`): the field team assigns
@@ -1134,11 +1144,12 @@ for a contributor:
2. **Build in stage-sized commits** exactly as this lab does - each commit a
working checkpoint with its tests. Subject line: short, plain English,
present tense ("printedparts stage 5: the ledger"); body says WHY.
3. **Before every push**, the same three gates CI runs:
```bash
bash scripts/check-naming-and-style.sh
venv/bin/python -m pytest tests/ -q
cd frontend && npx vitest run && npm run build
3. **Before every push**, run the three gates CI runs (in VS Code: the
**Check: naming + tests + build** task). By hand in PowerShell:
```powershell
venv\Scripts\python -m pytest tests/ -q
cd frontend; npx vitest run; npm run build; cd ..
bash scripts/check-naming-and-style.sh # naming - runs via Git Bash
```
4. **Push your branch and open a Pull Request** against `main`:
```bash
@@ -1179,4 +1190,4 @@ for a contributor:
| List/Detail master templates | `PrintersList.vue`, `PrinterDetail.vue` |
| Reports hook + CSV | `plugins/warranty/` + `shopdb/core/api/reports.py` |
| Permissions declaration | `plugins/usb/plugin.py::get_permissions` |
| The finished plugin itself | branch `feat/printedparts-plugin`, tags `lab-stage-01..16` |
| The finished plugin itself | branch `feat/printedparts-plugin`, tags `lab-stage-01..17` |

View File

@@ -7,7 +7,7 @@ For the full hook reference, see [PLUGIN-HOOKS.md](PLUGIN-HOOKS.md).
> **Windows / VS Code:** command examples below use the Linux venv path
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
For the architectural decisions behind the contract, see [docs/adr/](../docs/adr/).

View File

@@ -62,7 +62,7 @@ which revisions each plugin has applied in `migrations_applied`.
For sister-site plugins (per [ADR-003](adr/ADR-003-plugin-distribution.md)):
- Plugin lives in its own git repo: `gitea.proudtech.net/<your-site>/<pluginname>`
- Plugin lives in its own git repo: `<git-host>/<your-site>/<pluginname>`
- Adopting site clones or symlinks into their `<repo>/plugins/<name>/`
- Plugin manifest declares `core_version` range matching the framework version they target
- Plugin readme explains: what it tracks, who maintains it, where to file issues

View File

@@ -145,4 +145,4 @@ Migration path:
- `shopdb/plugins/base.py` (`get_collector_schema` + `apply_collector_payload` hooks)
- ADR-001 (asset model the collectors target)
- ADR-002 (collector schema is part of plugin contract; changes to the hook signature are major bumps)
- The PXE project (`/home/camp/projects/pxe/`) which feeds the computers collector
- The PXE project (the PXE imaging project) which feeds the computers collector