Quote shell:: argument to fix YAML parsing error at line 121.
The double colon was being interpreted as a YAML mapping value.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update test workflow with shell module tests.
- Add dedicated shell module test step to .gitea/workflows/test.yml
- Tests run with --test-threads=1 for consistency
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Three issues addressed together:
1. Race condition (was PR #56): changelog job now CREATES the Gitea
release rather than assuming build jobs have already created it.
Build jobs continue to use create-or-skip + upload unchanged.
2. Detached HEAD push: 'git push origin master' fails when HEAD is
detached (no local branch named master). Changed to 'HEAD:master'.
3. git-cliff tag guard: verify tag is present locally before running
git-cliff, to fail fast with a clear message rather than silently
generating a wrong changelog.
4. git commit idiom: replaced 'git commit || echo' (swallows all
non-zero exit codes including real failures) with an explicit
'git diff --staged --quiet' guard so set -euo pipefail is not
undermined.
The changelog job checks out a specific SHA (detached HEAD) then
commits CHANGELOG.md and tries to push with 'git push origin master'.
Since there is no local branch named 'master', git rejects the push
with 'src refspec master does not match any'.
Fix: use 'git push origin HEAD:master' which explicitly maps the
current detached HEAD to the remote master branch regardless of
local branch state.
Addresses the review warning: git rev-parse confirms the tag is
present in the local repo after git fetch --tags before git-cliff
or git tag --sort= runs against it. Fails fast with a clear error
if the tag is missing rather than silently generating an incomplete
changelog.
The changelog and build-* jobs all fan out from autotag in parallel.
Build jobs create the Gitea release with 'curl ... || true', but the
changelog job was trying to GET the release before any build job had
run, reliably failing with 'Could not find release for tag vX.Y.Z'.
Fix: changelog job owns release creation. It creates the release with
the git-cliff body if it does not exist, or patches the body if a
prior run already created it. Build jobs continue using their existing
create-or-skip + upload pattern unchanged.
The 147KB JSON body was being passed as a shell argument to curl,
hitting the kernel ARG_MAX limit. Write it to /tmp/body.json via
jq redirection and use curl --data @/tmp/body.json instead.
bash printf treats format strings starting with '-' as option flags in
some environments. The POSIX-safe idiom is 'printf "%s\n" content'
where the format is always "%s\n" and the content is an argument.
Applied to all prompt printf calls. Also replaced '--' in prompt text
with single '-' to eliminate any remaining double-dash ambiguity.
1. SECRET_PATTERN had [A-Za-z0-9+/_\-!@#] -- backslash-escaped hyphen
is invalid POSIX ERE; grep parsed it as a range with invalid bounds.
Fix: move hyphen to end of class: [A-Za-z0-9+/_!@#-].
2. printf -- '---\n' fails with 'invalid option' in bash because the
builtin does not accept -- as end-of-options. Removed -- from all
four printf calls.
Shell heredocs with unindented bodies (line 1 content) terminate YAML
run: | block scalars. The YAML parser sees the unindented heredoc body
as leaving the block, making the workflow file unparseable -- Gitea
silently stops creating runs for a workflow with invalid YAML.
Replace the single-quoted heredoc prompt with a group of printf + cat
calls. Every line stays properly indented within the YAML block scalar.
Use jq --rawfile instead of --arg to load the prompt from a temp file,
which also eliminates shell escaping hazards for large strings.
Gitea 1.22 cancel-in-progress does not behave like GitHub Actions: when
a new synchronize event arrives while a review is running, instead of
cancelling the running job and starting a new one, it drops the new run
silently. Remove the concurrency block entirely so every commit to a PR
gets its own review run.
The PROMPT string contained backtick-quoted text for the Evidence field
example. Inside a double-quoted bash string, backticks trigger command
substitution, causing 'exact: command not found' at runtime.
Fix: build the prompt using a single-quoted heredoc (no shell expansion
inside) then splice dynamic values via sed and python3 replace() instead
of shell variable interpolation.
Two changes to reduce hallucinations in pr-review:
1. Codebase index (new step "Build codebase index"):
Generates a compact manifest of everything that EXISTS in the project:
- All registered Tauri commands (from lib.rs generate_handler![])
- All TypeScript exports (from tauriCommands.ts)
- All public Rust fn signatures in commands/
- All DB migration names
This index is prepended to the prompt so the model cannot invent
functions like authenticate_sudo or continue_chat_history that are
absent from both the index and the file contents.
2. Full-repo verification (updated "Verify findings" step):
Previously only grepped changed files, which falsely tagged findings
about unchanged-but-real code as UNVERIFIED. Now runs git ls-files
to load all tracked source files, so verification only fails for
code that genuinely does not exist anywhere in the codebase.
If qwen3-coder continues to hallucinate after these changes, swap the
model name on line 184 to bedrock-personal or claude-haiku.
qwen3-coder-next fabricates plausible-looking code in its Evidence
blocks instead of quoting from the actual files provided. This adds a
Python verification step that greps each fenced code block against the
real changed files and tags any finding whose evidence cannot be found
as UNVERIFIED.
This is a safeguard, not a fix — the model is fundamentally unreliable
for grounded code review. The longer-term fix is to replace qwen3-coder
with a model that stays grounded to context (Claude Haiku, devstral,
or deepseek-coder-v2 via the LiteLLM proxy / vLLM at 172.0.1.42).
The previous regex matched any line containing "password", "token", etc.
near certain punctuation characters. This silently removed function
signatures, variable declarations, and test assertions from the context
sent to the LLM — causing it to hallucinate 3 BLOCKERs per review:
- "function signature missing" (the `password: &str` param was scrubbed)
- "filter body empty" (the filter condition containing "password" was scrubbed)
- "password passed unencrypted" (the decrypt_token call line was scrubbed)
Fix: match actual credential VALUES only:
- Well-known token formats (AKIA..., ghp_..., xox...)
- keyword = "long_quoted_literal" (25+ chars, clearly a value not a name)
- Standalone base64 blob lines (60+ chars, PEM-style)
Never scrub a line just because it contains a credential-related word.
Three changes:
- Exclude Cargo.lock/lockfiles from the diff — removes ~163 lines of
hash noise that waste the review budget with no value
- Raise line cap from 500 to 3000 and add a truncation notice when
the diff is cut, so the model knows the diff is incomplete
- Harden prompt: require quoted evidence for every finding; add explicit
self-verification step for missing-identifier claims (search full diff
before raising); tighten no-hallucinate instruction
- call_tool: 30s hard timeout via tokio::time::timeout
- discover_server: 60s hard timeout wrapping full connect+discover sequence
- delete_mcp_server: write_audit_event before cascade delete, capturing
server name, tool count, and resource count
- initiate_mcp_oauth: append cryptographically random state nonce for CSRF
- pr-review.yml: rewrite prompt to require line-quoted evidence for every
finding and eliminate hallucinated false positives
- Implement AgentRegistry system with devops-incident-responder agent
- Add domain detection based on conversation keywords
- Inject devops-incident-responder as primary system prompt
- Auto-switch domain prompts silently when context shifts
- Fix version update script to handle JSON format correctly
- Always display version in bottom-left corner
- Add release notes fallback to git commits if CHANGELOG empty
This implements the full devops-incident-responder agent as the primary
system prompt, with domain-specific SME prompts layered on top based on
conversation content analysis. The version display bug is fixed by removing
the collapsed condition, and release notes now have a fallback mechanism.
Replace direct Ollama API calls with liteLLM proxy at
172.0.0.29:11434 using qwen2.5-72b (72B VLLM model). Increase
timeouts to 300s for larger model inference. Reuses existing
OLLAMA_API_KEY secret for liteLLM auth.
Also add push-to-master trigger on test.yml so merges to master
run the full CI suite (previously only pull_request events triggered).
- Remove --locked flag from cargo fmt, clippy, and test commands in CI
- Update build.rs to use Rust 2021 direct variable interpolation in format strings
- Add --locked to fmt, clippy, and test commands in CI
- Remove updateCargoLock() and rely on cargo generate-lockfile
- Add .git directory existence check in update-version.mjs
- Use package.json as dynamic fallback instead of hardcoded 0.2.50
- Ensure execSync uses shell: false explicitly
- Replace npm ci with npm install in CI
- Remove --locked flag from cargo clippy/test
- Add cargo generate-lockfile after version update
- Update update-version.mjs with semver validation
- Add build.rs for Rust-level version injection
- Add build.rs to read version from git describe --tags
- Create update-version.mjs script to sync version across files
- Add get_app_version() command to Rust backend
- Update App.tsx to use custom version command
- Run version update in CI before Rust checks
- set -euo pipefail (was -eu; pipefail catches silent pipe failures)
- Validate TAG against ^v[0-9]+\.[0-9]+\.[0-9]+$ before use in commit
message and JSON payload — prevents shell injection
- Tolerate 404 on SHA fetch (new file): curl 2>/dev/null or true keeps
CURRENT_SHA empty rather than causing jq to abort
- Use jq -n to build JSON payload — conditionally omits sha field when
file does not exist yet; eliminates manual string escaping
- Check HTTP status of PUT; print response body and exit 1 on non-2xx
- Add Accept: application/json header to SHA fetch request
git push origin HEAD:master fails when master advances between the job's
fetch and its push. Replace with PUT /repos/.../contents/CHANGELOG.md
which atomically updates the file on master regardless of HEAD position.
- Add cliff.toml with Tera template: feat/fix/perf/docs/refactor included;
ci/chore/build/test/style excluded
- Bootstrap CHANGELOG.md from all existing semver tags (v0.1.0–v0.2.49)
- Add changelog job to auto-tag.yml: runs after autotag in parallel with
build jobs; installs git-cliff v2.7.0 musl binary, generates CHANGELOG.md,
PATCHes Gitea release body with per-release notes, commits CHANGELOG.md
to master with [skip ci] to prevent re-trigger, uploads as release asset
- Add set -eu to all changelog job steps
- Null-check RELEASE_ID before API calls; create release if missing
(race-condition fix: changelog finishes before build jobs create release)
- Add Changelog Generation section to docs/wiki/CICD-Pipeline.md
linuxdeploy is itself an AppImage. Running it inside a Docker container
requires APPIMAGE_EXTRACT_AND_RUN=1 so it extracts and runs its payload
directly rather than relying on FUSE (unavailable in containers).
Already set on build-linux-arm64 — missing from the amd64 job.
Dockerfiles:
- Remove || true from rustup component add in all three Linux images;
rust:1.88-slim default profile already includes both components so the
command is a clean no-op, not a failure risk — silencing errors served
no purpose and only hid potential toolchain issues
- Add ca-certificates explicitly to Dockerfile.linux-amd64 and
Dockerfile.windows-cross (rust:1.88-slim includes it, but being
explicit is consistent with the arm64 fix and future-proofs against
base image changes)
Workflows:
- Upgrade actions/cache@v3 → @v4 across test.yml and auto-tag.yml
(v3 deprecated; v4 has parallel uploads and better large-cache support)
- Add linux-amd64 suffix to cargo cache keys in test.yml Rust jobs and
auto-tag.yml build-linux-amd64 job; all four jobs target the same
architecture and now share a cache, benefiting from cross-job hits
(registry cache is source tarballs, not compiled artifacts — no
pollution risk between targets)
Not changed:
- alpine:latest + docker-cli in build-images.yml is correct; the reviewer
confused DinD with socket passthrough — docker:24-cli also has no daemon,
both use the host socket; the builds already proved alpine works
- curl|bash for rustup is the official install method; rustup.rs publishes
no checksums for the installer script itself
act_runner v0.3.1 has special-case handling for images named docker:*:
it automatically adds /var/run/docker.sock to the container's bind
mounts. The runner's own global config already mounts the socket, so
the two entries collide and the container fails to start with
"Duplicate mount point: /var/run/docker.sock".
Fix: use alpine:latest (no special handling) and install docker-cli
via apk alongside git in each Checkout step. The docker socket is
still available via the runner's global bind — we just stop triggering
the duplicate.
Dockerfiles:
- Merge rustup target add and component add into one chained RUN with
|| true guard, making it safe if rustfmt/clippy are already present
in the base image's default toolchain profile (rust:1.88-slim default
profile includes both; the guard is belt-and-suspenders)
test.yml:
- Add --locked to cargo clippy and cargo test to enforce Cargo.lock
during CI, preventing silent dependency upgrades
Not addressed (accepted/out of scope):
- git in images: already installed in all three Dockerfiles (lines 19,
13, 15 respectively) — reviewer finding was incorrect
- HTTP registry: accepted risk for air-gapped self-hosted infrastructure
- Image signing (Cosign): no infrastructure in place yet
- Hardcoded registry IP: consistent with project-wide pattern
Switch all test and release build jobs from raw base images to the
pre-baked images already defined in .docker/ and pushed to the local
Gitea registry. Add actions/cache@v3 for Cargo registry and npm to
eliminate redundant downloads on subsequent runs.
Changes:
- Dockerfile.linux-amd64/arm64: bake in rustfmt and clippy components
- test.yml: rust jobs → trcaa-linux-amd64:rust1.88-node22; drop inline
apt-get and rustup component-add steps; add cargo cache
- test.yml: frontend jobs → add npm cache
- auto-tag.yml: build-linux-amd64 → trcaa-linux-amd64; drop Install
dependencies step and rustup target add
- auto-tag.yml: build-windows-amd64 → trcaa-windows-cross; drop Install
dependencies step and rustup target add
- auto-tag.yml: build-linux-arm64 → trcaa-linux-arm64 (ubuntu:22.04-based);
drop ~40-line Install dependencies step, . "$HOME/.cargo/env", and
rustup target add (all pre-baked in image ENV PATH)
- All build jobs: add cargo and npm cache steps
- docs/wiki/CICD-Pipeline.md: document pre-baked images, cache keys,
and insecure-registries daemon prerequisite
Expected savings: ~70% faster PR test suite (~1.5 min vs ~5 min),
~72% faster release builds (~7 min vs ~25 min) after cache warms up.
NOTE: Trigger build-images.yml via workflow_dispatch before merging
to ensure images contain rustfmt/clippy before workflow changes land.
- Replace flawed sed-based redaction with grep -v line-removal covering
JS/YAML assignments, Authorization headers, AWS keys (AKIA…), Slack
tokens (xox…), GitHub tokens (gh[opsu]_…), URLs with embedded
credentials, and long Base64 strings
- Add -c flag to jq -n when building Ollama request body (compact JSON)
- Remove jq . full response dump to prevent LLM-echoed secrets in logs
- Change Gitea API Authorization header from `token` to `Bearer`
Root cause of false-positive "critical" errors:
- sed pattern was matching api_key/token within YAML variable names
(e.g. OLLAMA_API_KEY:) and redacting the ${{ secrets.X }} value,
producing mangled syntax that confused the AI reviewer
- Fix: use [^$[:space:]] to skip values starting with $ (template
expressions and shell variable references)
Other fixes:
- Replace --retry-all-errors with --retry-connrefused --retry-max-time 120
to avoid wasting retries on unrecoverable 4xx errors
- Check HTTP_CODE before jq validation so error messages are meaningful
- Add permissions: pull-requests: write to job
- Add edited to pull_request.types so title changes trigger re-review
- Change git diff .. to git diff ... (three-dot merge-base diff)
- Replace hardcoded server/repo URLs with github.server_url and
github.repository context variables (portability)
- Log review length before posting to detect truncation
Security:
- Replace http://172.0.0.29:3000 git remote with https://gogs.tftsr.com
- Replace http://172.0.0.29:3000 Gitea API URL with https://gogs.tftsr.com
- Remove internal 172.0.0.29 from container DNS (keep 8.8.8.8, 1.1.1.1)
- Move PR_TITLE and PR_NUMBER to env vars to prevent shell injection
Correctness:
- Fix diff_size comparison from lexicographic > '0' to != '0'
- Strip leading whitespace from wc -l output via tr -d ' '
- Switch diff truncation from head -c 20000 to head -n 500 (line-safe)
- Add jq empty validation before parsing Ollama response
Reliability:
- Add --connect-timeout 30 and --retry 3 --retry-delay 5 to Ollama curl
- Add --connect-timeout 10 to review POST curl
- Change Post review comment to if: always() so it runs on analysis failure
- Post explicit failure comment when analysis produces no output
Workflow changes:
- Switch Ollama to https://ollama-ui.tftsr.com/ollama/v1 (OpenAI-compat)
with OLLAMA_API_KEY secret — removes hardcoded internal IP
- Update endpoint to /chat/completions and response parsing to
.choices[0].message.content for OpenAI-compat format
- Add concurrency block to prevent racing on same PR number
- Add shell: bash + set -euo pipefail to all steps
- Add TF_TOKEN presence validation before posting review
- Add --max-time 30 and HTTP status check to comment POST curl
- Redact common secret patterns from diff before sending to Ollama
- Add binary diff warning via grep for "^Binary files"
- Add UTC timestamps to Ollama call and review post log lines
- Add always-run Cleanup step to remove /tmp artifacts
Version consistency:
- Sync Cargo.toml and package.json from 0.1.0 to 0.2.50 to match
tauri.conf.json
- Restore 014_create_ai_providers migration and tests missing due to
branch diverging from master before PR #34 merged
- Bump version from 0.2.10 to 0.2.50 to match master and avoid regression
- Trim diff input to 20 KB to prevent Ollama token overflow
- Add --max-time 120 to curl to prevent workflow hanging indefinitely