quor 0.1.0__tar.gz

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 (104) hide show
  1. quor-0.1.0/.github/workflows/canary.yml +108 -0
  2. quor-0.1.0/.github/workflows/ci.yml +43 -0
  3. quor-0.1.0/.github/workflows/publish-testpypi.yml +84 -0
  4. quor-0.1.0/.github/workflows/release.yml +101 -0
  5. quor-0.1.0/.gitignore +46 -0
  6. quor-0.1.0/CHANGELOG.md +111 -0
  7. quor-0.1.0/CODE_OF_CONDUCT.md +69 -0
  8. quor-0.1.0/CONTRIBUTING.md +372 -0
  9. quor-0.1.0/LICENSE +201 -0
  10. quor-0.1.0/PKG-INFO +259 -0
  11. quor-0.1.0/README.md +221 -0
  12. quor-0.1.0/SECURITY.md +65 -0
  13. quor-0.1.0/docs/archive/architecture-exploration/engineering-patterns.md +1690 -0
  14. quor-0.1.0/docs/archive/product-discovery/competitive-research.md +943 -0
  15. quor-0.1.0/docs/archive/product-discovery/final-discovery.md +1726 -0
  16. quor-0.1.0/docs/archive/research/design-review.md +913 -0
  17. quor-0.1.0/docs/archive/research/zap-analysis.md +1955 -0
  18. quor-0.1.0/docs/final/ANTI_GOALS.md +166 -0
  19. quor-0.1.0/docs/final/CLAUDE.md +310 -0
  20. quor-0.1.0/docs/final/CONTRIBUTING.md +5 -0
  21. quor-0.1.0/docs/final/DECISIONS.md +742 -0
  22. quor-0.1.0/docs/final/IMPLEMENTATION_PLAN.md +509 -0
  23. quor-0.1.0/docs/final/PROJECT_BIBLE.md +512 -0
  24. quor-0.1.0/docs/final/PROJECT_STATUS.md +374 -0
  25. quor-0.1.0/docs/final/RELEASE_CRITERIA.md +181 -0
  26. quor-0.1.0/docs/final/RESEARCH_COMPLETION.md +185 -0
  27. quor-0.1.0/docs/final/ROADMAP.md +161 -0
  28. quor-0.1.0/pyproject.toml +130 -0
  29. quor-0.1.0/quor/__init__.py +1 -0
  30. quor-0.1.0/quor/__main__.py +104 -0
  31. quor-0.1.0/quor/adapters/__init__.py +0 -0
  32. quor-0.1.0/quor/adapters/base.py +39 -0
  33. quor-0.1.0/quor/adapters/claude.py +65 -0
  34. quor-0.1.0/quor/adapters/dispatcher.py +212 -0
  35. quor-0.1.0/quor/cli/__init__.py +0 -0
  36. quor-0.1.0/quor/cli/commands/__init__.py +0 -0
  37. quor-0.1.0/quor/cli/commands/doctor.py +190 -0
  38. quor-0.1.0/quor/cli/commands/explain.py +84 -0
  39. quor-0.1.0/quor/cli/commands/gain.py +54 -0
  40. quor-0.1.0/quor/cli/commands/init.py +210 -0
  41. quor-0.1.0/quor/cli/commands/validate.py +55 -0
  42. quor-0.1.0/quor/cli/commands/verify.py +52 -0
  43. quor-0.1.0/quor/cli/main.py +58 -0
  44. quor-0.1.0/quor/config/__init__.py +0 -0
  45. quor-0.1.0/quor/config/loader.py +43 -0
  46. quor-0.1.0/quor/config/model.py +60 -0
  47. quor-0.1.0/quor/errors.py +69 -0
  48. quor-0.1.0/quor/filters/__init__.py +0 -0
  49. quor-0.1.0/quor/filters/builtin/__init__.py +0 -0
  50. quor-0.1.0/quor/filters/builtin/build.toml +89 -0
  51. quor-0.1.0/quor/filters/builtin/cat.toml +50 -0
  52. quor-0.1.0/quor/filters/builtin/git.toml +131 -0
  53. quor-0.1.0/quor/filters/builtin/pytest.toml +59 -0
  54. quor-0.1.0/quor/filters/builtin/z_generic.toml +34 -0
  55. quor-0.1.0/quor/filters/loader.py +32 -0
  56. quor-0.1.0/quor/filters/registry.py +278 -0
  57. quor-0.1.0/quor/filters/trust.py +19 -0
  58. quor-0.1.0/quor/pipeline/__init__.py +0 -0
  59. quor-0.1.0/quor/pipeline/content_type.py +69 -0
  60. quor-0.1.0/quor/pipeline/engine.py +151 -0
  61. quor-0.1.0/quor/pipeline/mask.py +57 -0
  62. quor-0.1.0/quor/pipeline/plugin_loader.py +517 -0
  63. quor-0.1.0/quor/pipeline/stages/__init__.py +0 -0
  64. quor-0.1.0/quor/pipeline/stages/_utils.py +48 -0
  65. quor-0.1.0/quor/pipeline/stages/base.py +88 -0
  66. quor-0.1.0/quor/pipeline/stages/deduplicate_consecutive.py +68 -0
  67. quor-0.1.0/quor/pipeline/stages/group_repeated.py +162 -0
  68. quor-0.1.0/quor/pipeline/stages/max_tokens.py +126 -0
  69. quor-0.1.0/quor/pipeline/stages/remove_ansi.py +71 -0
  70. quor-0.1.0/quor/pipeline/stages/strip_lines.py +70 -0
  71. quor-0.1.0/quor/plugins/__init__.py +61 -0
  72. quor-0.1.0/quor/plugins/base.py +473 -0
  73. quor-0.1.0/quor/plugins/registry.py +349 -0
  74. quor-0.1.0/quor/py.typed +0 -0
  75. quor-0.1.0/quor/rewrite/__init__.py +0 -0
  76. quor-0.1.0/quor/rewrite/classifier.py +222 -0
  77. quor-0.1.0/quor/rewrite/lexer.py +198 -0
  78. quor-0.1.0/quor/rewrite/rules.py +145 -0
  79. quor-0.1.0/quor/tracking/__init__.py +0 -0
  80. quor-0.1.0/quor/tracking/db.py +310 -0
  81. quor-0.1.0/quor/tracking/schema.sql +28 -0
  82. quor-0.1.0/tests/conftest.py +24 -0
  83. quor-0.1.0/tests/fixtures/commands/compound.toml +82 -0
  84. quor-0.1.0/tests/fixtures/commands/env_prefix.toml +67 -0
  85. quor-0.1.0/tests/fixtures/commands/exclusions.toml +99 -0
  86. quor-0.1.0/tests/fixtures/commands/simple.toml +151 -0
  87. quor-0.1.0/tests/fixtures/commands/transparent_prefix.toml +80 -0
  88. quor-0.1.0/tests/fixtures/test_plugin/pyproject.toml +15 -0
  89. quor-0.1.0/tests/fixtures/test_plugin/quor_test_stage/__init__.py +0 -0
  90. quor-0.1.0/tests/fixtures/test_plugin/quor_test_stage/stage.py +21 -0
  91. quor-0.1.0/tests/integration/__init__.py +0 -0
  92. quor-0.1.0/tests/unit/__init__.py +0 -0
  93. quor-0.1.0/tests/unit/test_adapters.py +469 -0
  94. quor-0.1.0/tests/unit/test_cli.py +566 -0
  95. quor-0.1.0/tests/unit/test_fail_open.py +454 -0
  96. quor-0.1.0/tests/unit/test_filter_safety.py +507 -0
  97. quor-0.1.0/tests/unit/test_filters.py +483 -0
  98. quor-0.1.0/tests/unit/test_pipeline.py +486 -0
  99. quor-0.1.0/tests/unit/test_plugin_loader.py +701 -0
  100. quor-0.1.0/tests/unit/test_plugins.py +858 -0
  101. quor-0.1.0/tests/unit/test_rewrite.py +449 -0
  102. quor-0.1.0/tests/unit/test_stages.py +509 -0
  103. quor-0.1.0/tests/unit/test_tracking.py +602 -0
  104. quor-0.1.0/tests/unit/test_version.py +47 -0
