@techierathore/techieflow 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.claude/commands/TechieFlow/agents/analyst.md +5 -8
  2. package/.claude/commands/TechieFlow/agents/architect.md +1 -10
  3. package/.claude/commands/TechieFlow/agents/flow-master.md +1 -0
  4. package/.claude/commands/TechieFlow/agents/verifier.md +1 -0
  5. package/.claude/commands/TechieFlow/tasks/_owner-language.md +27 -0
  6. package/.claude/commands/TechieFlow/tasks/_smoke-test-policy.md +1 -1
  7. package/.claude/commands/TechieFlow/tasks/_status-update-gate.md +4 -3
  8. package/.claude/commands/TechieFlow/tasks/amend-docs.md +6 -5
  9. package/.claude/commands/TechieFlow/tasks/build-phase.md +1 -1
  10. package/.claude/commands/TechieFlow/tasks/create-deep-research-prompt.md +4 -0
  11. package/.claude/commands/TechieFlow/tasks/create-doc.md +4 -0
  12. package/.claude/commands/TechieFlow/tasks/create-project-brief.md +22 -0
  13. package/.claude/commands/TechieFlow/tasks/day1-brownfield.md +3 -3
  14. package/.claude/commands/TechieFlow/tasks/day1-greenfield.md +3 -3
  15. package/.claude/commands/TechieFlow/tasks/facilitate-brainstorming-session.md +4 -0
  16. package/.claude/commands/TechieFlow/tasks/fix-issues.md +2 -2
  17. package/.claude/commands/TechieFlow/tasks/generate-html.md +7 -3
  18. package/.claude/commands/TechieFlow/tasks/handoff-phase.md +20 -18
  19. package/.claude/commands/TechieFlow/tasks/metrics-report.md +2 -0
  20. package/.claude/commands/TechieFlow/tasks/mockups.md +1 -1
  21. package/.claude/commands/TechieFlow/tasks/productguide.md +2 -2
  22. package/.claude/commands/TechieFlow/tasks/render-workflow-docs.md +1 -1
  23. package/.claude/commands/TechieFlow/tasks/triage-issues.md +2 -2
  24. package/.claude/commands/TechieFlow/tasks/verify-phase.md +1 -1
  25. package/.opencode/plugin/techieflow.js +181 -9
  26. package/.tfcore/agents/analyst.md +5 -8
  27. package/.tfcore/agents/architect.md +1 -10
  28. package/.tfcore/agents/flow-master.md +1 -0
  29. package/.tfcore/agents/verifier.md +1 -0
  30. package/.tfcore/hooks/block-git.sh +4 -2
  31. package/.tfcore/hooks/guard-build.sh +5 -1
  32. package/.tfcore/hooks/guard-db.sh +6 -4
  33. package/.tfcore/hooks/guard-metrics.sh +13 -1
  34. package/.tfcore/hooks/guard-status-html.sh +86 -2
  35. package/.tfcore/hooks/guard-verify-deps.sh +124 -0
  36. package/.tfcore/hooks/guard-verify.sh +8 -3
  37. package/.tfcore/hooks/sweep-artifacts.sh +46 -0
  38. package/.tfcore/routing.yaml +61 -0
  39. package/.tfcore/standards/owner-words.txt +23 -0
  40. package/.tfcore/tasks/_owner-language.md +27 -0
  41. package/.tfcore/tasks/_smoke-test-policy.md +1 -1
  42. package/.tfcore/tasks/_status-update-gate.md +4 -3
  43. package/.tfcore/tasks/amend-docs.md +6 -5
  44. package/.tfcore/tasks/build-phase.md +1 -1
  45. package/.tfcore/tasks/create-deep-research-prompt.md +4 -0
  46. package/.tfcore/tasks/create-doc.md +4 -0
  47. package/.tfcore/tasks/create-project-brief.md +22 -0
  48. package/.tfcore/tasks/day1-brownfield.md +3 -3
  49. package/.tfcore/tasks/day1-greenfield.md +3 -3
  50. package/.tfcore/tasks/facilitate-brainstorming-session.md +4 -0
  51. package/.tfcore/tasks/fix-issues.md +2 -2
  52. package/.tfcore/tasks/generate-html.md +7 -3
  53. package/.tfcore/tasks/handoff-phase.md +20 -18
  54. package/.tfcore/tasks/metrics-report.md +2 -0
  55. package/.tfcore/tasks/mockups.md +1 -1
  56. package/.tfcore/tasks/productguide.md +2 -2
  57. package/.tfcore/tasks/render-workflow-docs.md +1 -1
  58. package/.tfcore/tasks/triage-issues.md +2 -2
  59. package/.tfcore/tasks/verify-phase.md +1 -1
  60. package/.tfcore/telemetry/SCHEMA.md +61 -1
  61. package/.tfcore/telemetry/install-metrics.sh +3 -2
  62. package/.tfcore/telemetry/tf-metrics.sh +435 -21
  63. package/.tfcore/templates/v4custom/app-agents-md-tmpl.md +6 -0
  64. package/.tfcore/templates/v4custom/app-brd-tmpl.md +5 -3
  65. package/.tfcore/templates/v4custom/app-brief-tmpl.md +65 -0
  66. package/.tfcore/templates/v4custom/app-decision-request-tmpl.md +85 -0
  67. package/.tfcore/templates/v4custom/app-library-feedback-tmpl.md +86 -31
  68. package/.tfcore/templates/v4custom/app-phase-brd-tmpl.md +94 -0
  69. package/.tfcore/templates/v4custom/app-phase-uidesign-tmpl.md +60 -0
  70. package/.tfcore/templates/v4custom/app-uidesign-tmpl.md +4 -2
  71. package/.tfcore/templates/v4custom/build-subagent-prompt.md +2 -2
  72. package/.tfcore/templates/v4custom/html-render-shell.md +4 -2
  73. package/.tfcore/utils/__pycache__/tf-brd-status.cpython-312.pyc +0 -0
  74. package/.tfcore/utils/__pycache__/tf-checklist-edit.cpython-312.pyc +0 -0
  75. package/.tfcore/utils/__pycache__/tf-day1-files.cpython-312.pyc +0 -0
  76. package/.tfcore/utils/__pycache__/tf-devguide-list.cpython-312.pyc +0 -0
  77. package/.tfcore/utils/__pycache__/tf-doc-check.cpython-312.pyc +0 -0
  78. package/.tfcore/utils/__pycache__/tf-log-miss.cpython-312.pyc +0 -0
  79. package/.tfcore/utils/__pycache__/tf-misses-md.cpython-312.pyc +0 -0
  80. package/.tfcore/utils/__pycache__/tf-mockups-locate.cpython-312.pyc +0 -0
  81. package/.tfcore/utils/__pycache__/tf-opencode-audit.cpython-312.pyc +0 -0
  82. package/.tfcore/utils/__pycache__/tf-perf-grade.cpython-312.pyc +0 -0
  83. package/.tfcore/utils/__pycache__/tf-render-html.cpython-312.pyc +0 -0
  84. package/.tfcore/utils/__pycache__/tf-split-brd.cpython-312.pyc +0 -0
  85. package/.tfcore/utils/__pycache__/tf-status-facts.cpython-312.pyc +0 -0
  86. package/.tfcore/utils/__pycache__/tf-triage.cpython-312.pyc +0 -0
  87. package/.tfcore/utils/__pycache__/tf-verify-list.cpython-312.pyc +0 -0
  88. package/.tfcore/utils/__pycache__/tf-verify-verdict.cpython-312.pyc +0 -0
  89. package/.tfcore/utils/tf-assets-browser.mjs +96 -0
  90. package/.tfcore/utils/tf-assets.sh +97 -12
  91. package/.tfcore/utils/tf-brd-status.py +14 -5
  92. package/.tfcore/utils/tf-build-list.py +99 -11
  93. package/.tfcore/utils/tf-build-list.sh +5 -3
  94. package/.tfcore/utils/tf-build.sh +88 -15
  95. package/.tfcore/utils/tf-ci-repro.py +685 -0
  96. package/.tfcore/utils/tf-ci-repro.sh +24 -0
  97. package/.tfcore/utils/tf-devguide-list.py +47 -22
  98. package/.tfcore/utils/tf-doc-check.py +242 -10
  99. package/.tfcore/utils/tf-doc-tests.sh +79 -0
  100. package/.tfcore/utils/tf-emit.sh +413 -19
  101. package/.tfcore/utils/tf-feedback.sh +13 -0
  102. package/.tfcore/utils/tf-fix-close.sh +113 -31
  103. package/.tfcore/utils/tf-gitignore-audit.sh +50 -3
  104. package/.tfcore/utils/tf-goal.sh +105 -18
  105. package/.tfcore/utils/tf-harness.sh +9 -4
  106. package/.tfcore/utils/tf-lock.sh +29 -0
  107. package/.tfcore/utils/tf-log-miss.py +34 -8
  108. package/.tfcore/utils/tf-login.mjs +167 -0
  109. package/.tfcore/utils/tf-misses-md.py +24 -21
  110. package/.tfcore/utils/tf-misses-md.sh +5 -4
  111. package/.tfcore/utils/tf-mockup-parity.mjs +427 -63
  112. package/.tfcore/utils/tf-mockup-parity.sh +14 -2
  113. package/.tfcore/utils/tf-model-pick.sh +475 -0
  114. package/.tfcore/utils/tf-owner-text.py +233 -0
  115. package/.tfcore/utils/tf-owner-text.sh +14 -0
  116. package/.tfcore/utils/tf-phase.sh +33 -1
  117. package/.tfcore/utils/tf-portable.sh +230 -0
  118. package/.tfcore/utils/tf-render-html.py +80 -2
  119. package/.tfcore/utils/tf-routing-bind.sh +87 -1
  120. package/.tfcore/utils/tf-routing.sh +7 -0
  121. package/.tfcore/utils/tf-selfcheck.sh +356 -0
  122. package/.tfcore/utils/tf-split-brd.py +17 -2
  123. package/.tfcore/utils/tf-status-evidence.sh +11 -9
  124. package/.tfcore/utils/tf-status-facts.py +133 -44
  125. package/.tfcore/utils/tf-triage.py +62 -16
  126. package/.tfcore/utils/tf-triage.sh +2 -0
  127. package/.tfcore/utils/tf-verify-boot.sh +263 -43
  128. package/.tfcore/utils/tf-verify-emit.sh +20 -0
  129. package/.tfcore/utils/tf-verify-env.sh +33 -4
  130. package/.tfcore/utils/tf-verify-list.py +75 -8
  131. package/.tfcore/utils/tf-verify-screens.mjs +254 -43
  132. package/.tfcore/utils/tf-verify-screens.sh +18 -4
  133. package/.tfcore/utils/tf-verify-tests.sh +272 -28
  134. package/.tfcore/utils/tf-verify-verdict.py +59 -11
  135. package/.tfcore/utils/tf_defect.py +27 -0
  136. package/.tfcore/utils/tf_feedback.py +209 -0
  137. package/.tfcore/utils/tf_roadmap.py +35 -0
  138. package/README.md +19 -5
  139. package/docs/TechieFlow-Installation.md +5 -3
  140. package/opencode.jsonc +24 -0
  141. package/package.json +1 -1
  142. package/scripts/install.mjs +4 -0
  143. package/.tfcore/templates/architecture-tmpl.yaml +0 -650
  144. package/.tfcore/templates/brownfield-architecture-tmpl.yaml +0 -476
  145. package/.tfcore/templates/competitor-analysis-tmpl.yaml +0 -306
  146. package/.tfcore/templates/front-end-architecture-tmpl.yaml +0 -218
  147. package/.tfcore/templates/fullstack-architecture-tmpl.yaml +0 -823
  148. package/.tfcore/templates/market-research-tmpl.yaml +0 -252
  149. package/.tfcore/templates/project-brief-tmpl.yaml +0 -221
  150. package/.tfcore/utils/techieflow-doc-template.md +0 -325
