Wave one complete. Three cards, no new data and no migrations.
Printer supplies reuses the existing low-supplies query and its five-minute
cache; a Zabbix round-trip per printer on every dashboard load would make this
the slowest page in the app. One row per printer listing every depleted
cartridge, criticals first - a row per cartridge would report one printer three
times and read as three problems, and showing only the worst class would hide a
low cartridge behind a critical one on the same machine when whoever walks out
there wants to carry both.
While there: the low-supplies REPORT itself was including healthy cartridges. A
printer with one empty black and three full colour ones listed all four, so the
reader had to find the problem inside the row. It now lists only what needs
replacing, and the test that asserted the old behaviour now asserts the new.
Expiring warranties keeps already-expired entries on the list rather than
dropping them the day they lapse, which is how they get missed. Horizon is
warranty_expiringdays, default 90, because that suits a site budgeting
quarterly and nobody else.
Mis-numbered bays promotes check-shared-machines out of a CLI command nobody
will remember to run - it found seven bays that had been wrong for weeks. It
reports only numbers with NO child assets, so part markers legitimately sharing
an operation stay silent: that distinction is the whole card, and without it it
would list correct data beside faults and be ignored.
Printers also loses its dead component-named widget; notifications, network and
machines still have theirs.
A shopfloor PC is bought, warranted and replaced as a PC, but it is FOUND by
the machine it drives - nobody walks the floor looking for an asset number. The
warranty tables listed the covered asset and left the reader to work out where
that is.
Both tables gain a Machine # column. The payload resolves it by walking the
asset relationship graph in BOTH directions: the canonical edge is
PC --controls--> machine, but a dual-bay pair carries controls on both bays and
hand-made links are not reliably oriented.
Hovering the chip shows the floor map with the machine marked, so the row
answers "where do I go" without opening anything. The blueprint follows the
viewer's theme and the marker is placed from mapx/mapy as a percentage of the
configured map dimensions, since the preview is a few hundred pixels wide
rather than the full plan. It renders only while hovered, so a long table does
not build a blueprint per row.
A machine with no map position still gets its chip and says so, rather than
being dropped: against real data 142 PCs resolve to a machine and 125 of those
are placed, so 17 rows would otherwise have silently lost their number.
The chip is deliberately not a link. /machines/:id is keyed by machineid, not
assetid, and resolving one to the other here would make the warranty plugin
import the machines plugin (ADR-014). Worth noting separately: the existing
assetLink() in these tables already sends machine-type assets to
/machines/<assetid>, which is that same mismatch and predates this change.
The bulk /sync/dell reuse check only matched an existing warranty when its
provider was exactly 'dell'. Warranties added by hand or via import default to
provider 'manual', so re-check-all did not recognize them and created a brand
new Dell warranty for every asset - duplicating the whole set.
Broaden the reuse match to treat a warranty as Dell by any signal (provider,
matching service tag, or a 'Dell' vendor), and canonicalize the reused row to
provider 'dell' so later re-checks match by provider and never duplicate.
- Perf: list_warranties did a db.session.get(Asset) per link per warranty
(~1.8s for the full list). Eager-load the links and batch-fetch every linked
asset in one query -> ~0.19s.
- Filters: the "Status" label wrapped its select onto a second line, so the
dropdown sat above the search box; keep the label inline so they align.
- Covers: each asset chip now shows the asset name (often the hostname/alias) on
hover, keeping the machine number as the label.
Goal: an LLM or script can migrate an entire legacy database using only
the HTTP API - original history preserved, safely re-runnable.
- X-Import-Mode header (admin only): create/update endpoints across 15
timestamped entity types accept original createddate/modifieddate;
helper exposed via shopdb.api (contract 0.7.0 -> 0.8.0).
- Exact-match natural-key lookup filters on 13 list endpoints for the
lookup-then-upsert recipe.
- Selfhosted USB checkout/checkin accept backdated event times in
import mode.
- docs/IMPORT-API.md: operator manual grounded in the real legacy
schema - order of operations, full table-by-table mapping including
the machines fan-out, idempotent Python importer with dry-run, parity
checks, and decided dispositions for unmigrated tables (DNC config
stays live-fed via the collector; supportteams/appowners map to the
upcoming supportteams model).
635 tests pass; naming green; frontend untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Make the app distributable to other GE Aerospace sites (one self-hosted
instance per site, ADR-004). GE values remain the shipped defaults; every
site-specific behavior is now a Setting an admin can change in the UI.
Settings-driven site config:
- Branding: site/QR/badge logos, favicon, primary color (upload endpoints
mirror the map-blueprint pattern; new Settings > Branding section).
- ServiceNow: search/incident/change URL templates ({ticket}), ticket
prefixes, enable toggle. Defaults point at the current
geaerospaceqa.service-now.com global search. Disabled = plain-text tickets.
- Employee-id regex (employeeid_pattern), printer hostname template,
QR label targets (qr_target_printer / qr_target_usb, blank = asset page,
else URL template with placeholders), usb_label_style (barcode|qr).
- West Jefferson floor-plan PNGs removed from the tree; generic placeholder
ships as the map default and sites upload their own blueprint.
Security closeout:
- dashboarddefaults writes now require admin.
- Collector: generic error messages (no str(exc) leak); API key accepted
via X-API-Key header only (BREAKING: querystring api_key removed).
- IP-based login rate limiting (AUTH_RATELIMIT_* knobs) atop account lockout.
- Setting.set() creation race fixed (IntegrityError retry).
Release engineering and docs:
- __version__ 0.5.0 (distinct from __contract_version__, ADR-007),
CHANGELOG.md, Gitea Actions CI config, frontend version aligned.
- One wizard-first install story across README/DEPLOY; new CONFIG.md,
UPGRADE.md, BACKUP-RESTORE.md; CLAUDE.md and ROADMAP de-staled.
- Dockerfile multi-stage build now bundles the frontend; compose binds
MySQL to 127.0.0.1; stale database/schema.sql and one-off SQL removed.
Debt and fixes:
- .query.get() -> db.session.get() sweep; datetime.utcnow() removed
(naive-UTC via timezone-aware now); users.py on authz decorators.
- Fixed 4 stale tests (slides feed shape, shopfloor splitperemployee,
plugin contract purity) and the USB label page field mapping (both usb
modes emit the cmmc shape: device_id/device_desc).
- Health endpoint reports the real version.
248 tests pass; naming/style check green; frontend builds; fresh-DB
flask db upgrade + seeds verified; QR targets verified by decoding
rendered codes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Shared utils/apiError.js reads the correct nested error message (with
fallbacks); swept 37 views/components off the shallow path so real backend
messages (e.g. in-use 409s) surface instead of generic "Failed".
- useWarrantyBadge composable: warranty hero status/end-date badge now on PC,
equipment, printer, and network detail heroes (one shared fetch feeds the
badge + the WarrantyPanel).
- Warranties list: client-side search (vendor/level/tag/asset) + pagination;
truncate long service levels so they stop blowing out the table width.
- "Re-check all" button + POST /warranty/sync/dell?all=true to re-pull dated
Dell warranties, not just missing ones.
- Deprecate the standalone pxe-images/warranty_sync.py in favor of the plugin.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- POST /warranty/sync/dell: auto-detect Dell hardware by service tag (asset
serial) and create warranties for the ones Dell recognizes, in batches of 100.
Only looks up assets that lack a dated warranty, so re-runs are cheap and
non-Dell serials are filtered out by Dell. DellProvider.bulk_lookup batches
tags over the cached token.
- "Sync Dell" button on the Warranties page with a toast summary.
- WarrantyPanel "Add one / Add-manage" links deep-link to the warranties add
modal pre-linked to that asset (?addfor=); WarrantiesList opens it prefilled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- DellProvider: real Dell TechDirect lookup. OAuth2 via HTTP Basic auth,
asset-entitlements under /PROD/sbil/eapi/v5 (the device.warranty path 404s
for this account), map latest dated entitlement to service level + dates.
Cache the token process-wide; Dell rate-limits the token endpoint and a
fresh request per refresh trips a 401 cooldown. Verified against live Dell.
- Lenovo/HP stay config-shaped stubs.
- Settings: warranty_dell_* keys (category integrations); Dell Warranty Lookup
block in System Settings > Integrations (enable + client id/secret masked +
optional token/API URL overrides).
- WarrantyPanel takes optional pre-fetched items; PCDetail fetches once and
feeds both the panel and a new hero warranty-status/end-date badge.
- tools/mock_dell.py for offline testing of the provider flow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Feature work from the 2026-07 session:
Settings IA
- Replace the flat 27-card settings hub with a persistent two-pane shell
(SettingsLayout.vue): grouped, searchable left rail + content pane.
- Nest all settings/* routes under the shell via router post-processing;
shared nav catalog in settingsNav.js. Group by asset class (PCs, Printers,
Equipment, Network) so per-type settings stop scattering.
Custom fields (core)
- customfields + customfieldvalues tables (migration 7d14), CRUD API at
/api/customfields, per-asset value get/save.
- Settings management page + reusable CustomFieldsSection (detail) and
CustomFieldsInputs (form) wired into all four asset types.
Warranty (new plugin)
- plugins/warranty: warranties + warrantyassets (migration 7d15), derived
coverage status, provider abstraction (manual now; Dell/Lenovo/HP stubs).
- API CRUD + per-asset panel + report buckets; WarrantyPanel on all four
detail pages; Warranties management page; Warranty report + Reports card.
- Seed warranty.* permissions.
Printer drivers
- printerdrivers table (migration 7d13) linked to printer models; drivers now
surface on the matching printer's detail page.
Other
- PCDetail rebalanced (Network + Status + Warranty + custom fields on the right).
- Rename PCs list "Features" column to "Remote Access"; fix badge hover underline.
- Drop equipment islocationonly field.
- Centralize asset-type label/route maps into utils/assetTypes.js.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>