Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
quality:
name: Lint and coverage gate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Install TeaForge and development dependencies
run: python -m pip install -e ".[dev]"
- name: Check Python source quality
run: python -m ruff check src tests demo
- name: Enforce TeaForge branch coverage baseline
run: |
python -m coverage run -m pytest -q
python -m coverage report

python-tests:
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
python-version: "3.11"
- os: ubuntu-latest
python-version: "3.14"
- os: windows-latest
python-version: "3.12"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install TeaForge and test dependencies
run: python -m pip install -e ".[dev]"
- name: Run tests
run: python -m pytest -q
- name: Compile Python modules
run: python -m compileall -q src tests

package-smoke:
name: Wheel and sdist smoke
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- name: Build and validate distributions
run: |
python -m pip install build twine
python -m build
python -m twine check dist/*
- name: Install wheel outside the source tree
run: |
python -m venv "$RUNNER_TEMP/teaforge-smoke"
"$RUNNER_TEMP/teaforge-smoke/bin/pip" install dist/*.whl
cd "$RUNNER_TEMP"
"$RUNNER_TEMP/teaforge-smoke/bin/teaforge" --version
"$RUNNER_TEMP/teaforge-smoke/bin/teaforge" pcl generate \
--path "$GITHUB_WORKSPACE/tests/fixtures/test_sample_pytest.py" \
--output "$RUNNER_TEMP/pcl.html"
"$RUNNER_TEMP/teaforge-smoke/bin/teaforge" coverage generate \
--path "$GITHUB_WORKSPACE/tests/fixtures/coverage_project/tests" \
--output "$RUNNER_TEMP/coverage.html" \
--python-executable "$RUNNER_TEMP/teaforge-smoke/bin/python" \
--min-c0 100
"$RUNNER_TEMP/teaforge-smoke/bin/python" -c \
"from importlib import resources; assert resources.files('teaforge.templates').joinpath('pcl.html').is_file(); assert resources.files('teaforge.templates').joinpath('coverage_report.html').is_file(); assert resources.files('teaforge.jest.assets').joinpath('runtime-listener.cjs').is_file()"
- uses: actions/upload-artifact@v4
with:
name: teaforge-distributions
path: dist/*

jest-integration:
name: Jest ${{ matrix.node-version }} integration
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
node-version: ["20", "22"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
cache-dependency-path: demo/jest_runtime/package-lock.json
- name: Install TeaForge and locked Jest fixture
run: |
python -m pip install -e .
npm ci --prefix demo/jest_runtime
- name: Run target project tests
run: npm --prefix demo/jest_runtime test -- --runInBand
- name: Generate runtime evidence outside the project directory
run: |
cd "$RUNNER_TEMP"
teaforge doctor \
--framework jest \
--path "$GITHUB_WORKSPACE/demo/jest_runtime/tests/user.test.js" \
--json
teaforge pcl generate \
--framework jest \
--evidence-mode runtime \
--path "$GITHUB_WORKSPACE/demo/jest_runtime/tests/user.test.js" \
--output "$RUNNER_TEMP/jest-pcl.html"
teaforge coverage generate \
--framework jest \
--path "$GITHUB_WORKSPACE/demo/jest_runtime/tests/user.test.js" \
--output "$RUNNER_TEMP/jest-coverage.html" \
--min-c0 100 \
--min-c1 100
- name: Verify runtime evidence identity and status
run: |
python -c "import json, pathlib; files=list(pathlib.Path('$RUNNER_TEMP/user').glob('jest-pcl-*.json')); docs=[json.loads(path.read_text()) for path in files]; serialized=json.dumps(docs); assert {doc['method'] for doc in docs} == {'createUser', 'getUser'}; assert all(case['execution_status'] == 'passed' for doc in docs for case in doc['testcases']); assert 'demo-secret' not in serialized; assert '[REDACTED]' in serialized"
python -c "import json, pathlib, re; html=pathlib.Path('$RUNNER_TEMP/jest-coverage.html').read_text(); payload=json.loads(re.search(r'<script id=\"teaforge-coverage-data\" type=\"application/json\">(.*?)</script>', html, re.S).group(1)); assert payload['schema_version'] == 2; assert payload['evidence_source'] == 'Jest/Istanbul coverage-final.json'; assert payload['c0']['percent'] == 100.0; assert payload['c1']['percent'] == 100.0"

render-integration:
name: Mermaid and PDF integration
runs-on: ubuntu-latest
timeout-minutes: 20
env:
TEAFORGE_MERMAID_PUPPETEER_CONFIG: ${{ github.workspace }}/tools/render/puppeteer-ci.json
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: tools/render/package-lock.json
- name: Install TeaForge and locked render tools
run: |
sudo apt-get update
sudo apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0
python -m pip install -e ".[dev,pdf]"
npm ci --prefix tools/render
echo "$GITHUB_WORKSPACE/tools/render/node_modules/.bin" >> "$GITHUB_PATH"
- name: Check required render capabilities
run: teaforge doctor --framework pytest --path demo/fastapi_crud/tests --require-mermaid --require-pdf
- name: Render flowchart, sequence, coverage, and PDF
run: |
teaforge mermaid generate \
--source demo/fastapi_crud/app/main.py \
--function create_item \
--diagram-type flowchart \
--code "flowchart TD
A[Receive request] --> B{Valid?}
B -- Yes --> C[Create item]
B -- No --> D[Return validation error]" \
--output-dir "$RUNNER_TEMP/diagrams"
teaforge mermaid generate \
--source demo/fastapi_crud/app/main.py \
--function create_item \
--diagram-type sequence \
--code "sequenceDiagram
Client->>API: Create item
API->>DB: Insert item
DB-->>API: Stored row
API-->>Client: Created response" \
--output-dir "$RUNNER_TEMP/diagrams"
teaforge coverage generate \
--path demo/fastapi_crud/tests \
--output "$RUNNER_TEMP/render-coverage.html" \
--function create_item \
--sequence create_item \
--diagram-dir "$RUNNER_TEMP/diagrams" \
--min-c0 100 \
--min-c1 100
teaforge export \
--path "$RUNNER_TEMP/render-coverage.html" \
--output "$RUNNER_TEMP/render-coverage.pdf"
test -s "$RUNNER_TEMP/diagrams/main_create_item_coverage_report.svg"
test -s "$RUNNER_TEMP/diagrams/main_create_item_sequence_diagram.svg"
python -c "from pathlib import Path; assert Path('$RUNNER_TEMP/render-coverage.pdf').read_bytes().startswith(b'%PDF')"
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
.pytest_cache/
.ruff_cache/
.coverage
.coverage.*
__pycache__/
*.pyc
*.pyo
*.pyd
.DS_Store
.venv/
node_modules/
.playwright-cli/
dist/
build/
*.egg-info/
Expand All @@ -14,11 +18,20 @@ demo/fastapi_crud/*.db
*.html
*.json

# The executable Jest integration fixture must remain reproducible.
!demo/jest_runtime/package.json
!demo/jest_runtime/package-lock.json
!tools/render/package.json
!tools/render/package-lock.json
!tools/render/puppeteer-ci.json

# Report templates are source files, not generated output.
!src/teaforge/templates/*.html

# Ignore all files under output/ but keep the directory with a .gitkeep
output/*
!output/.gitkeep

# Internal development docs
STEP*.md
DESIGN.md

26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Changelog

## 0.2.0 - 2026-07-15

TeaForge moves from an early pytest document demo to a beta-quality, installable CLI.

### Added

- Versioned PCL and coverage artifacts for pytest, Jest, Angular/Jest, and Playwright tests.
- Project-local Jest runtime assertion evidence with expected/actual values, pass/fail state, redaction, and bounded JSONL capture.
- Tree-sitter JavaScript, TypeScript, and TSX syntax evidence shared by parsers and coverage analysis.
- File-level C0/C1 gates, Mermaid flowchart and sequence-diagram report pages, and optional PDF export.
- `teaforge doctor` capability checks and machine-readable JSON output.
- Linux, Windows, Python 3.11-3.14, Node 20/22, packaging, lint, branch-coverage, Jest, Mermaid, and PDF CI gates.

### Changed

- External processes now share monotonic workflow deadlines, retain bounded diagnostics, and clean up descendant processes on timeout.
- Pytest and Jest run from discovered target-project roots without downloading target dependencies.
- Test discovery excludes generated dependency and virtual-environment trees by default.
- Text artifacts are rendered before same-directory atomic replacement.

### Security

- Runtime evidence redacts common credential fields and patterns and enforces value, record, and file-size limits.
- External commands execute without a shell and use exact resolved runner and test paths.
61 changes: 61 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# TeaForge Domain Context

TeaForge converts executable test evidence into reviewable engineering artifacts. The implementation should use these terms consistently in code, CLI help, reports, and architecture decisions.

## Core terms

### Test Subject

The source file and function or method whose behavior a test exercises. A subject identity is proven from imports and calls when possible. Coverage generation must fail when source identity cannot be proven; PCL generation may preserve an explicitly marked fallback identity.

### PCL Document

A Program Check List for one Test Subject. It contains design-time inputs and checks, optional runtime evidence, source identity, execution status, warnings, and a stable artifact schema version. One document may be split into multiple 25-column sheets without changing its subject identity.

### Static Evidence

Inputs and expectations inferred without executing the target tests. Static evidence describes test intent, but can be incomplete when values are built dynamically.

### JavaScript Syntax Evidence

Structural Static Evidence produced from TeaForge-packaged Tree-sitter JavaScript, TypeScript, and TSX grammars. Adapters consume stable imports, test cases, calls, exported symbols, and source-function facts rather than grammar nodes. It proves syntax structure, not TypeScript types or runtime values.

### Runtime Evidence

Matcher, expected value, observed actual value, and pass/fail status captured while an already-installed target-project Jest runs. Runtime Evidence supplements rather than replaces Static Evidence.

### Coverage Evidence

Executed and missing statement/branch locations read from Python coverage data or Istanbul JSON. Coverage Evidence is distinct from assertion evidence and must remain traceable to its source file.

### Flowchart

A typed Mermaid diagram of internal control flow for a Test Subject: decisions, loops, error paths, and exits.

### Sequence Diagram

A typed Mermaid diagram of interaction order across participants such as caller, service, persistence, and external systems. TeaForge does not infer a trustworthy sequence solely from assertion values; the caller supplies the reviewed diagram source.

### Artifact

A versioned JSON, HTML, PDF, Mermaid, or SVG output produced from evidence. Text artifacts are written through same-directory temporary files and atomic replacement. Concurrent writers to the same path are outside the current contract.

### Capability Check

A non-mutating `teaforge doctor` check that reports whether a required executable, Python module, packaged resource, renderer, or target-project discovery rule is satisfied.

## Invariants

1. TeaForge never downloads or installs target-project test runners during analysis.
2. Target tests and renderers execute from their discovered context with exact paths, one workflow deadline, memory-bounded captured output, and process-tree cleanup on timeout.
3. A failed test and a failed TeaForge invocation are different results. Evidence-bearing Jest failures use exit code 2.
4. Runtime Evidence never silently overwrites the test's Static Evidence.
5. Unsupported or ambiguous diagram types and source identities fail explicitly.
6. New artifact schemas are versioned; unknown future versions are rejected rather than guessed.
7. Machine-readable command modes emit versioned JSON for both success and post-dispatch failure paths.

## Current boundaries

JavaScript/TypeScript discovery uses structural Tree-sitter grammars rather than regex matching, but it is not a TypeScript type checker and does not evaluate dynamic imports or computed test construction. Runtime evidence has default sensitive-key and common credential-pattern redaction plus size limits, but each organization must decide whether that policy is sufficient. File-level C0/C1 gates are available; organization-wide aggregation and policy profiles remain outside the current contract.

Directory discovery excludes generated dependency and environment trees by default. An explicitly supplied file remains eligible even when it is located under an excluded directory.
13 changes: 13 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
include LICENSE
include README.md
include README_JP.md
include CONTEXT.md
include CHANGELOG.md
recursive-include docs *.md
recursive-include skill *.md
recursive-include demo *.py *.md *.js *.cjs package.json package-lock.json
recursive-include tests *.py *.ts *.toml
recursive-include tools/render package.json package-lock.json
recursive-include .github/workflows *.yml
prune demo/jest_runtime/node_modules
prune tools/render/node_modules
Loading
Loading