@@ -6,7 +6,9 @@
6
6
  # bash .tfcore/utils/tf-build.sh [build] [<target>] [-- <extra dotnet args>]
7
7
  # bash .tfcore/utils/tf-build.sh test [<target>] [-- <extra dotnet args>]
8
8
  # bash .tfcore/utils/tf-build.sh run <project> [-- <extra dotnet args>]
9
+ # bash .tfcore/utils/tf-build.sh publish <project> -- -o <dir> [-c <config>] # tf-verify-boot.sh's
9
10
  # bash .tfcore/utils/tf-build.sh probe # print the platform and the rungs, run nothing
11
+ # bash .tfcore/utils/tf-build.sh --print [build|test] # print the command this repository uses, run nothing
10
12
  #
11
13
  # <target> is a .sln, .slnx or .csproj relative to the current folder (default: the one
12
14
  # solution or project found here). Output goes to tests/.artifacts/build/<UTC time>.log.
@@ -27,14 +29,28 @@
27
29
  # A workload / SDK-missing error (NETSDK1178, Microsoft.iOS/Android/MacCatalyst.Sdk, Workload ID,
28
30
  # WindowsAppSDK, command not found) means the wrong rung: the next one is tried, nothing is logged
29
31
  # as a blocker. A CS#### error means the code is wrong: the rung stays and the script exits 1.
