generic-ml-wrapper 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.
- generic_ml_wrapper-0.1.0/.coveragerc +4 -0
- generic_ml_wrapper-0.1.0/.gitattributes +1 -0
- generic_ml_wrapper-0.1.0/.github/workflows/ci.yml +112 -0
- generic_ml_wrapper-0.1.0/.github/workflows/pr-hygiene.yml +47 -0
- generic_ml_wrapper-0.1.0/.github/workflows/release.yml +28 -0
- generic_ml_wrapper-0.1.0/.github/workflows/sonar.yml +34 -0
- generic_ml_wrapper-0.1.0/.gitignore +44 -0
- generic_ml_wrapper-0.1.0/.importlinter +40 -0
- generic_ml_wrapper-0.1.0/.pre-commit-config.yaml +57 -0
- generic_ml_wrapper-0.1.0/AGENTS.md +31 -0
- generic_ml_wrapper-0.1.0/CHANGELOG.md +45 -0
- generic_ml_wrapper-0.1.0/CODE_OF_CONDUCT.md +89 -0
- generic_ml_wrapper-0.1.0/CONTRIBUTING.md +94 -0
- generic_ml_wrapper-0.1.0/GOVERNANCE.md +95 -0
- generic_ml_wrapper-0.1.0/LICENSE +201 -0
- generic_ml_wrapper-0.1.0/NOTICE +8 -0
- generic_ml_wrapper-0.1.0/PKG-INFO +172 -0
- generic_ml_wrapper-0.1.0/README.md +136 -0
- generic_ml_wrapper-0.1.0/SECURITY.md +122 -0
- generic_ml_wrapper-0.1.0/VERSION +1 -0
- generic_ml_wrapper-0.1.0/docs/DESIGN.md +328 -0
- generic_ml_wrapper-0.1.0/docs/images/gmlw-favicon.svg +16 -0
- generic_ml_wrapper-0.1.0/docs/images/gmlw-lockup-dark.svg +11 -0
- generic_ml_wrapper-0.1.0/docs/images/gmlw-lockup.svg +11 -0
- generic_ml_wrapper-0.1.0/docs/images/gmlw-statusline.gif +0 -0
- generic_ml_wrapper-0.1.0/docs/images/gmlw-usage.gif +0 -0
- generic_ml_wrapper-0.1.0/docs/tapes/README.md +24 -0
- generic_ml_wrapper-0.1.0/docs/tapes/render.sh +49 -0
- generic_ml_wrapper-0.1.0/docs/tapes/seed.py +42 -0
- generic_ml_wrapper-0.1.0/docs/tapes/statusline.tape +23 -0
- generic_ml_wrapper-0.1.0/docs/tapes/usage.tape +24 -0
- generic_ml_wrapper-0.1.0/noxfile.py +136 -0
- generic_ml_wrapper-0.1.0/pyproject.toml +116 -0
- generic_ml_wrapper-0.1.0/pyrightconfig.json +17 -0
- generic_ml_wrapper-0.1.0/sonar-project.properties +14 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/__init__.py +13 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/inbound/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/inbound/cli/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/inbound/cli/app.py +369 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/inbound/cli/banner.py +30 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/bootstrap/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/bootstrap/filesystem_layout_seeder.py +104 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/claude_cli_caller.py +158 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/codex_cli_caller.py +165 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/context_file.py +44 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/context_opening.py +27 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/cursor_cli_caller.py +98 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/default_provider.py +79 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/loader.py +34 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/status_line_config.py +142 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/vibe_cli_caller.py +169 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/caller/vibe_config.py +128 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/credentials/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/credentials/filesystem_credentials_store.py +130 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/gateway/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/gateway/anthropic_sse.py +148 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/gateway/openai_chat.py +112 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/gateway/openai_responses.py +85 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/gateway/relay.py +402 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/interceptor/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/interceptor/compressor.py +107 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/interceptor/size_logger.py +32 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/status/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/status/claude_status_parser.py +76 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/status/cursor_status_parser.py +71 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/filesystem_transcript_store.py +65 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/ledger.py +104 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/sqlite_per_turn_store.py +68 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/sqlite_session_store.py +73 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/store/sqlite_usage_store.py +44 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/workflow/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/workflow/filesystem_workflow_source.py +165 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/workspace/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/adapter/outbound/workspace/local_workspace_inspector.py +75 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/client_status.py +44 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/identifiers.py +76 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/run.py +35 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/session.py +24 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/turn_usage.py +62 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/model/workspace.py +31 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/interceptor.py +30 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/interceptor_chain.py +57 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/rule_cleaner.py +35 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/session_naming.py +30 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/domain/service/statusline_renderer.py +81 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/bootstrap.py +15 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/export_usage.py +109 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/list_jobs.py +33 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/list_sessions.py +36 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/list_workflows.py +19 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/new_workflow.py +48 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/render_statusline.py +24 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/set_credential.py +35 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/inbound/start_job.py +53 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/cli_caller.py +97 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/client_status.py +27 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/credentials_store.py +36 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/interceptor.py +25 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/layout_seeder.py +18 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/per_turn_metering.py +38 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/session_store.py +62 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/transcript.py +46 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/usage_store.py +32 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/workflow_source.py +58 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/port/outbound/workspace.py +27 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/bootstrap.py +24 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/export_usage.py +93 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/list_jobs.py +31 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/list_sessions.py +34 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/list_workflows.py +28 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/new_workflow.py +109 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/render_statusline.py +117 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/set_credential.py +27 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/usecase/start_job.py +125 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/wiring/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/application/wiring/composition.py +230 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/common/__init__.py +3 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/common/config.py +179 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/common/log.py +83 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/common/paths.py +25 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/common/spec_loader.py +67 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/py.typed +0 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/resources/workflows/_common/base.md +25 -0
- generic_ml_wrapper-0.1.0/src/generic_ml_wrapper/resources/workflows/create-workflow/workflow.md +35 -0
- generic_ml_wrapper-0.1.0/tests/_conformance.py +301 -0
- generic_ml_wrapper-0.1.0/tests/conftest.py +34 -0
- generic_ml_wrapper-0.1.0/tests/test_anthropic_sse.py +90 -0
- generic_ml_wrapper-0.1.0/tests/test_authoring_separation.py +42 -0
- generic_ml_wrapper-0.1.0/tests/test_banner.py +28 -0
- generic_ml_wrapper-0.1.0/tests/test_bootstrap.py +51 -0
- generic_ml_wrapper-0.1.0/tests/test_caller_loader.py +45 -0
- generic_ml_wrapper-0.1.0/tests/test_callers.py +499 -0
- generic_ml_wrapper-0.1.0/tests/test_cli.py +505 -0
- generic_ml_wrapper-0.1.0/tests/test_compressor.py +88 -0
- generic_ml_wrapper-0.1.0/tests/test_config.py +92 -0
- generic_ml_wrapper-0.1.0/tests/test_context_file.py +36 -0
- generic_ml_wrapper-0.1.0/tests/test_credentials_store.py +80 -0
- generic_ml_wrapper-0.1.0/tests/test_cursor_status_parser.py +54 -0
- generic_ml_wrapper-0.1.0/tests/test_export_usage_usecase.py +89 -0
- generic_ml_wrapper-0.1.0/tests/test_filesystem_transcript_store.py +43 -0
- generic_ml_wrapper-0.1.0/tests/test_filesystem_workflow_source.py +135 -0
- generic_ml_wrapper-0.1.0/tests/test_identifiers.py +63 -0
- generic_ml_wrapper-0.1.0/tests/test_interceptor_chain.py +65 -0
- generic_ml_wrapper-0.1.0/tests/test_list_jobs_usecase.py +41 -0
- generic_ml_wrapper-0.1.0/tests/test_list_sessions_usecase.py +45 -0
- generic_ml_wrapper-0.1.0/tests/test_list_workflows_usecase.py +34 -0
- generic_ml_wrapper-0.1.0/tests/test_local_workspace_inspector.py +77 -0
- generic_ml_wrapper-0.1.0/tests/test_log.py +37 -0
- generic_ml_wrapper-0.1.0/tests/test_model_invariants.py +66 -0
- generic_ml_wrapper-0.1.0/tests/test_new_workflow_usecase.py +115 -0
- generic_ml_wrapper-0.1.0/tests/test_openai_chat.py +66 -0
- generic_ml_wrapper-0.1.0/tests/test_openai_responses.py +36 -0
- generic_ml_wrapper-0.1.0/tests/test_relay.py +363 -0
- generic_ml_wrapper-0.1.0/tests/test_rule_cleaner.py +36 -0
- generic_ml_wrapper-0.1.0/tests/test_session_naming.py +21 -0
- generic_ml_wrapper-0.1.0/tests/test_size_logger.py +25 -0
- generic_ml_wrapper-0.1.0/tests/test_smoke.py +20 -0
- generic_ml_wrapper-0.1.0/tests/test_sqlite_per_turn_store.py +38 -0
- generic_ml_wrapper-0.1.0/tests/test_sqlite_session_store.py +65 -0
- generic_ml_wrapper-0.1.0/tests/test_sqlite_usage_store.py +43 -0
- generic_ml_wrapper-0.1.0/tests/test_start_job_usecase.py +234 -0
- generic_ml_wrapper-0.1.0/tests/test_status_line_config.py +68 -0
- generic_ml_wrapper-0.1.0/tests/test_statusline.py +211 -0
- generic_ml_wrapper-0.1.0/tests/test_store_conformance.py +103 -0
- generic_ml_wrapper-0.1.0/tests/test_vibe_config.py +70 -0
- generic_ml_wrapper-0.1.0/tools/hooks/check-commit-msg.sh +14 -0
- generic_ml_wrapper-0.1.0/tools/hooks/guard-branch.sh +23 -0
- generic_ml_wrapper-0.1.0/uv.lock +727 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* text=auto
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Cancel superseded runs on the same ref (e.g. rapid pushes to a PR).
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
# CI is a thin caller of nox (see noxfile.py), run through uv from the committed
|
|
18
|
+
# uv.lock -- so the check here is byte-for-byte the local gate AND every build-time
|
|
19
|
+
# input is pinned: GitHub Actions by commit SHA, uv by [tool.uv] required-version,
|
|
20
|
+
# and nox by the lock (`uv run --frozen nox`). A dependency -- or a tool that runs
|
|
21
|
+
# the gate -- can only move via a deliberate `uv lock --upgrade` or a SHA bump.
|
|
22
|
+
# lint/typecheck/imports/coverage are version-independent (their own single jobs, no
|
|
23
|
+
# --python filter, which would silently drop a session declaring no interpreter);
|
|
24
|
+
# tests fan out across the interpreter matrix.
|
|
25
|
+
env:
|
|
26
|
+
UV_CACHE_DIR: ${{ github.workspace }}/.uv-cache
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
test:
|
|
30
|
+
name: test (${{ matrix.os }}, py${{ matrix.python-version }})
|
|
31
|
+
runs-on: ${{ matrix.os }}
|
|
32
|
+
strategy:
|
|
33
|
+
fail-fast: false
|
|
34
|
+
matrix:
|
|
35
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
36
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
39
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
40
|
+
with:
|
|
41
|
+
python-version: ${{ matrix.python-version }}
|
|
42
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
43
|
+
- name: Run tests (nox from the lock)
|
|
44
|
+
run: uv run --frozen nox --python ${{ matrix.python-version }} -s tests
|
|
45
|
+
|
|
46
|
+
lint:
|
|
47
|
+
name: lint (ruff)
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
51
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.13"
|
|
54
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
55
|
+
- name: Lint + format check (nox from the lock)
|
|
56
|
+
run: uv run --frozen nox -s lint
|
|
57
|
+
|
|
58
|
+
typecheck:
|
|
59
|
+
name: typecheck (pyright)
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
63
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
64
|
+
with:
|
|
65
|
+
python-version: "3.13"
|
|
66
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
67
|
+
- name: Type check (nox from the lock)
|
|
68
|
+
run: uv run --frozen nox -s typecheck
|
|
69
|
+
|
|
70
|
+
import-contracts:
|
|
71
|
+
name: architecture contracts (import-linter)
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
75
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
76
|
+
with:
|
|
77
|
+
python-version: "3.13"
|
|
78
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
79
|
+
- name: Check architecture contracts (nox from the lock)
|
|
80
|
+
run: uv run --frozen nox -s imports
|
|
81
|
+
|
|
82
|
+
coverage:
|
|
83
|
+
name: coverage (>=80%)
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
87
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
88
|
+
with:
|
|
89
|
+
python-version: "3.13"
|
|
90
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
91
|
+
- name: Tests with coverage floor (nox from the lock)
|
|
92
|
+
run: uv run --frozen nox -s coverage
|
|
93
|
+
|
|
94
|
+
# A single, stable check that rolls up every gate job, so branch protection can
|
|
95
|
+
# require "ci" instead of every matrix + gate job name.
|
|
96
|
+
ci:
|
|
97
|
+
name: ci
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
needs: [test, lint, typecheck, import-contracts, coverage]
|
|
100
|
+
if: ${{ always() }}
|
|
101
|
+
steps:
|
|
102
|
+
- name: All gate jobs passed
|
|
103
|
+
run: |
|
|
104
|
+
results="${{ needs.test.result }} ${{ needs.lint.result }} ${{ needs.typecheck.result }} ${{ needs.import-contracts.result }} ${{ needs.coverage.result }}"
|
|
105
|
+
echo "gate results: $results"
|
|
106
|
+
for r in $results; do
|
|
107
|
+
if [ "$r" != "success" ]; then
|
|
108
|
+
echo "a gate job did not pass"
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
done
|
|
112
|
+
echo "all gate jobs passed"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: PR hygiene
|
|
2
|
+
|
|
3
|
+
# Server-side enforcement of the "no AI attribution" rule. The commit-msg hook
|
|
4
|
+
# (tools/hooks/check-commit-msg.sh) covers commit messages locally, but hooks are
|
|
5
|
+
# opt-in and `--no-verify`-bypassable, and a PR's title/body live only on GitHub.
|
|
6
|
+
# This required check gives the rule teeth: it scans the PR title, body, AND every
|
|
7
|
+
# commit message in the PR, with the SAME regex the local hook uses.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [opened, edited, reopened, synchronize]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pull-requests: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
no-ai-attribution:
|
|
19
|
+
name: no AI attribution
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
env:
|
|
22
|
+
# Kept byte-for-byte in sync with tools/hooks/check-commit-msg.sh.
|
|
23
|
+
PATTERN: 'co-authored-by:[[:space:]]*.*(claude|copilot|chatgpt|gpt-|anthropic|openai)|generated with[[:space:]]+\[?(claude|copilot|chatgpt)|🤖[[:space:]]*generated'
|
|
24
|
+
steps:
|
|
25
|
+
- name: Check PR title and body
|
|
26
|
+
env:
|
|
27
|
+
TITLE: ${{ github.event.pull_request.title }}
|
|
28
|
+
BODY: ${{ github.event.pull_request.body }}
|
|
29
|
+
run: |
|
|
30
|
+
if printf '%s\n%s' "$TITLE" "$BODY" | grep -qiE "$PATTERN"; then
|
|
31
|
+
echo "::error::PR title or body contains AI attribution -- please remove it."
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
echo "OK: no AI attribution in PR title/body."
|
|
35
|
+
|
|
36
|
+
- name: Check commit messages
|
|
37
|
+
env:
|
|
38
|
+
GH_TOKEN: ${{ github.token }}
|
|
39
|
+
run: |
|
|
40
|
+
msgs=$(gh api --paginate \
|
|
41
|
+
"repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits" \
|
|
42
|
+
--jq '.[].commit.message')
|
|
43
|
+
if printf '%s' "$msgs" | grep -qiE "$PATTERN"; then
|
|
44
|
+
echo "::error::A commit message contains AI attribution -- please amend it."
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
echo "OK: no AI attribution in commit messages."
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Publish to PyPI via Trusted Publishing (OIDC) when a GitHub Release is published.
|
|
2
|
+
# No token or secret: PyPI trusts this exact repo + workflow + environment, and
|
|
3
|
+
# GitHub mints a short-lived OIDC credential for the upload. Every build-time input
|
|
4
|
+
# is pinned (Actions by commit SHA, uv by [tool.uv] required-version).
|
|
5
|
+
name: release
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
release:
|
|
9
|
+
types: [published]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
pypi-publish:
|
|
16
|
+
name: Build and publish to PyPI
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment: pypi
|
|
19
|
+
permissions:
|
|
20
|
+
id-token: write # OIDC token for Trusted Publishing
|
|
21
|
+
contents: read
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
24
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
25
|
+
- name: Build sdist + wheel
|
|
26
|
+
run: uv build
|
|
27
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
28
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Sonar
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
sonar:
|
|
14
|
+
name: SonarQube Cloud
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # full history so Sonar can attribute new code and blame
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
|
|
25
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
26
|
+
|
|
27
|
+
# Thin caller of nox (see noxfile.py): the `sonar` session writes coverage.xml
|
|
28
|
+
# (path in sonar-project.properties) that the scan below ingests.
|
|
29
|
+
- name: Run tests with coverage (nox from the lock)
|
|
30
|
+
run: uv run --frozen nox -s sonar
|
|
31
|
+
|
|
32
|
+
- uses: SonarSource/sonarqube-scan-action@713881670b6b3676cda39549040e2d88c70d582e # v8
|
|
33
|
+
env:
|
|
34
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Byte-compiled / optimized
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Test / coverage / tooling caches
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
htmlcov/
|
|
22
|
+
.nox/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
coverage.xml
|
|
25
|
+
|
|
26
|
+
# Editors / OS / tools
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
29
|
+
.claude/
|
|
30
|
+
.DS_Store
|
|
31
|
+
Thumbs.db
|
|
32
|
+
|
|
33
|
+
# Secrets — nothing secret belongs in this public repo, but guard anyway
|
|
34
|
+
.env
|
|
35
|
+
*.pem
|
|
36
|
+
*.key
|
|
37
|
+
secrets/
|
|
38
|
+
credentials.toml
|
|
39
|
+
|
|
40
|
+
# Local bootstrap/publish tooling — never published.
|
|
41
|
+
# secret-audit.sh contains the literal private terms it scans for, so it must
|
|
42
|
+
# not be committed (it would reintroduce the very leak it checks for).
|
|
43
|
+
/setup-repo.sh
|
|
44
|
+
/secret-audit.sh
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[importlinter]
|
|
2
|
+
root_package = generic_ml_wrapper
|
|
3
|
+
|
|
4
|
+
# Rule 1 — domain purity. The innermost ring models the world; it must not import
|
|
5
|
+
# use cases, ports, the composition root, or any adapter. (The interceptor_chain ->
|
|
6
|
+
# port.outbound.interceptor edge this catches was the sole violation at review time,
|
|
7
|
+
# fixed by defining the Interceptor abstraction in the domain and having the outbound
|
|
8
|
+
# InterceptorPort extend it.)
|
|
9
|
+
[importlinter:contract:domain-purity]
|
|
10
|
+
name = Domain must not import use cases, ports, wiring, or adapters
|
|
11
|
+
type = forbidden
|
|
12
|
+
source_modules =
|
|
13
|
+
generic_ml_wrapper.application.domain
|
|
14
|
+
forbidden_modules =
|
|
15
|
+
generic_ml_wrapper.application.usecase
|
|
16
|
+
generic_ml_wrapper.application.port
|
|
17
|
+
generic_ml_wrapper.application.wiring
|
|
18
|
+
generic_ml_wrapper.adapter
|
|
19
|
+
|
|
20
|
+
# Rule 2 — the application ring (domain + use cases + ports) is independent of the
|
|
21
|
+
# adapter and composition layers. Dependencies point inward, never outward.
|
|
22
|
+
[importlinter:contract:application-ring]
|
|
23
|
+
name = Application ring must not import adapters or the composition root
|
|
24
|
+
type = forbidden
|
|
25
|
+
source_modules =
|
|
26
|
+
generic_ml_wrapper.application.domain
|
|
27
|
+
generic_ml_wrapper.application.usecase
|
|
28
|
+
generic_ml_wrapper.application.port
|
|
29
|
+
forbidden_modules =
|
|
30
|
+
generic_ml_wrapper.adapter
|
|
31
|
+
generic_ml_wrapper.application.wiring
|
|
32
|
+
|
|
33
|
+
# Rule 3 — ports are abstractions; use cases depend on them, never the reverse.
|
|
34
|
+
[importlinter:contract:ports-no-usecases]
|
|
35
|
+
name = Ports must not import use cases
|
|
36
|
+
type = forbidden
|
|
37
|
+
source_modules =
|
|
38
|
+
generic_ml_wrapper.application.port
|
|
39
|
+
forbidden_modules =
|
|
40
|
+
generic_ml_wrapper.application.usecase
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# .pre-commit-config.yaml -- the gates that run BEFORE a commit is written.
|
|
2
|
+
#
|
|
3
|
+
# A failing hook rejects the commit, so malformed code can never reach local
|
|
4
|
+
# history (and therefore can never be pushed). The linters are local hooks backed
|
|
5
|
+
# by the project's own .venv, so no extra environment and no network is needed.
|
|
6
|
+
#
|
|
7
|
+
# One-time setup after cloning:
|
|
8
|
+
# nox -s dev
|
|
9
|
+
# .venv/bin/pre-commit install
|
|
10
|
+
# .venv/bin/pre-commit install --hook-type commit-msg
|
|
11
|
+
#
|
|
12
|
+
# Run manually against all files:
|
|
13
|
+
# .venv/bin/pre-commit run --all-files
|
|
14
|
+
#
|
|
15
|
+
repos:
|
|
16
|
+
- repo: local
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff-check
|
|
19
|
+
name: ruff (lint)
|
|
20
|
+
entry: .venv/bin/ruff check .
|
|
21
|
+
language: system
|
|
22
|
+
pass_filenames: false
|
|
23
|
+
types: [python]
|
|
24
|
+
|
|
25
|
+
- id: lint-imports
|
|
26
|
+
name: import-linter (architecture contracts)
|
|
27
|
+
entry: .venv/bin/lint-imports
|
|
28
|
+
language: system
|
|
29
|
+
pass_filenames: false
|
|
30
|
+
types: [python]
|
|
31
|
+
|
|
32
|
+
- id: ruff-format
|
|
33
|
+
name: ruff (format check)
|
|
34
|
+
entry: .venv/bin/ruff format --check .
|
|
35
|
+
language: system
|
|
36
|
+
pass_filenames: false
|
|
37
|
+
types: [python]
|
|
38
|
+
|
|
39
|
+
- id: pyright
|
|
40
|
+
name: pyright (strict type checking)
|
|
41
|
+
entry: .venv/bin/pyright --pythonpath .venv/bin/python
|
|
42
|
+
language: system
|
|
43
|
+
pass_filenames: false
|
|
44
|
+
types: [python]
|
|
45
|
+
|
|
46
|
+
- id: guard-branch
|
|
47
|
+
name: branch guard (no commits to main; naming convention)
|
|
48
|
+
entry: tools/hooks/guard-branch.sh
|
|
49
|
+
language: script
|
|
50
|
+
pass_filenames: false
|
|
51
|
+
always_run: true
|
|
52
|
+
|
|
53
|
+
- id: no-ai-attribution
|
|
54
|
+
name: no AI attribution in commit message
|
|
55
|
+
entry: tools/hooks/check-commit-msg.sh
|
|
56
|
+
language: script
|
|
57
|
+
stages: [commit-msg]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# AGENTS.md — working rules for this repo
|
|
2
|
+
|
|
3
|
+
Rules an automated or human contributor must follow. Where a rule can be a
|
|
4
|
+
runnable gate it is one (see `.pre-commit-config.yaml` and `noxfile.py`); prose
|
|
5
|
+
here is the intent behind those gates.
|
|
6
|
+
|
|
7
|
+
## Process
|
|
8
|
+
|
|
9
|
+
- **Never commit directly to `main`.** Work on a branch named
|
|
10
|
+
`feature/… tech/… fix/… release/… docs/… chore/… test/…`. Enforced by
|
|
11
|
+
`tools/hooks/guard-branch.sh`.
|
|
12
|
+
- **`nox -s green` must pass before every commit** — ruff (lint + format), import
|
|
13
|
+
contracts, pyright strict, tests + coverage floor. The gate that runs locally is the
|
|
14
|
+
one CI runs. `main` requires the `ci` and `no AI attribution` checks (branch protection).
|
|
15
|
+
- **Keep changes small and cohesive**, each landing with its tests and leaving the gate
|
|
16
|
+
green. The pipeline is never red.
|
|
17
|
+
- **No AI/assistant attribution** in commit messages or PRs. Enforced locally by
|
|
18
|
+
`tools/hooks/check-commit-msg.sh` and server-side by the `pr-hygiene` workflow.
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
- The design is `docs/DESIGN.md`; do not re-derive it. Follow the hexagon:
|
|
23
|
+
**dependencies point inward** (domain ← usecase ← ports; adapters depend on
|
|
24
|
+
ports, never the reverse).
|
|
25
|
+
- **Public-clean by construction.** No personal data, employer, internal hosts, or
|
|
26
|
+
private protocols in this repo — real content lives only under `~/.gmlw`. Commit with a
|
|
27
|
+
public identity (a GitHub no-reply address), never a private/work one; the gitignored
|
|
28
|
+
`secret-audit.sh` gates every publish for private terms and secrets.
|
|
29
|
+
- **Strict typing.** pyright strict, zero errors, over **both `src/` and `tests/`**,
|
|
30
|
+
pinned to `pyright==1.1.411`. `# type: ignore` only for a provably-safe case that cannot
|
|
31
|
+
be expressed in the type system, with a comment.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres
|
|
5
|
+
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-07-14
|
|
10
|
+
|
|
11
|
+
First public release — a metering wrapper around ML coding CLIs.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- **Jobs & sessions.** Enter at a **job** you tag; the wrapper mints a named,
|
|
15
|
+
resumable **session** on the client and persists it. `gmlw start`, `jobs`,
|
|
16
|
+
`sessions`, `export`.
|
|
17
|
+
- **Four clients** driven the same way: **claude**, **cursor**, **codex**, **vibe**.
|
|
18
|
+
Which one is config-driven (`[client]`) or `--client`.
|
|
19
|
+
- **Metering relay.** A local, capability-URL-authenticated relay records **per-turn
|
|
20
|
+
tokens and cost** for the metered clients (claude/codex/vibe) into a SQLite ledger;
|
|
21
|
+
`gmlw export` reports per-turn rows, per-model totals, and per-session cost.
|
|
22
|
+
- **Status line** for the clients that host one (claude, cursor): git · folder ·
|
|
23
|
+
model · context% · a client-specific allowance block, plus a per-session and
|
|
24
|
+
per-job usage footer.
|
|
25
|
+
- **Workflows.** Author a small operating context once (`gmlw workflow new`, a warm
|
|
26
|
+
create-workflow interview) and launch a job with it; context is compiled from a
|
|
27
|
+
shared base + your profile + rules + the workflow steps, through an interceptor
|
|
28
|
+
chain (with opt-in context compression via `generic-ml-cache`).
|
|
29
|
+
- **Durable provenance.** The exact compiled context is written per session
|
|
30
|
+
(`contexts/<job>/<session>.context.md`); an **opt-in transcript** keeps each metered
|
|
31
|
+
call's request/response/usage.
|
|
32
|
+
- **Credentials.** Per-workflow secrets stored `0600` and injected into the client's
|
|
33
|
+
environment at launch (`gmlw creds set`).
|
|
34
|
+
- **Storage.** A single SQLite ledger (`~/.gmlw/ledger.db`, WAL) for jobs, sessions,
|
|
35
|
+
per-turn usage, and session costs.
|
|
36
|
+
- **Safety.** Validated `JobId` + filesystem containment under an owner-only `~/.gmlw`;
|
|
37
|
+
never overwriting an unparseable client-settings or credentials file.
|
|
38
|
+
|
|
39
|
+
### Engineering
|
|
40
|
+
- Hexagonal (ports & adapters), enforced by `import-linter`; strict `ruff` + `pyright`
|
|
41
|
+
over `src` and `tests`; `nox` gates mirrored by CI across Python 3.11–3.14; a
|
|
42
|
+
server-side no-AI-attribution check and branch protection.
|
|
43
|
+
|
|
44
|
+
[Unreleased]: https://github.com/danielslobozian/generic-ml-wrapper/compare/v0.1.0...HEAD
|
|
45
|
+
[0.1.0]: https://github.com/danielslobozian/generic-ml-wrapper/releases/tag/v0.1.0
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributor Covenant 3.0 Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We pledge to make our community welcoming, safe, and equitable for all.
|
|
6
|
+
|
|
7
|
+
We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Encouraged Behaviors
|
|
11
|
+
|
|
12
|
+
While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
|
|
13
|
+
|
|
14
|
+
With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
|
|
15
|
+
|
|
16
|
+
1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
|
|
17
|
+
2. Engaging **kindly and honestly** with others.
|
|
18
|
+
3. Respecting **different viewpoints** and experiences.
|
|
19
|
+
4. **Taking responsibility** for our actions and contributions.
|
|
20
|
+
5. Gracefully giving and accepting **constructive feedback**.
|
|
21
|
+
6. Committing to **repairing harm** when it occurs.
|
|
22
|
+
7. Behaving in other ways that promote and sustain the **well-being of our community**.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Restricted Behaviors
|
|
26
|
+
|
|
27
|
+
We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
|
|
28
|
+
|
|
29
|
+
1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
|
|
30
|
+
2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
|
|
31
|
+
3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
|
|
32
|
+
4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
|
|
33
|
+
5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
|
|
34
|
+
6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
|
|
35
|
+
7. Behaving in other ways that **threaten the well-being** of our community.
|
|
36
|
+
|
|
37
|
+
### Other Restrictions
|
|
38
|
+
|
|
39
|
+
1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
|
|
40
|
+
2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
|
|
41
|
+
3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
|
|
42
|
+
4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## Reporting an Issue
|
|
46
|
+
|
|
47
|
+
Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
|
|
48
|
+
|
|
49
|
+
When an incident does occur, it is important to report it promptly. To report a possible violation, please use the repository's **private reporting** channel: go to the **Security** tab and choose **Report a vulnerability**, which opens a confidential advisory visible only to the maintainers — note clearly at the top that it is a **Code of Conduct report**, not a security issue. This keeps the report private without requiring an email address. If you would rather not use that channel, or if a maintainer is the subject of the report, you may report the account or content directly to GitHub through [GitHub's report abuse flow](https://github.com/contact/report-abuse). Please do **not** open a public issue for a Code of Conduct concern.
|
|
50
|
+
|
|
51
|
+
The maintainers act as this project's Community Moderators. They take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Addressing and Repairing Harm
|
|
55
|
+
|
|
56
|
+
If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
|
|
57
|
+
|
|
58
|
+
1) Warning
|
|
59
|
+
1) Event: A violation involving a single incident or series of incidents.
|
|
60
|
+
2) Consequence: A private, written warning from the Community Moderators.
|
|
61
|
+
3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
|
|
62
|
+
2) Temporarily Limited Activities
|
|
63
|
+
1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
|
|
64
|
+
2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
|
|
65
|
+
3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
|
|
66
|
+
3) Temporary Suspension
|
|
67
|
+
1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
|
|
68
|
+
2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
|
|
69
|
+
3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
|
|
70
|
+
4) Permanent Ban
|
|
71
|
+
1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
|
|
72
|
+
2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
|
|
73
|
+
3) Repair: There is no possible repair in cases of this severity.
|
|
74
|
+
|
|
75
|
+
This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## Scope
|
|
79
|
+
|
|
80
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Attribution
|
|
84
|
+
|
|
85
|
+
This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
|
|
86
|
+
|
|
87
|
+
Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
|
|
88
|
+
|
|
89
|
+
For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in `generic-ml-wrapper`. This is an open project under the
|
|
4
|
+
Apache-2.0 license and contributions are welcome — bug reports, documentation, tests,
|
|
5
|
+
and code alike.
|
|
6
|
+
|
|
7
|
+
By participating you agree to abide by the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
8
|
+
|
|
9
|
+
## Ways to help
|
|
10
|
+
|
|
11
|
+
- **Report a bug.** Open an issue describing what you ran and what happened. A minimal
|
|
12
|
+
repro — the exact `gmlw` command, the client, and a failing test if you can — is gold.
|
|
13
|
+
- **Propose a feature.** Open an issue and check [`docs/DESIGN.md`](docs/DESIGN.md)
|
|
14
|
+
first — the design invariants there are load-bearing, and your idea may already be
|
|
15
|
+
covered or deliberately out of scope.
|
|
16
|
+
- **Improve the docs.** If something was unclear, a doc fix helps the next person.
|
|
17
|
+
- **Send code.** See below.
|
|
18
|
+
|
|
19
|
+
## Development setup
|
|
20
|
+
|
|
21
|
+
You need Python 3.11–3.13 and [`uv`](https://docs.astral.sh/uv/). The gates run through
|
|
22
|
+
[`nox`](https://nox.thea.codes/) with the `uv` backend, from the committed `uv.lock`.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/danielslobozian/generic-ml-wrapper.git
|
|
26
|
+
cd generic-ml-wrapper
|
|
27
|
+
uv sync --extra dev # install the project + dev tools into .venv
|
|
28
|
+
# or: nox -s dev # build the IDE .venv the same way
|
|
29
|
+
pre-commit install # wire the local git hooks (optional but recommended)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The only runtime dependencies are the sibling
|
|
33
|
+
[`generic-ml-cache`](https://github.com/danielslobozian/generic-ml-cache) packages
|
|
34
|
+
(`-core`, `-adapters`, `-bootstrap`) — used by the context compressor. Dev tooling
|
|
35
|
+
(`pytest`, `ruff`, `pyright`, `import-linter`, `nox`, `pre-commit`) lives in the `dev`
|
|
36
|
+
extra / dependency group and is pinned by the lock.
|
|
37
|
+
|
|
38
|
+
## The gate
|
|
39
|
+
|
|
40
|
+
The gates are defined once in [`noxfile.py`](noxfile.py); CI is a thin caller of them,
|
|
41
|
+
so what runs locally is byte-for-byte what runs in CI.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
nox # the default gate: lint · imports · typecheck · tests (3.11–3.13)
|
|
45
|
+
nox -s green # the whole gate in one env: lint · format · imports · pyright · coverage
|
|
46
|
+
nox -s tests # just the test matrix
|
|
47
|
+
nox -s coverage # tests with the 80% coverage floor
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- **`lint`** — `ruff check` + `ruff format --check` (line length 100, strict rule set).
|
|
51
|
+
- **`typecheck`** — `pyright` in **strict** mode over both `src/` **and** `tests/`
|
|
52
|
+
(pinned to `pyright==1.1.411`; the 3.11 floor is checked deliberately).
|
|
53
|
+
- **`imports`** — `lint-imports`: the three hexagon contracts in `.importlinter`.
|
|
54
|
+
- **`coverage`** — the suite with an 80% floor.
|
|
55
|
+
|
|
56
|
+
The suite is **offline and token-free**: no real client is launched, no network call is
|
|
57
|
+
made, and no model is invoked — external boundaries are faked, and `~/.gmlw` is
|
|
58
|
+
redirected to a temp dir for every test. CI runs it on Linux, macOS, and Windows across
|
|
59
|
+
Python 3.11–3.13; make sure it passes locally before opening a PR.
|
|
60
|
+
|
|
61
|
+
## Coding guidelines
|
|
62
|
+
|
|
63
|
+
- **Dependencies point inward.** `domain ← usecase ← ports`; adapters depend on ports,
|
|
64
|
+
never the reverse. This is enforced by `import-linter`, not just convention — see the
|
|
65
|
+
hexagon in [`docs/DESIGN.md`](docs/DESIGN.md).
|
|
66
|
+
- **The domain is pure.** Entities and domain services do no I/O and import no adapter.
|
|
67
|
+
A new capability the app *needs* from the world is an outbound **port**, implemented by
|
|
68
|
+
an adapter and wired in `application/wiring/composition.py`.
|
|
69
|
+
- **Strict typing.** `pyright` strict, zero errors. A `# type: ignore` is only for a
|
|
70
|
+
provably-safe case that cannot be expressed in the type system, with a comment.
|
|
71
|
+
- **Public-clean by construction.** No personal data, employer, internal hosts, or work
|
|
72
|
+
identities in the repo — real content lives only under `~/.gmlw`. Commit with a public
|
|
73
|
+
identity (a GitHub no-reply address), not a private/work one.
|
|
74
|
+
- **Cross-platform.** Don't assume an OS; the CI matrix (incl. Windows) will tell on you.
|
|
75
|
+
- **Match the existing style.** `ruff` formats and lints; run `nox -s lint` (or
|
|
76
|
+
`ruff check . && ruff format .`).
|
|
77
|
+
|
|
78
|
+
## Pull request process
|
|
79
|
+
|
|
80
|
+
1. Branch from `main` — `feature/… tech/… fix/… docs/… chore/… test/…`. Direct pushes to
|
|
81
|
+
`main` are blocked by branch protection.
|
|
82
|
+
2. Make your change with tests that cover it, keeping `nox -s green` passing.
|
|
83
|
+
3. Update [`CHANGELOG.md`](CHANGELOG.md) under `[Unreleased]`.
|
|
84
|
+
4. Open the pull request and describe the *why*, not just the *what*; link any related
|
|
85
|
+
issue. CI must be green and the **no-AI-attribution** check must pass — commit messages
|
|
86
|
+
and PR text must contain no AI/assistant attribution (a hard project rule, enforced
|
|
87
|
+
both by a local hook and server-side).
|
|
88
|
+
|
|
89
|
+
See [`GOVERNANCE.md`](GOVERNANCE.md) for how decisions get made.
|
|
90
|
+
|
|
91
|
+
## Reporting security issues
|
|
92
|
+
|
|
93
|
+
Please do **not** open a public issue for a vulnerability. Follow
|
|
94
|
+
[`SECURITY.md`](SECURITY.md) instead.
|