@@ -0,0 +1,108 @@
1
+ name: Canary — Claude Code Compatibility
2
+
3
+ # Runs weekly to catch Anthropic changing the Claude Code hook format before
4
+ # users do. Also triggerable manually for immediate spot-checks.
5
+
6
+ on:
7
+ schedule:
8
+ - cron: '0 8 * * 1' # Every Monday at 08:00 UTC
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ canary:
16
+ name: Hook compatibility — Claude Code latest
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Set up Python 3.11
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: '3.11'
26
+
27
+ - name: Set up Node.js (for Claude Code)
28
+ uses: actions/setup-node@v4
29
+ with:
30
+ node-version: 'lts/*'
31
+
32
+ - name: Install Claude Code (latest, unpinned)
33
+ # Not pinned intentionally — the canary exists to catch Anthropic
34
+ # changing hook format in a new release before our users hit it.
35
+ run: npm install -g @anthropic-ai/claude-code
36
+
37
+ - name: Record Claude Code version
38
+ run: |
39
+ echo "## Claude Code version" >> $GITHUB_STEP_SUMMARY
40
+ npm ls -g @anthropic-ai/claude-code --json 2>/dev/null \
41
+ | python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d.get('dependencies',{}).values())[0].get('version','unknown'))" \
42
+ || echo "version unavailable" >> $GITHUB_STEP_SUMMARY
43
+
44
+ - name: Install Quor from source
45
+ run: pip install -e ".[dev]"
46
+
47
+ - name: Install test plugin fixture
48
+ run: pip install -e ./tests/fixtures/test_plugin
49
+
50
+ - name: Verify hook responds to current Claude Code PreToolUse format
51
+ # Send the JSON format Claude Code actually sends via PreToolUse hook,
52
+ # verify the response is valid JSON with the expected command rewrite.
53
+ run: |
54
+ PAYLOAD='{"tool_name":"Bash","tool_input":{"command":"git status"}}'
55
+ RESULT=$(echo "$PAYLOAD" | python -m quor hook claude)
56
+ echo "Hook response: $RESULT"
57
+
58
+ # Verify: response is valid JSON and command is rewritten
59
+ echo "$RESULT" | python3 - <<'EOF'
60
+ import sys, json
61
+ try:
62
+ d = json.load(sys.stdin)
63
+ except json.JSONDecodeError as e:
64
+ print(f"FAIL: hook response is not valid JSON: {e}", file=sys.stderr)
65
+ sys.exit(1)
66
+ cmd = d.get("tool_input", {}).get("command", "")
67
+ if "quor git status" not in cmd:
68
+ print(f"FAIL: expected 'quor git status' in command, got {cmd!r}", file=sys.stderr)
69
+ sys.exit(1)
70
+ print(f"OK: command rewritten to {cmd!r}")
71
+ EOF
72
+
73
+ - name: Verify hook preserves extra JSON fields
74
+ # Claude Code may add extra fields to the payload; Quor must not drop them.
75
+ run: |
76
+ PAYLOAD='{"tool_name":"Bash","tool_input":{"command":"git status"},"session_id":"abc123","extra_field":"preserved"}'
77
+ RESULT=$(echo "$PAYLOAD" | python -m quor hook claude)
78
+ echo "$RESULT" | python3 - <<'EOF'
79
+ import sys, json
80
+ d = json.load(sys.stdin)
81
+ if d.get("session_id") != "abc123":
82
+ print("FAIL: extra fields were dropped by the hook", file=sys.stderr)
83
+ sys.exit(1)
84
+ if d.get("extra_field") != "preserved":
85
+ print("FAIL: extra_field was dropped by the hook", file=sys.stderr)
86
+ sys.exit(1)
87
+ print("OK: extra JSON fields preserved")
88
+ EOF
89
+
90
+ - name: Verify quor init --claude writes valid settings.json
91
+ run: |
92
+ mkdir -p /tmp/canary-claude
93
+ python -m quor init --claude --yes --settings-path /tmp/canary-claude/settings.json
94
+ python3 -c "
95
+ import json, sys
96
+ with open('/tmp/canary-claude/settings.json') as f:
97
+ d = json.load(f)
98
+ hooks = d.get('hooks', {}).get('PreToolUse', [])
99
+ if not any('claude-hook.ps1' in str(h) for entry in hooks for h in entry.get('hooks', [])):
100
+ print('FAIL: hook not found in settings.json', file=sys.stderr)
101
+ sys.exit(1)
102
+ print('OK: hook registered in settings.json')
103
+ "
104
+
105
+ - name: Run full test suite
106
+ # The canary also proves Quor's test suite passes against the latest
107
+ # Claude Code version (package installed above).
108
+ run: pytest tests/ -q --tb=short -x
@@ -0,0 +1,43 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Python ${{ matrix.python-version }} / ${{ matrix.os }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ os: [ubuntu-latest, windows-latest]
20
+ python-version: ["3.11", "3.12"]
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Set up Python ${{ matrix.python-version }}
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: ${{ matrix.python-version }}
29
+
30
+ - name: Install project with dev dependencies
31
+ run: pip install -e ".[dev]"
32
+
33
+ - name: Install test plugin fixture
34
+ run: pip install -e ./tests/fixtures/test_plugin
35
+
36
+ - name: Lint — ruff
37
+ run: ruff check quor/ tests/
38
+
39
+ - name: Type check — mypy
40
+ run: mypy quor/
41
+
42
+ - name: Tests
43
+ run: pytest tests/ --cov=quor --cov-report=term-missing
@@ -0,0 +1,84 @@
1
+ name: Publish to TestPyPI
2
+
3
+ # Manual dry-run publish used to validate packaging/publishing before tagging
4
+ # a real release. Trigger from the Actions tab, supplying the version you
5
+ # expect to publish (must match pyproject.toml).
6
+ #
7
+ # Mirrors release.yml's build/verify/publish structure as closely as possible
8
+ # so a TestPyPI dry run exercises the same path the real release will use.
9
+ # Independent of release.yml — does not touch the tag-triggered real-PyPI
10
+ # release path.
11
+
12
+ on:
13
+ workflow_dispatch:
14
+ inputs:
15
+ version:
16
+ description: 'Expected version to publish (must match pyproject.toml, e.g. 0.1.0)'
17
+ required: true
18
+ type: string
19
+
20
+ permissions:
21
+ contents: read
22
+
23
+ jobs:
24
+ build:
25
+ name: Build and verify distribution
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v5
32
+ with:
33
+ python-version: '3.11'
34
+
35
+ - name: Install build tooling
36
+ run: pip install build twine
37
+
38
+ - name: Build wheel and sdist
39
+ run: python -m build
40
+
41
+ - name: Verify distribution metadata
42
+ run: twine check dist/*
43
+
44
+ - name: Verify input version matches package version
45
+ run: |
46
+ INPUT_VERSION="${{ inputs.version }}"
47
+ PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
48
+ if [ "$INPUT_VERSION" != "$PKG_VERSION" ]; then
49
+ echo "::error::Input version ($INPUT_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
50
+ exit 1
51
+ fi
52
+ echo "Input version $INPUT_VERSION matches package version $PKG_VERSION"
53
+
54
+ - name: Upload build artifacts
55
+ uses: actions/upload-artifact@v4
56
+ with:
57
+ name: dist
58
+ path: dist/
59
+ retention-days: 30
60
+
61
+ publish-testpypi:
62
+ name: Publish to TestPyPI
63
+ needs: build
64
+ runs-on: ubuntu-latest
65
+ environment: testpypi
66
+ steps:
67
+ - name: Download build artifacts
68
+ uses: actions/download-artifact@v4
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+
73
+ - name: Check TestPyPI token is configured
74
+ run: |
75
+ if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
76
+ echo "::error::TEST_PYPI_API_TOKEN is not set for the 'testpypi' environment. Configure it under Settings > Environments > testpypi before this job can publish."
77
+ exit 1
78
+ fi
79
+
80
+ - name: Publish to TestPyPI
81
+ uses: pypa/gh-action-pypi-publish@release/v1
82
+ with:
83
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
84
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,101 @@
1
+ name: Release
2
+
3
+ # Triggered by pushing a version tag, e.g.: git tag v0.1.0 && git push origin v0.1.0
4
+ #
5
+ # Builds wheel + sdist, verifies them, attaches them to a GitHub Release,
6
+ # and (only if a PYPI_API_TOKEN secret is configured for the `pypi` GitHub
7
+ # environment) publishes to PyPI. Without that secret configured, the
8
+ # publish job runs and fails fast at the "Check PyPI token is configured"
9
+ # step rather than silently skipping — so a maintainer always gets a clear
10
+ # signal instead of a false "success".
11
+
12
+ on:
13
+ push:
14
+ tags:
15
+ - 'v*'
16
+
17
+ permissions:
18
+ contents: write
19
+
20
+ jobs:
21
+ build:
22
+ name: Build and verify distribution
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: '3.11'
31
+
32
+ - name: Install build tooling
33
+ run: pip install build twine
34
+
35
+ - name: Build wheel and sdist
36
+ run: python -m build
37
+
38
+ - name: Verify distribution metadata
39
+ run: twine check dist/*
40
+
41
+ - name: Verify tag matches package version
42
+ run: |
43
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
44
+ PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
45
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
46
+ echo "::error::Tag version ($TAG_VERSION) does not match pyproject.toml version ($PKG_VERSION)"
47
+ exit 1
48
+ fi
49
+ echo "Tag $GITHUB_REF_NAME matches package version $PKG_VERSION"
50
+
51
+ - name: Upload build artifacts
52
+ uses: actions/upload-artifact@v4
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+ retention-days: 30
57
+
58
+ github-release:
59
+ name: Create GitHub Release
60
+ needs: build
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+
65
+ - name: Download build artifacts
66
+ uses: actions/download-artifact@v4
67
+ with:
68
+ name: dist
69
+ path: dist/
70
+
71
+ - name: Create GitHub Release
72
+ uses: softprops/action-gh-release@v2
73
+ with:
74
+ files: dist/*
75
+ generate_release_notes: true
76
+ draft: false
77
+ prerelease: false
78
+
79
+ publish-pypi:
80
+ name: Publish to PyPI
81
+ needs: build
82
+ runs-on: ubuntu-latest
83
+ environment: pypi
84
+ steps:
85
+ - name: Download build artifacts
86
+ uses: actions/download-artifact@v4
87
+ with:
88
+ name: dist
89
+ path: dist/
90
+
91
+ - name: Check PyPI token is configured
92
+ run: |
93
+ if [ -z "${{ secrets.PYPI_API_TOKEN }}" ]; then
94
+ echo "::error::PYPI_API_TOKEN is not set for the 'pypi' environment. Configure it under Settings > Environments > pypi before this job can publish. Skipping publish for this run."
95
+ exit 1
96
+ fi
97
+
98
+ - name: Publish to PyPI
99
+ uses: pypa/gh-action-pypi-publish@release/v1
100
+ with:
101
+ password: ${{ secrets.PYPI_API_TOKEN }}
quor-0.1.0/.gitignore ADDED
@@ -0,0 +1,46 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.pyo
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .eggs/
11
+ MANIFEST
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+ ENV/
18
+
19
+ # uv
20
+ .python-version
21
+
22
+ # Testing
23
+ .pytest_cache/
24
+ .coverage
25
+ .coverage.*
26
+ htmlcov/
27
+ coverage.xml
28
+
29
+ # Type checking
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # Distribution / packaging
34
+ *.whl
35
+ *.tar.gz
36
+
37
+ # IDE
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+ *.swo
42
+ *~
43
+
44
+ # OS
45
+ .DS_Store
46
+ Thumbs.db
@@ -0,0 +1,111 @@
1
+ # Changelog
2
+
3
+ All notable changes to Quor are documented here. Format loosely follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [0.1.0] — Internal Alpha
7
+
8
+ First public-quality release. Quor is a rule-based command-output
9
+ optimization and context-compression layer for AI coding assistants: it
10
+ runs your command, captures the output, and applies a deterministic,
11
+ fail-open filtering pipeline before the output reaches the assistant's
12
+ context window.
13
+
14
+ ### Core pipeline (Phases 0-6)
15
+
16
+ - **ContentMask pipeline** — the `KEEP` / `COMPRESS` / `PROTECT` line-level
17
+ decision model that every compression stage operates on. `PROTECT` is
18
+ immutable once set: no later stage can downgrade it (ADR-003, "Core
19
+ Abstraction — ContentMask").
20
+ - **Five built-in compression stages** — `remove_ansi`, `strip_lines`,
21
+ `deduplicate_consecutive`, `group_repeated`, `max_tokens`.
22
+ - **Five built-in filters** — `git`, `pytest`, `build` (mypy/ruff), `cat`,
23
+ and a generic ANSI+truncation fallback, each with inline TOML tests.
24
+ - **Three-tier filter registry** — project > user > built-in precedence
25
+ (ADR "Filter Registry — Three-Tier Lookup").
26
+ - **Command rewriter and classifier** — quote-aware shell lexer, rule-based
27
+ command classification, 100+ fixture-driven tests.
28
+ - **Claude Code hook adapter** — intercepts the `PreToolUse` hook, rewrites
29
+ the command to route through Quor, preserves every extra JSON field
30
+ Claude Code sends.
31
+ - **SQLite + JSONL dual tracking** — background-thread writes, WAL mode,
32
+ never blocks the hook path (ADR "Persistence — Dual (SQLite + JSONL)").
33
+ - **Six CLI commands** — `init --claude`, `validate`, `explain`, `gain`,
34
+ `verify`, `doctor` — plus the `schema` utility command for the filter
35
+ JSON Schema. Both `quor` and `qr` are registered entry points.
36
+
37
+ ### Plugin Infrastructure (Phase 8)
38
+
39
+ - Public `Plugin` Protocol (`quor.plugins.base`) — `@runtime_checkable`,
40
+ versioned via `QUOR_PLUGIN_API_VERSION`, lifecycle-managed
41
+ (`initialize` / `execute` / `shutdown`).
42
+ - `PluginRegistry` — three-tier registration (project > user > builtin),
43
+ deterministic execution order, fully fail-open execution.
44
+ - Deliberately kept separate from the existing `StageHandler` Protocol:
45
+ `StageHandler` is TOML-configurable, line-level, stateless compression;
46
+ `Plugin` is Python-coded, lifecycle-managed middleware for telemetry,
47
+ policy, and routing (ADR "Plugin Architecture — Two-Tier Separation").
48
+
49
+ ### Plugin Discovery & Loading (Phase 9)
50
+
51
+ - Entry-point discovery for both `quor.compression_stage` and `quor.plugin`
52
+ groups via `importlib.metadata`, with a package-set-hash-invalidated
53
+ local cache.
54
+ - `api_version` compatibility check accepts any version `<= QUOR_PLUGIN_API_VERSION`
55
+ and rejects only newer ones — plugins built against an older API keep
56
+ working as the API evolves.
57
+ - `file://` escape hatch for loading a local `StageHandler` during
58
+ development without packaging it.
59
+ - `quor doctor` plugin diagnostics: lists discovered stages and plugins
60
+ (including each plugin's declared version), and flags load failures.
61
+ Tier is deliberately not reported for entry-point-discovered plugins —
62
+ `importlib.metadata` carries no signal that maps to project/user/builtin,
63
+ so this is a documented scope boundary, not a bug (see DECISIONS.md,
64
+ ADR "Plugin Architecture — Two-Tier Separation").
65
+ - End-to-end fail-open verification: a real (non-mock) plugin that raises
66
+ during `execute()` is driven through the actual dispatcher, confirming
67
+ the exception is isolated, a warning is emitted, and the hook still
68
+ returns valid output.
69
+
70
+ ### Release Hardening
71
+
72
+ A dedicated pass to close reliability gaps before packaging:
73
+
74
+ - Eliminated the last local-machine dependency in the test suite (CLI
75
+ tests previously read the developer's real `~/.claude/settings.json`);
76
+ all tests now inject an isolated settings path.
77
+ - `ruff` and `mypy` are exact-pinned in dev dependencies; `pytest`/`pytest-cov`
78
+ use bounded ranges. CI now lints `tests/` as well as `quor/` (ADR
79
+ "Release Hardening — Dev Tooling Version Policy & CI Lint Scope").
80
+ - Removed `ExitCode.PLUGIN_ERROR` as dead code — every `PluginError` is
81
+ caught internally by Quor's fail-open contract and never reaches a
82
+ process exit code.
83
+ - Python compatibility verified by actually running the full suite (ruff,
84
+ mypy, pytest) on Python 3.11, 3.13, and 3.14 in isolated virtual
85
+ environments, not just static review.
86
+
87
+ ### Testing
88
+
89
+ - **605 tests passing**, `ruff` and `mypy` clean on both `quor/` and
90
+ `tests/`.
91
+ - ≥80% coverage on `quor/pipeline/`, `quor/filters/`, and `quor/rewrite/`
92
+ (93% overall).
93
+ - Dedicated chaos/fail-open suite: corrupted TOML, malformed hook JSON,
94
+ permission errors, hook timeout, pathological regex (ReDoS) — all
95
+ degrade to the original, unfiltered output rather than crashing or
96
+ losing data.
97
+ - Error-safety snapshot tests across all 7 built-in filters, confirming
98
+ failure-relevant lines are never removed.
99
+ - CI on `windows-latest` and `ubuntu-latest`; a weekly canary workflow
100
+ installs unpinned `@anthropic-ai/claude-code` to catch upstream hook
101
+ format changes before users do.
102
+
103
+ ### Known limitations
104
+
105
+ - The `AUDIT` / `OPTIMIZE` / `SIMULATE` operating-mode system is
106
+ display-only in this release — `quor doctor` and `quor gain` show the
107
+ configured mode, but the dispatcher does not yet branch on it. This is
108
+ an intentional, scoped roadmap item (see PROJECT_STATUS.md), not a bug.
109
+ - Not yet published to PyPI.
110
+
111
+ [0.1.0]: https://github.com/priyanshup/Quor/releases/tag/v0.1.0
@@ -0,0 +1,69 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in
6
+ our community a harassment-free experience for everyone, regardless of age,
7
+ body size, visible or invisible disability, ethnicity, sex characteristics,
8
+ gender identity and expression, level of experience, education,
9
+ socio-economic status, nationality, personal appearance, race, religion, or
10
+ sexual identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment:
18
+
19
+ - Demonstrating empathy and kindness toward other people
20
+ - Being respectful of differing opinions, viewpoints, and experiences
21
+ - Giving and gracefully accepting constructive feedback
22
+ - Accepting responsibility and apologizing for mistakes, and learning from them
23
+ - Focusing on what is best for the community, not just the individual
24
+
25
+ Examples of unacceptable behavior:
26
+
27
+ - The use of sexualized language or imagery, and unwelcome sexual attention
28
+ - Trolling, insulting or derogatory comments, and personal or political attacks
29
+ - Public or private harassment
30
+ - Publishing others' private information without explicit permission
31
+ - Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ Technical disagreements are settled by evidence — benchmark results, test
35
+ cases, and precedent in `docs/final/DECISIONS.md` — not by persistence or
36
+ volume. See `CONTRIBUTING.md` for how design and review decisions are made.
37
+
38
+ ## Enforcement Responsibilities
39
+
40
+ Project maintainers are responsible for clarifying and enforcing our
41
+ standards of acceptable behavior and will take appropriate and fair
42
+ corrective action in response to any behavior deemed inappropriate,
43
+ threatening, offensive, or harmful.
44
+
45
+ ## Scope
46
+
47
+ This Code of Conduct applies within all community spaces (issues, pull
48
+ requests, discussions) and also applies when an individual is officially
49
+ representing the project in public spaces.
50
+
51
+ ## Enforcement
52
+
53
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
54
+ reported to the maintainers by opening a confidential report via GitHub
55
+ (see the repository's Security & Conduct contact in `SECURITY.md`), or by
56
+ emailing the maintainer listed in the repository's commit history. All
57
+ complaints will be reviewed and investigated promptly and fairly.
58
+
59
+ This project does not yet have a dedicated conduct-response team — as a
60
+ pre-1.0, single-maintainer project, reports are currently handled directly
61
+ by the project owner. This will be revisited as the contributor base grows.
62
+
63
+ ## Attribution
64
+
65
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
66
+ version 2.1, available at
67
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
68
+
69
+ [homepage]: https://www.contributor-covenant.org