32
+ # A locked output file (MSB3021, MSB3027, "being used by another process") is neither: the same
33
+ # rung waits and tries again, then says NOT-RUN naming the lock; it never falls to the next rung
34
+ # (TF-035). On WSL a build that changes side -- a WSL rung, then a Windows one, over the same obj/,
35
+ # in this run or since the last -- first clears obj/**/scopedcss: a Blazor build names each scoped
36
+ # stylesheet by a hash of the path, the two sides hash differently, and the incremental target
37
+ # kept the old names, so every page's own styles stopped applying (TfLens, 2026-09-11).
38
+ # One build, test or publish at a time per repository: a second one waits for the first
39
+ # (tests/.artifacts/build/.lock names who holds it). Builders side by side each rebuilt the same
40
+ # obj/ under the others: a running app lost its stylesheet or its process, and a publish carried a
41
+ # dll older than its own source (TF-043). A lock whose process has gone is taken over.
30
42
  set -u
43
+ # mapfile and the other bash-4 / GNU-only spellings go through the shim (a stock Mac has bash 3.2).
44
+ source "$(dirname "${BASH_SOURCE[0]}")/tf-portable.sh"
31
45
 
32
- MODE="build"; TARGET=""; EXTRA=()
46
+ MODE="build"; TARGET=""; EXTRA=(); PRINT=0
47
+ [[ "${1:-}" == "--print" ]] && { PRINT=1; shift; } # print the resolved command, run nothing (TfLens TF-049)
33
48
  case "${1:-}" in
