Skip to main content

linc-crucible

Quality gates for mobile apps, answering one question: is this app ready to ship? crucible builds an Android (Gradle) or iOS (xcodebuild) app and deploys it to devices through linc-nerve, runs your lint and tests and folds them into a 7-dimension quality score with a ship gate at 85, checks app-store readiness (5 dimensions, threshold 80%), runs YAML build-verification tests (BVT) against visual and accessibility-tree baselines, and pulls ranked marketing screenshots out of a screen recording. It is one layer of a 4-layer stack: linc-device-kit (shared models) → linc-nerve (device I/O) → linc-crucible (quality). crucible itself contains no device code; everything that touches a device goes through the nerve daemon's HTTP API.

8 MCP tools | Python 3.13+ | Android + iOS

Install

Requires Python 3.13+ (python3 --version); with pipx/uv, --python 3.13.

  • pipx install linc-crucible (or pipx install --python 3.13 linc-crucible)
  • uv tool install linc-crucible (or uv tool install --python 3.13 linc-crucible)
  • pip install linc-crucible (inside a virtual environment)

AI-ranked frame picks need the unpublished linc-ai package; without it crucible_extract_assets uses evenly-spaced frames and reports fallback_used: true.

Project setup

crucible discovers projects by scanning $LINC_PROJECTS_DIR/*/linc-manifest.yaml (default ~/projects/*/linc-manifest.yaml). Two files live in the project root:

  • linc-manifest.yaml — identity. identity.name is the project name every command takes; identity.package is the Android applicationId / iOS bundle ID.
  • .forge.yaml — holds the build/test/lint commands; the name is historical. Commands in .forge.yaml run from the project root, so the ./gradlew example below requires a Gradle wrapper (./gradlew) in that directory. When neither lint_command nor test_command runs to a measurement (nothing measured), crucible assay reports "status": "unmeasured" and exits 1.
# ~/projects/myapp/linc-manifest.yaml
schema_version: 1
identity:
  name: myapp
  package: com.example.myapp
# ~/projects/myapp/.forge.yaml
forge:
  build_command: "./gradlew :app:assembleDebug"
  test_command: "./gradlew test"
  lint_command: "./gradlew lint"

For crucible preflight --project myapp, place store metadata in <project>/store/:

  • <project>/store/store-listing.md — markdown with headings # Title, # Short Description, # Full Description, # Keywords, # Category.
  • <project>/store/brand-brief.json — JSON object with required fields app_name, tagline, emotion, primary_color, accent_color, background_color, icon_concept, style, platforms.

Apps with a phone and a watch module sharing one package ID can declare modules: in .forge.yaml (name, device_types, build_command, apk_pattern) so each artifact is routed to the right device type: crucible pour --project myapp --module watchApp --device-type watch.

Quickstart

After Project setup above (myapp has linc-manifest.yaml and .forge.yaml in $LINC_PROJECTS_DIR/myapp), the first four commands need no daemon, device or Docker:

crucible fuelcheck
crucible assay --project myapp
crucible score myapp
crucible preflight --project myapp
# pour, bvt and baseline capture reach devices through the nerve daemon:
pipx install "linc-nerve>=0.1,<0.2" && nerve daemon &
claude mcp add linc-crucible -- crucible bellows

crucible fuelcheck reports which build tools, projects and services it can see and exits 0 on a fresh machine. Seven of the 8 MCP tools map directly to CLI commands (crucible_extract_assets → crucible extract-assets), while crucible_baseline is served by crucible baseline (capture, list, prune; see MCP Tools); options are flags, not positionals (crucible pour --project myapp), except crucible score PROJECT [--history] [--limit N] and crucible baseline. Output is JSON on stdout; for text, put --human before the command (crucible --human score myapp). Exit 0 success (status: "partial" stays exit 0 with ship_gate: false in the payload), 1 tool error (a run that measured nothing, or a BVT that did not run, exits 1), 2 usage error. crucible schema --json prints every command and option.

MCP Tools (8)

