#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" # LinuxCNC source basis: this guard ties the six active workstream contexts to # their source-map guards and to LinuxCNC source/config anchors. It validates # coordination policy only; it does not add CNC behavior or expand smoke # parsing. linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} linuxcnc_root=$(cd "$linuxcnc_root" && pwd) grep -F '1. `switchkins/remap` generator and tables' docs/architecture.md >/dev/null grep -F '2. `linuxcnc-kinematics` source manifest' docs/architecture.md >/dev/null grep -F '3. WASM filesystem and OPFS persistence' docs/architecture.md >/dev/null grep -F '4. browser app integration' docs/architecture.md >/dev/null grep -F '5. Node and browser smoke coverage' docs/architecture.md >/dev/null grep -F '6. source-link, build, and documentation constraints' docs/architecture.md >/dev/null grep -F 'The synchronized source-map guard for these contexts is' docs/architecture.md >/dev/null grep -F '`docs/linuxcnc-workstreams-source-map.md`.' docs/architecture.md >/dev/null grep -F 'REMAP = M428' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" >/dev/null grep -F '#' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" >/dev/null grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \ "$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \ "$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \ "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null grep -F 'int Interp::save_parameters(const char *filename,' \ "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null grep -F 'LIBRS274SRCS := $(addprefix emc/rs274ngc/,' \ "$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \ "$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null grep -F 'LIBLCNCINISRCS := emc/ini/inifile.cc' \ "$linuxcnc_root/src/emc/ini/Submakefile" >/dev/null grep -F 'LIBPPSRCS := $(addprefix emc/pythonplugin/,' \ "$linuxcnc_root/src/emc/pythonplugin/Submakefile" >/dev/null grep -F 'LinuxCNC workstreams source map' docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'must not add CNC' docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'temporary smoke parser' docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'Every active workstream must have at least one source-map document and guard' \ docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'docs/linuxcnc-source-policy.md` must keep the same one-context workflow' \ docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'Browser filesystem persistence remains OPFS-only.' docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'Smoke coverage remains verification only' docs/linuxcnc-workstreams-source-map.md >/dev/null grep -F 'Keep the change inside one active workstream context unless a dependency is' \ docs/linuxcnc-source-policy.md >/dev/null grep -F 'Each pass should advance one reasonable, reviewable context.' \ docs/linuxcnc-source-policy.md >/dev/null grep -F 'Keep the six active workstream contexts synchronized with' \ docs/linuxcnc-source-policy.md >/dev/null grep -F '`docs/linuxcnc-workstreams-source-map.md` so source-map guards' \ docs/linuxcnc-source-policy.md >/dev/null grep -F 'Smoke tests are verification guardrails. They must not be expanded with new' \ docs/linuxcnc-source-policy.md >/dev/null grep -F 'Do not mix generator/table work, manifests, OPFS/browser work, and build' \ docs/linuxcnc-source-policy.md >/dev/null grep -F 'Browser-side wasm filesystem behavior must use OPFS-backed storage.' \ docs/linuxcnc-source-policy.md >/dev/null python3 - <<'PY' from pathlib import Path doc = Path("docs/linuxcnc-workstreams-source-map.md").read_text(encoding="utf-8") policy = Path("docs/linuxcnc-source-policy.md").read_text(encoding="utf-8") expected = { "switchkins/remap": [ "docs/linuxcnc-switchkins-remap-source-map.md", "check-linuxcnc-switchkins-remap-source-map.sh", ], "linuxcnc-kinematics": [ "docs/linuxcnc-kinematics-source-map.md", "check-linuxcnc-kinematics-source-map.sh", ], "WASM filesystem and OPFS persistence": [ "docs/linuxcnc-opfs-source-map.md", "docs/linuxcnc-wasm-shims-source-map.md", "docs/linuxcnc-wasm-blockers-source-map.md", "docs/linuxcnc-tooldata-source-map.md", "check-linuxcnc-opfs-source-map.sh", "check-linuxcnc-wasm-shims-source-map.sh", "check-linuxcnc-wasm-blockers-source-map.sh", "check-linuxcnc-tooldata-source-map.sh", ], "browser app integration": [ "docs/linuxcnc-browser-app-source-map.md", "check-linuxcnc-browser-app-source-map.sh", ], "Node and browser smoke coverage": [ "docs/linuxcnc-wasm-smoke-source-map.md", "check-linuxcnc-wasm-smoke-source-map.sh", ], "source-link, build, and documentation constraints": [ "docs/linuxcnc-rs274-source-map.md", "docs/linuxcnc-build-source-map.md", "docs/linuxcnc-tooldata-source-map.md", "check-linuxcnc-rs274-source-map.sh", "check-linuxcnc-build-source-map.sh", "check-linuxcnc-tooldata-source-map.sh", ], } for workstream, paths in expected.items(): if workstream not in doc: raise SystemExit(f"missing workstream row in docs/linuxcnc-workstreams-source-map.md: {workstream}") policy_terms = { "switchkins/remap": ["switchkins/remap", "M428/M429/M430"], "linuxcnc-kinematics": ["kinematics manifest", "RTCP coverage"], "WASM filesystem and OPFS persistence": ["OPFS", "filesystem"], "browser app integration": ["Browser app", "wasm bridge"], "Node and browser smoke coverage": ["Smoke tests are verification guardrails", "temporary smoke parser"], "source-link, build, and documentation constraints": ["source-link", "build-policy"], }[workstream] if not any(term in policy for term in policy_terms): raise SystemExit(f"docs/linuxcnc-source-policy.md no longer references workstream boundary: {workstream}") for path in paths: if path not in doc: raise SystemExit(f"missing source-map path in workstream index: {path}") if not Path(path).exists(): raise SystemExit(f"source-map path listed in workstream index does not exist: {path}") if path.startswith("check-") and not Path(path).stat().st_mode & 0o111: raise SystemExit(f"source-map guard is not executable: {path}") for test_entry in ["test-native.sh", "test-linuxcnc-source-link.sh", "build-wasm.sh"]: text = Path(test_entry).read_text(encoding="utf-8") for guard in sorted({path for paths in expected.values() for path in paths if path.startswith("check-")}): if f"./{guard}" not in text: raise SystemExit(f"{test_entry} does not run workstream source-map guard: {guard}") PY