34
- build|test|run|probe) MODE="$1"; shift ;;
49
+ build|test|run|publish|probe) MODE="$1"; shift ;;
35
50
  esac
36
51
  while [[ $# -gt 0 ]]; do
37
52
  if [[ "$1" == "--" ]]; then shift; EXTRA=("$@"); break; fi
53
+ if [[ "$1" == "--print" ]]; then PRINT=1; shift; continue; fi
38
54
  TARGET="$1"; shift
39
55
  done
40
56
 
@@ -48,10 +64,11 @@ elif [[ "$U" == Linux* ]]; then
48
64
  fi
49
65
  elif [[ "$U" == windows || "$U" == MINGW* || "$U" == MSYS* || "$U" == CYGWIN* ]]; then PLATFORM="windows"
50
66
  fi
67
+ PLATFORM="${TF_BUILD_PLATFORM:-$PLATFORM}" # the self-tests only
51
68
 
52
69
  # ---- target ---------------------------------------------------------------------------
53
70
  if [[ -z "$TARGET" && "$MODE" != "probe" ]]; then
54
- mapfile -t CANDS < <(ls -1 *.slnx *.sln 2>/dev/null; ls -1 *.csproj 2>/dev/null)
71
+ tf_read_lines CANDS < <(ls -1 *.slnx *.sln 2>/dev/null; ls -1 *.csproj 2>/dev/null)
55
72
  if [[ ${#CANDS[@]} -eq 1 ]]; then TARGET="${CANDS[0]}"
56
73
  elif [[ ${#CANDS[@]} -gt 1 ]]; then echo "NOT-RUN more than one solution or project here; name one: ${CANDS[*]}"; exit 2
57
74
  fi
@@ -63,14 +80,14 @@ projects=()
63
80
  if [[ -n "$TARGET" ]]; then
64
81
  case "$TARGET" in
65
82
  *.csproj) projects=("$TARGET") ;;
66
- *.sln) mapfile -t projects < <(grep -oE '"[^"]+\.csproj"' "$TARGET" 2>/dev/null | tr -d '"' | tr '\\' '/') ;;
67
- *.slnx) mapfile -t projects < <(grep -oE 'Path="[^"]+\.csproj"' "$TARGET" 2>/dev/null | sed 's/Path="//; s/"$//' | tr '\\' '/') ;;
83
+ *.sln) tf_read_lines projects < <(grep -oE '"[^"]+\.csproj"' "$TARGET" 2>/dev/null | tr -d '"' | tr '\\' '/') ;;
84
+ *.slnx) tf_read_lines projects < <(grep -oE 'Path="[^"]+\.csproj"' "$TARGET" 2>/dev/null | sed 's/Path="//; s/"$//' | tr '\\' '/') ;;
68
85
  esac
69
86
  base="$(dirname "$TARGET")"
70
- for p in "${projects[@]}"; do
87
+ for p in ${projects[@]+"${projects[@]}"}; do # the +form: an empty array under set -u (bash 3.2)
71
88
  f="$p"; [[ -f "$f" ]] || f="$base/$p"
72
89
  [[ -f "$f" ]] || continue
73
- if grep -qiE '<UseMaui>\s*true|Microsoft\.NET\.Sdk\.Maui|net[0-9.]+-(android|ios|maccatalyst|windows10)' "$f"; then maui=1; fi
90
+ if grep -qiE '<UseMaui>[[:space:]]*true|Microsoft\.NET\.Sdk\.Maui|net[0-9.]+-(android|ios|maccatalyst|windows10)' "$f"; then maui=1; fi
74
91
  done
75
92
  fi
76
93
 
@@ -96,10 +113,53 @@ verb="$MODE"
96
113
  args=("$verb")
97
114
  [[ -n "$TARGET" ]] && args+=("$TARGET")
98
115
  [[ "$MODE" == "run" ]] && args=("run" "--project" "$TARGET")
99
- args+=("${EXTRA[@]}")
116
+ args+=(${EXTRA[@]+"${EXTRA[@]}"})
117
+ [[ $PRINT -eq 1 ]] && { echo "dotnet ${args[*]}"; exit 0; }
100
118
 
101
119
  mkdir -p tests/.artifacts/build
102
- LOG="tests/.artifacts/build/$(date -u +%Y%m%dT%H%M%SZ)-$MODE.log"
120
+ LOG="tests/.artifacts/build/$(date -u +%Y%m%dT%H%M%SZ)-$MODE-$$.log" # two builds in one second never share a log
121
+
122
+ # ---- one build or browser check at a time (TF-043, TF-048): tf-lock.sh. `run` does not take it.
123
+ source "$(dirname "${BASH_SOURCE[0]}")/tf-lock.sh"
124
+ LOCK="$TF_LOCK"
125
+ [[ "$MODE" != "run" ]] && tf_take_lock "$MODE"
126
+ LOCKED='error MSB302[17]|being used by another process|Access to the path .* is denied'
127
+ # which side of a WSL machine a rung builds on, and the project folders whose obj/ it writes
128
+ side_of() { case "$1" in winrun|cmd.exe|powershell.exe) echo windows ;; *) echo wsl ;; esac; }
129
+ # the projects the target builds, and every project they reference: a web head's static assets come
130
+ # from the library it references, whose obj/ the other side may have written (AppManager TF-004)
131
+ tf_read_lines pdirs < <(python3 - "${base:-.}" ${projects[@]+"${projects[@]}"} <<'PY'
132
+ import os, re, sys
133
+ base, todo, seen = sys.argv[1], [], []
134
+ for p in sys.argv[2:]:
135
+ todo.append(p if os.path.isfile(p) else os.path.join(base, p))
136
+ while todo:
137
+ f = os.path.normpath(todo.pop())
138
+ if not os.path.isfile(f) or os.path.dirname(f) in seen:
139
+ continue
140
+ seen.append(os.path.dirname(f) or ".")
141
+ for ref in re.findall(r'<ProjectReference\s+Include="([^"]+)"', open(f, encoding="utf-8", errors="replace").read()):
142
+ todo.append(os.path.join(os.path.dirname(f), ref.replace("\\", "/")))
143
+ print("\n".join(seen))
144
+ PY
145
+ )
146
+ cross_side() { # side: clear what another side built with its own paths in it, then mark this side
147
+ local d cleared=0
148
+ for d in ${pdirs[@]+"${pdirs[@]}"}; do
149
+ [[ -d "$d/obj" ]] || continue
150
+ if [[ "$(cat "$d/obj/.tf-build-side" 2>/dev/null)" != "$1" ]]; then
151
+ # the scoped stylesheets (TF-035) and the static web asset lists: each side writes its own absolute
152
+ # paths into those, and a Windows-written one (461 C:\ paths in AppManagerUI's) stopped the WSL
153
+ # host at startup with "The path must be absolute (Parameter 'root')" (AppManager TF-004)
154
+ if [[ -n "$(find "$d/obj" -type d -name scopedcss -print -quit 2>/dev/null)" || -n "$(find "$d/obj" "$d/bin" -name '*staticwebassets*.json' -print -quit 2>/dev/null)" ]]; then
155
+ find "$d/obj" -type d -name scopedcss -prune -exec rm -rf {} + 2>/dev/null
156
+ find "$d/obj" "$d/bin" -name '*staticwebassets*.json' -delete 2>/dev/null; cleared=1
157
+ fi
158
+ fi
159
+ echo "$1" > "$d/obj/.tf-build-side"
160
+ done
161
+ [[ $cleared -eq 1 ]] && echo "note the scoped stylesheets and static asset lists were built on the other side of this machine; cleared so the $1 build writes them itself"
162
+ }
103
163
  WRONG_RUNG='NETSDK1178|Microsoft\.(iOS|Android|MacCatalyst|tvOS)\.Sdk|Workload ID|not recognized|WindowsAppSDK|command not found|No such file or directory|is not recognized as an internal or external command|The term .* is not recognized|workload.*not installed|Inadequate permissions'
104
164
 
105
165
  tried=()
@@ -114,9 +174,15 @@ for r in "${rungs[@]}"; do
114
174
  powershell.exe) cmd=(powershell.exe -Command "dotnet ${args[*]}"); label="powershell.exe dotnet" ;;