Seven of the 8 MCP tools are reflected directly as CLI commands (crucible_pour → crucible pour). crucible_baseline is served on the CLI by the hand-written crucible baseline group (capture, list, prune for screenshot baselines); its a11y mode and latest / promote actions are MCP-only.

Tool Needs nerve daemon Description
crucible_pour yes (no with build_only) Build, install and launch on devices (launch_intent deep-link URI, e.g. myapp://settings). Android/iOS, phone/watch routing, build_only and skip_build modes
crucible_assay no (live a11y tree check uses it when running) Quality analysis: lint, tests, optional security (MobSF), perf, static a11y, live a11y tree (a11y_tree honours the personal-device policy; allow_personal=True overrides). Modes: quick or full
crucible_bvt yes Run a YAML Build Verification Test flow on a device: scripted UI steps with screenshot, element and assert checks
crucible_score no Latest quality score or history. 7-dimension weighted composite, ship gate 85
crucible_preflight no App-store readiness: code quality, store listing (<project>/store/store-listing.md), visual assets, brand brief (<project>/store/brand-brief.json), build artifacts. Threshold 80%
crucible_baseline yes for capture; no for latest, list, prune Golden baselines for screenshot and a11y-tree regression detection (crucible baseline capture|list|prune on the CLI; a11y mode, latest and promote are MCP-only)
crucible_extract_assets no Ranked marketing screenshots (with social/store variants) from a video recording
crucible_usage_stats no Usage analytics over the local MCP call log: per-tool calls, success rate, p50/p95 latency, dormant tools

Configuration

Environment variable Default Purpose
LINC_HOME ~/.linc Root of crucible state ($LINC_HOME/crucible/*) and the MCP call log
LINC_PROJECTS_DIR ~/projects Directory scanned for */linc-manifest.yaml project discovery
LINC_WORKSPACE $LINC_HOME/projects/LINC (or ~/projects/LINC when LINC_HOME is unset) Optional LINC workspace fallback read by preflight when <project>/store/ is absent
LINC_DEVICE_FLEET_FILE $LINC_HOME/device-fleet.yaml Personal-device registry; devices marked personal: true are refused unless allow_personal=True. A missing file means no personal devices are registered; a file that exists but cannot be read or parsed refuses EVERY device operation unless allow_personal=True
LINC_LOG_LEVEL WARNING Level of the structured logs written to stderr
LINC_BUILD_GATE (unset) 1 wraps builds in linc-build-gate (host build-slot mutex)
LINC_CRUCIBLE_BUILD_TIMEOUT 600 Build timeout in seconds for pour; invalid values fall back to the default
FORGE_PROJECTS_FILE $LINC_HOME/.forge/projects.yaml Legacy project registry, read-only; manifests discovered on disk take precedence
FORGE_RUN_ID manual Deprecated session id, read when CRUCIBLE_RUN_ID is unset or empty
CRUCIBLE_USAGE_LOG redacted Local usage log mode: off, redacted or full; an unknown value means redacted (see Trust)
CRUCIBLE_RUN_ID FORGE_RUN_ID, else manual Session id stamped on usage log entries; a value outside [A-Za-z0-9._-]{1,128} is logged as manual
CRUCIBLE_SCORES_DIR $LINC_HOME/crucible/scores Quality score history
CRUCIBLE_BASELINES_DIR $LINC_HOME/crucible/baselines Visual and a11y-tree baselines
CRUCIBLE_FLEET_FILE $LINC_HOME/crucible/fleet.yaml Device fleet registry
CRUCIBLE_IOS_TEAM_ID (unset) Apple Developer Team ID for physical-iOS device builds
CRUCIBLE_MOBSF_API_KEY (unset) API key for the optional MobSF security scan (assay mode full)
ANDROID_HOME (unset) Android SDK root; its build-tools/*/aapt2 reads the package name from a built APK
ANDROID_SDK_ROOT (unset) Legacy Android SDK root, used when ANDROID_HOME is unset

Generated from linc_crucible.config_contract.ENV_VARS (crucible fuelcheck --env-table); pinned to the variables read under src/ by tests/test_config_contract.py.

State crucible writes between runs:

Path Contents
$LINC_HOME/crucible/scores/<project>/ Quality score history (JSON)
$LINC_HOME/crucible/baselines/<project>/ Visual and a11y-tree baselines
$LINC_HOME/crucible/assets/ Extracted screenshots
$LINC_HOME/crucible/fleet.yaml Device fleet registry
$LINC_HOME/mcp-calls.jsonl Local MCP usage log; redacted by default (CRUCIBLE_USAGE_LOG): keeps tool name, timing, success and allowlisted parameter values, replaces every other parameter value with <redacted len=N> and absolute paths in error with <path> (dir 0700, file 0600)

Troubleshooting

Problem Cause Fix
"Project not found" No linc-manifest.yaml under $LINC_PROJECTS_DIR Add the manifest to the project root, or point LINC_PROJECTS_DIR at its parent
"Nerve unreachable at http://localhost:8111 — start it with nerve daemon" nerve daemon not running nerve daemon, then curl http://localhost:8111/health
"No APK found" Build not run crucible pour --project myapp; check build_command in .forge.yaml
"No quality score found" No assay yet crucible assay --project myapp
Build timeout (>10 min) Cold build cache Build once by hand, then pour with skip_build

Trust

  • Your project's commands run as you. assay and pour run the build_command, test_command and lint_command from the project's .forge.yaml with your user's permissions. crucible never spawns a shell itself (argv list, shell=False, shlex.split) and refuses |, ;, &, $( and backticks, but a project command that is itself a shell (sh -c '…', bash script.sh) runs a shell because the project asked for it: the trust boundary is the project. Only point crucible at projects you trust. Details: SECURITY.md.
  • No telemetry, no phone-home. Nothing leaves the machine: crucible talks to your local nerve daemon, and to MobSF only if you run a full assay against one you started. The nerve daemon on loopback is a trusted peer: crucible acts on the device list, element trees and file paths (e.g. the screenshot path) nerve returns.
  • A local usage log, under your control. Each MCP tool call is appended to $LINC_HOME/mcp-calls.jsonl (default ~/.linc/mcp-calls.jsonl) with the tool name, timing and success. Set CRUCIBLE_USAGE_LOG=off|redacted|full:
    • redacted (default) — keeps allowlisted parameter values (such as project, mode, action and boolean/numeric flags), replaces every other parameter value length-only (<redacted len=N>) and scrubs redacted values, host roots and absolute paths (<path>) from error; new state directories are created 0700 and the log file is created owner-only (0600).
    • off — nothing is written.
    • full — raw parameter values. Opt in only on a machine you trust.

How this repo works

This is a release mirror of LINC's private repository. The code is built by LINC's AI software factory under human review; each release lands as one commit. Issues are welcome; pull requests by arrangement — see CONTRIBUTING.md.


Built by LINC Innovations — we diagnose flaky device test suites.

Licensed under the Apache License 2.0.

Metadata

Release files for linc-crucible 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for linc-crucible 0.2.0
File Size Uploaded Provenance
linc_crucible-0.2.0.tar.gz 441.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for linc-crucible 0.2.0
File Interpreter ABI Platform
linc_crucible-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 640.2 kB

Release files / linc_crucible-0.2.0.tar.gz

Download URL linc_crucible-0.2.0.tar.gz
Size 441.4 kB
Tags Source
SHA-256 checksum
How to use checksums
cbe660142ec863a5cb36add570db6a1c61c26f4ed771b89ddab217b7f939566c
BLAKE2b-256 checksum
How to use checksums
3021474ff1aacc84c8b34a48fe5b4275da456255d3965dcf07179eca31ae1865
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / linc_crucible-0.2.0-py3-none-any.whl

Download URL linc_crucible-0.2.0-py3-none-any.whl
Size 198.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
83fd89197078e08d51ee9477014b9e31621c6cc75062c5a2605297531f21997d
BLAKE2b-256 checksum
How to use checksums
f0f3390a12b561a677c2dbdf63141d2414c38e4fd854768df826342eec63ba6a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for linc-crucible 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for linc_crucible-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page