115
175
  esac
116
176
  tried+=("$label")
117
- { echo "### rung $n: ${cmd[*]}"; "${cmd[@]}" 2>&1; echo "### exit $?"; } >> "$LOG" 2>&1
118
- rc="$(tail -1 "$LOG" | sed 's/### exit //')"
119
- seg="$(awk -v n="### rung $n:" 'index($0,n)==1{p=1} p' "$LOG")"
177
+ [[ "$PLATFORM" == "wsl" ]] && cross_side "$(side_of "$r")"
178
+ for attempt in $(seq 0 "${TF_BUILD_LOCK_RETRIES:-2}"); do
179
+ from=1; [[ -f "$LOG" ]] && from=$(( $(wc -l < "$LOG") + 1 )) # the redirection itself fails on a log not yet written (TF-037)
180
+ { echo "### rung $n: ${cmd[*]}"; "${cmd[@]}" 2>&1; echo "### exit $?"; } >> "$LOG" 2>&1
181
+ rc="$(tail -1 "$LOG" | sed 's/### exit //')"
182
+ seg="$(tail -n +"$from" "$LOG")" # this attempt's lines only
183
+ [[ "$rc" != "0" ]] && grep -qE "$LOCKED" <<<"$seg" || break
184
+ [[ $attempt -lt ${TF_BUILD_LOCK_RETRIES:-2} ]] && sleep "${TF_BUILD_LOCK_WAIT:-15}"
185
+ done
120
186
  if [[ "$rc" == "0" ]]; then
121
187
  warns="$(grep -cE 'warning [A-Z]+[0-9]+' <<<"$seg" || true)"
122
188
  echo "PASS $MODE on $PLATFORM via $label (rung $n) — $warns warning line(s); log $LOG"
@@ -127,16 +193,23 @@ for r in "${rungs[@]}"; do
127
193
  # taken for a wrong rung and reported NOT-RUN "host issue" (MISS-TechieFlow-20260906-03).
128
194
  if grep -qE 'error (CS|RZ|BL|XC|XLS)[0-9]{3,5}|error MSB[0-9]{4}: .*(does not exist|could not be found)|error NU1' <<<"$seg" && ! grep -qE "$WRONG_RUNG" <<<"$seg"; then
129
195
  errs="$(grep -cE 'error (CS|RZ|BL|XC|XLS|MSB|NU)[0-9]+' <<<"$seg" || true)"
130
- first="$(grep -E 'error (CS|RZ|BL|XC|XLS|MSB|NU)[0-9]+' <<<"$seg" | head -1 | sed 's/^\s*//' | cut -c1-160)"
196
+ first="$(grep -E 'error (CS|RZ|BL|XC|XLS|MSB|NU)[0-9]+' <<<"$seg" | head -1 | sed 's/^[[:space:]]*//' | cut -c1-160)"
131
197
  echo "FAIL real errors on $PLATFORM via $label (rung $n): $errs error line(s), first: $first; log $LOG"
132
- grep -E 'error (CS|RZ|BL|XC|XLS|MSB|NU)[0-9]+' <<<"$seg" | sed 's/^\s*//' | sort -u | head -10
198
+ grep -E 'error (CS|RZ|BL|XC|XLS|MSB|NU)[0-9]+' <<<"$seg" | sed 's/^[[:space:]]*//' | sort -u | head -10
133
199
  exit 1
134
200
  fi
135
- if [[ "$MODE" == "test" ]] && grep -qE 'Failed!|Failed:\s+[1-9]|Tests? failed' <<<"$seg"; then
201
+ if [[ "$MODE" == "test" ]] && grep -qE 'Failed!|Failed:[[:space:]]+[1-9]|Tests? failed' <<<"$seg"; then
136
202
  echo "FAIL tests failed on $PLATFORM via $label (rung $n); log $LOG"
137
203
  grep -E 'Failed |Failed!|Error Message|Total tests|Passed!' <<<"$seg" | head -12
138
204
  exit 1
139
205
  fi
206
+ # a locked output file: the rung is right and the code is fine; another rung over the same obj/
207
+ # is exactly what broke the stylesheets (TF-035), so stop here and name the lock
208
+ if grep -qE "$LOCKED" <<<"$seg"; then
209
+ first="$(grep -E "$LOCKED" <<<"$seg" | head -1 | sed 's/^[[:space:]]*//' | cut -c1-200)"
210
+ echo "NOT-RUN the build output is held by a running process (still after $((${TF_BUILD_LOCK_RETRIES:-2} + 1)) tries on $label): $first. Stop that app -- bash .tfcore/utils/tf-verify-boot.sh stop --port <its port> -- and build again. Not a code error; log $LOG"
211
+ exit 2
212
+ fi
140
213
  # anything else (workload, wrong rung, tool absent): try the next rung
141
214
  done
142
215
  echo "NOT-RUN no rung could $MODE on $PLATFORM; tried: ${tried[*]}. This is a host issue, never a project blocker; log $LOG"