surf-agentic-base 0.3.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.
- surf_agentic_base-0.3.0/.dockerignore +13 -0
- surf_agentic_base-0.3.0/.github/CODEOWNERS +2 -0
- surf_agentic_base-0.3.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
- surf_agentic_base-0.3.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- surf_agentic_base-0.3.0/.github/ISSUE_TEMPLATE/feature_request.yml +20 -0
- surf_agentic_base-0.3.0/.github/PULL_REQUEST_TEMPLATE.md +26 -0
- surf_agentic_base-0.3.0/.github/allowed_signers +5 -0
- surf_agentic_base-0.3.0/.github/dependabot.yml +10 -0
- surf_agentic_base-0.3.0/.github/release.yml +16 -0
- surf_agentic_base-0.3.0/.github/renovate.json5 +10 -0
- surf_agentic_base-0.3.0/.github/workflows/ci.yml +37 -0
- surf_agentic_base-0.3.0/.github/workflows/label.yml +89 -0
- surf_agentic_base-0.3.0/.github/workflows/release.yml +88 -0
- surf_agentic_base-0.3.0/.gitignore +73 -0
- surf_agentic_base-0.3.0/.markdownlint-cli2.jsonc +7 -0
- surf_agentic_base-0.3.0/.pre-commit-config.yaml +41 -0
- surf_agentic_base-0.3.0/CITATION.cff +25 -0
- surf_agentic_base-0.3.0/CODE_OF_CONDUCT.md +15 -0
- surf_agentic_base-0.3.0/CONTRIBUTING.md +92 -0
- surf_agentic_base-0.3.0/Dockerfile +52 -0
- surf_agentic_base-0.3.0/Justfile +32 -0
- surf_agentic_base-0.3.0/LICENSE +190 -0
- surf_agentic_base-0.3.0/PKG-INFO +144 -0
- surf_agentic_base-0.3.0/README.md +95 -0
- surf_agentic_base-0.3.0/SECURITY.md +26 -0
- surf_agentic_base-0.3.0/START_HERE.md +81 -0
- surf_agentic_base-0.3.0/charts/app/.helmignore +23 -0
- surf_agentic_base-0.3.0/charts/app/Chart.yaml +26 -0
- surf_agentic_base-0.3.0/charts/app/templates/NOTES.txt +22 -0
- surf_agentic_base-0.3.0/charts/app/templates/_helpers.tpl +83 -0
- surf_agentic_base-0.3.0/charts/app/templates/deployment.yaml +89 -0
- surf_agentic_base-0.3.0/charts/app/templates/hpa.yaml +17 -0
- surf_agentic_base-0.3.0/charts/app/templates/ingress.yaml +76 -0
- surf_agentic_base-0.3.0/charts/app/templates/networkpolicy.yaml +28 -0
- surf_agentic_base-0.3.0/charts/app/templates/poddisruptionbudget.yaml +13 -0
- surf_agentic_base-0.3.0/charts/app/templates/prometheusrule.yaml +32 -0
- surf_agentic_base-0.3.0/charts/app/templates/service.yaml +30 -0
- surf_agentic_base-0.3.0/charts/app/templates/serviceaccount.yaml +13 -0
- surf_agentic_base-0.3.0/charts/app/templates/servicemonitor.yaml +15 -0
- surf_agentic_base-0.3.0/charts/app/templates/tests/test-connection.yaml +15 -0
- surf_agentic_base-0.3.0/charts/app/values.yaml +208 -0
- surf_agentic_base-0.3.0/docs/ENGINEERING.md +189 -0
- surf_agentic_base-0.3.0/docs/architecture/boundaries.md +79 -0
- surf_agentic_base-0.3.0/docs/architecture/compliance.md +97 -0
- surf_agentic_base-0.3.0/docs/architecture/deployment-overlay.md +82 -0
- surf_agentic_base-0.3.0/docs/architecture/from-agentic-env.md +97 -0
- surf_agentic_base-0.3.0/docs/architecture/go-live-on-sdp.md +65 -0
- surf_agentic_base-0.3.0/docs/architecture/kubernetes.md +76 -0
- surf_agentic_base-0.3.0/docs/architecture/layering.md +77 -0
- surf_agentic_base-0.3.0/docs/architecture/move-plan.md +182 -0
- surf_agentic_base-0.3.0/docs/architecture/operational-traps.md +116 -0
- surf_agentic_base-0.3.0/docs/architecture/picture.md +125 -0
- surf_agentic_base-0.3.0/docs/architecture/picture.svg +81 -0
- surf_agentic_base-0.3.0/docs/architecture/process.md +55 -0
- surf_agentic_base-0.3.0/docs/architecture/proposals.md +69 -0
- surf_agentic_base-0.3.0/docs/architecture/reuse-ledger.md +204 -0
- surf_agentic_base-0.3.0/docs/decisions.md +143 -0
- surf_agentic_base-0.3.0/docs/include-readme.md +1 -0
- surf_agentic_base-0.3.0/docs/index.md +10 -0
- surf_agentic_base-0.3.0/docs/schemas/OutcomeRunFacet.json +24 -0
- surf_agentic_base-0.3.0/mkdocs.yml +30 -0
- surf_agentic_base-0.3.0/overlay.cfg +25 -0
- surf_agentic_base-0.3.0/pyproject.toml +244 -0
- surf_agentic_base-0.3.0/scripts/__init__.py +0 -0
- surf_agentic_base-0.3.0/scripts/assert_no_permitted_failures.py +112 -0
- surf_agentic_base-0.3.0/scripts/check_wheel_imports.py +97 -0
- surf_agentic_base-0.3.0/scripts/overlay.py +240 -0
- surf_agentic_base-0.3.0/setup.cfg +4 -0
- surf_agentic_base-0.3.0/src/agentic_base/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/client.py +172 -0
- surf_agentic_base-0.3.0/src/agentic_base/code_policy/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/code_policy/policy.py +122 -0
- surf_agentic_base-0.3.0/src/agentic_base/config.py +24 -0
- surf_agentic_base-0.3.0/src/agentic_base/db.py +42 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/__init__.py +1 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/epochs.py +187 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/integrity.py +135 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/outcomes.py +260 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/run_record.py +190 -0
- surf_agentic_base-0.3.0/src/agentic_base/domain/validity.py +254 -0
- surf_agentic_base-0.3.0/src/agentic_base/hpc/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/hpc/clusters.py +165 -0
- surf_agentic_base-0.3.0/src/agentic_base/hpc/job_result.py +129 -0
- surf_agentic_base-0.3.0/src/agentic_base/hpc/profiles/lumi.yaml +74 -0
- surf_agentic_base-0.3.0/src/agentic_base/hpc/profiles/snellius.yaml +73 -0
- surf_agentic_base-0.3.0/src/agentic_base/limits.py +56 -0
- surf_agentic_base-0.3.0/src/agentic_base/llm/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/llm/health.py +136 -0
- surf_agentic_base-0.3.0/src/agentic_base/llm/resilience.py +87 -0
- surf_agentic_base-0.3.0/src/agentic_base/main.py +66 -0
- surf_agentic_base-0.3.0/src/agentic_base/mcp/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/mcp/server.py +266 -0
- surf_agentic_base-0.3.0/src/agentic_base/observability/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/observability/conventions.py +183 -0
- surf_agentic_base-0.3.0/src/agentic_base/observability/tracing.py +109 -0
- surf_agentic_base-0.3.0/src/agentic_base/provenance/__init__.py +25 -0
- surf_agentic_base-0.3.0/src/agentic_base/provenance/emit.py +272 -0
- surf_agentic_base-0.3.0/src/agentic_base/py.typed +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/recording.py +131 -0
- surf_agentic_base-0.3.0/src/agentic_base/routers/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/routers/health.py +72 -0
- surf_agentic_base-0.3.0/src/agentic_base/routers/runs.py +157 -0
- surf_agentic_base-0.3.0/src/agentic_base/security/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/security/netsec.py +258 -0
- surf_agentic_base-0.3.0/src/agentic_base/tools/__init__.py +0 -0
- surf_agentic_base-0.3.0/src/agentic_base/tools/types.py +139 -0
- surf_agentic_base-0.3.0/src/agentic_base/utils/__init__.py +1 -0
- surf_agentic_base-0.3.0/src/agentic_base/utils/logging.py +203 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/PKG-INFO +144 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/SOURCES.txt +161 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/dependency_links.txt +1 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/requires.txt +25 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/scm_file_list.json +158 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/scm_version.json +8 -0
- surf_agentic_base-0.3.0/src/surf_agentic_base.egg-info/top_level.txt +1 -0
- surf_agentic_base-0.3.0/tests/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/client/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/client/test_client.py +61 -0
- surf_agentic_base-0.3.0/tests/code_policy/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/code_policy/test_policy.py +50 -0
- surf_agentic_base-0.3.0/tests/conftest.py +26 -0
- surf_agentic_base-0.3.0/tests/domain/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/domain/test_epochs.py +141 -0
- surf_agentic_base-0.3.0/tests/domain/test_integrity.py +82 -0
- surf_agentic_base-0.3.0/tests/domain/test_outcomes.py +143 -0
- surf_agentic_base-0.3.0/tests/domain/test_run_record.py +71 -0
- surf_agentic_base-0.3.0/tests/domain/test_validity.py +178 -0
- surf_agentic_base-0.3.0/tests/hpc/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/hpc/test_clusters.py +129 -0
- surf_agentic_base-0.3.0/tests/hpc/test_job_result.py +74 -0
- surf_agentic_base-0.3.0/tests/lessons/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/lessons/test_a_null_from_a_mechanism_that_never_fired.py +67 -0
- surf_agentic_base-0.3.0/tests/llm/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/llm/test_health.py +70 -0
- surf_agentic_base-0.3.0/tests/llm/test_resilience.py +74 -0
- surf_agentic_base-0.3.0/tests/mcp/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/mcp/test_server.py +237 -0
- surf_agentic_base-0.3.0/tests/observability/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/observability/test_conventions.py +132 -0
- surf_agentic_base-0.3.0/tests/observability/test_tracing.py +110 -0
- surf_agentic_base-0.3.0/tests/provenance/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/provenance/test_emit.py +206 -0
- surf_agentic_base-0.3.0/tests/recording/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/recording/test_recording.py +91 -0
- surf_agentic_base-0.3.0/tests/routers/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/routers/test_health.py +15 -0
- surf_agentic_base-0.3.0/tests/routers/test_runs.py +107 -0
- surf_agentic_base-0.3.0/tests/security/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/security/test_netsec.py +264 -0
- surf_agentic_base-0.3.0/tests/test_chart.py +150 -0
- surf_agentic_base-0.3.0/tests/test_check_wheel_imports.py +55 -0
- surf_agentic_base-0.3.0/tests/test_config.py +7 -0
- surf_agentic_base-0.3.0/tests/test_limits.py +49 -0
- surf_agentic_base-0.3.0/tests/test_overlay_contract.py +145 -0
- surf_agentic_base-0.3.0/tests/test_permitted_failures.py +77 -0
- surf_agentic_base-0.3.0/tests/test_portable_surface.py +139 -0
- surf_agentic_base-0.3.0/tests/test_process.py +81 -0
- surf_agentic_base-0.3.0/tests/test_reuse_ledger.py +153 -0
- surf_agentic_base-0.3.0/tests/tools/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/tools/test_types.py +124 -0
- surf_agentic_base-0.3.0/tests/utils/__init__.py +0 -0
- surf_agentic_base-0.3.0/tests/utils/test_logging.py +97 -0
- surf_agentic_base-0.3.0/uv.lock +3992 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something behaves differently from what its documentation or its tests say.
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened, and what should have happened
|
|
9
|
+
description: The observed behaviour and the expected one. Quote the assertion or the documented claim it contradicts.
|
|
10
|
+
validations: { required: true }
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: repro
|
|
13
|
+
attributes:
|
|
14
|
+
label: Minimal reproduction
|
|
15
|
+
description: The smallest input that shows it. A failing test is the best form.
|
|
16
|
+
render: python
|
|
17
|
+
validations: { required: true }
|
|
18
|
+
- type: input
|
|
19
|
+
id: version
|
|
20
|
+
attributes:
|
|
21
|
+
label: Version and Python
|
|
22
|
+
placeholder: "surf-agentic-base 0.1.0, Python 3.10.12"
|
|
23
|
+
validations: { required: true }
|
|
24
|
+
- type: checkboxes
|
|
25
|
+
id: warnings
|
|
26
|
+
attributes:
|
|
27
|
+
label: Verification
|
|
28
|
+
options:
|
|
29
|
+
- label: I ran the suite without `-W ignore`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Something the base layer should carry, with the consumer that needs it.
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: consumer
|
|
7
|
+
attributes:
|
|
8
|
+
label: Which consumer needs this, and what does it do today instead
|
|
9
|
+
description: A base layer only carries what more than one consumer would otherwise write separately. Name the consumer.
|
|
10
|
+
validations: { required: true }
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: existing
|
|
13
|
+
attributes:
|
|
14
|
+
label: What already exists that does this
|
|
15
|
+
description: Reuse, then buy, then build. Say what was checked and why it does not fit, or say nothing was found.
|
|
16
|
+
validations: { required: true }
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: shape
|
|
19
|
+
attributes:
|
|
20
|
+
label: Proposed shape
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## What changes, and what does not
|
|
2
|
+
|
|
3
|
+
<!-- One behaviour per pull request. Say what is deliberately left for a follow-up. Do not oversell:
|
|
4
|
+
a reviewer who finds the claim broader than the diff stops trusting the rest. -->
|
|
5
|
+
|
|
6
|
+
Closes #
|
|
7
|
+
|
|
8
|
+
## How it was verified
|
|
9
|
+
|
|
10
|
+
- [ ] `uv sync --group dev --extra service` then `ruff check`, `ruff format --check`, `mypy src tests`
|
|
11
|
+
- [ ] `pytest tests -q` **with no `-W` flag**. The repository treats warnings as errors; a run that ignores them is not a run.
|
|
12
|
+
- [ ] Any new guard was broken on purpose and seen to fail before it was trusted.
|
|
13
|
+
|
|
14
|
+
## Portability
|
|
15
|
+
|
|
16
|
+
- [ ] If a module in `tests/test_portable_surface.py` changed: it still imports on 3.10 with only the four library dependencies.
|
|
17
|
+
- [ ] No site-specific value in code, chart defaults or the Dockerfile. Those belong in a deployment overlay.
|
|
18
|
+
|
|
19
|
+
## Risk
|
|
20
|
+
|
|
21
|
+
| Question | Answer (write N/A explicitly) |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Schema or stored record changed? | |
|
|
24
|
+
| Public interface changed? | |
|
|
25
|
+
| New dependency? | |
|
|
26
|
+
| Touches URL fetching, secrets, or code execution? | |
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Who may sign a release tag. `.github/workflows/release.yml` verifies the tag against this
|
|
2
|
+
# file before it builds anything, so a tag signed by a key absent from here does not release.
|
|
3
|
+
# Rotating a key means a commit, which is the point: the set of people who can cut a release
|
|
4
|
+
# is reviewable. The principal must equal the tagger email, or the signature does not match.
|
|
5
|
+
26249073+saradamian@users.noreply.github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEj+dDHYzSSo0LOhb8OzHcyAumQsrXnzkH2yYwlKEKud
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Dependabot and Renovate split the work rather than both doing it. Dependabot owns security
|
|
2
|
+
# alerts and their fixes, and the workflow actions, because it is native here. Renovate owns
|
|
3
|
+
# ordinary version updates and lock-file maintenance, and its config disables the actions manager
|
|
4
|
+
# so the two never open the same pull request.
|
|
5
|
+
version: 2
|
|
6
|
+
updates:
|
|
7
|
+
- package-ecosystem: github-actions
|
|
8
|
+
directory: /
|
|
9
|
+
schedule: { interval: weekly }
|
|
10
|
+
labels: [dependencies]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# How generated release notes are grouped. Labels are applied to pull requests; the conventional
|
|
2
|
+
# commit prefix is what the pre-commit hook checks, the label is what the notes read.
|
|
3
|
+
changelog:
|
|
4
|
+
categories:
|
|
5
|
+
- title: Breaking
|
|
6
|
+
labels: [breaking]
|
|
7
|
+
- title: Features
|
|
8
|
+
labels: [feat, enhancement]
|
|
9
|
+
- title: Fixes
|
|
10
|
+
labels: [fix, bug]
|
|
11
|
+
- title: Documentation
|
|
12
|
+
labels: [documentation]
|
|
13
|
+
- title: Dependencies
|
|
14
|
+
labels: [dependencies]
|
|
15
|
+
- title: Other
|
|
16
|
+
labels: ["*"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended", ":semanticCommits"],
|
|
4
|
+
"lockFileMaintenance": { "enabled": true, "schedule": ["before 6am on monday"] },
|
|
5
|
+
"enabledManagers": ["pep621", "pip_requirements"],
|
|
6
|
+
"labels": ["dependencies"],
|
|
7
|
+
"packageRules": [
|
|
8
|
+
{ "matchUpdateTypes": ["patch"], "automerge": false }
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# The gate this repository declares, run on both ends of the supported range. 3.10 is not
|
|
2
|
+
# decoration: it is the floor a consumer of this library runs on, and the portable-surface guards
|
|
3
|
+
# mean nothing unless something executes them there.
|
|
4
|
+
#
|
|
5
|
+
# Actions are pinned to commit hashes, not tags. A tag can be moved; a hash cannot.
|
|
6
|
+
name: ci
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
pull_request:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ci-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
check:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
strategy:
|
|
24
|
+
fail-fast: false
|
|
25
|
+
matrix:
|
|
26
|
+
python: ["3.10", "3.14"]
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
30
|
+
- run: uv lock --check
|
|
31
|
+
- run: uv sync --python ${{ matrix.python }} --group dev --extra service --extra provenance
|
|
32
|
+
- run: .venv/bin/ruff check src tests
|
|
33
|
+
- run: .venv/bin/ruff format --check src tests
|
|
34
|
+
- run: .venv/bin/mypy src tests
|
|
35
|
+
- run: .venv/bin/pytest tests -q --cov=src --cov-report=term
|
|
36
|
+
env:
|
|
37
|
+
DATABASE_URL: sqlite:///./ci.db
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# The release notes group by pull request label (`.github/release.yml`). Nothing ever applied one:
|
|
2
|
+
# all fourteen labels existed, none of the first five pull requests carried any, so every entry
|
|
3
|
+
# would have been filed under "Other" and the grouping config could not do the thing it describes.
|
|
4
|
+
#
|
|
5
|
+
# The input is the conventional-commit prefix the commit hook already enforces, which squash
|
|
6
|
+
# merging puts on main as the commit subject. Deriving the label from the title keeps one source
|
|
7
|
+
# of truth rather than adding a second one for humans to keep in step.
|
|
8
|
+
#
|
|
9
|
+
# A prefix that maps to nothing is not an error. `chore`, `ci`, `test` and `refactor` belong under
|
|
10
|
+
# "Other", and this says so rather than failing.
|
|
11
|
+
#
|
|
12
|
+
# The derivation RECONCILES rather than adds. A title edited from `feat:` to `fix:` re-fires this
|
|
13
|
+
# workflow, and an add-only version would leave both labels in place; the notes then file the
|
|
14
|
+
# entry under whichever category matches first, which is the stale one. So each label this
|
|
15
|
+
# workflow can derive is either applied or removed on every run, and only those: a label a person
|
|
16
|
+
# added by hand, `enhancement` or `good first issue`, is never touched.
|
|
17
|
+
#
|
|
18
|
+
# `pull_request_target` is used because a fork's `pull_request` token cannot write labels. It runs
|
|
19
|
+
# with a writable token against the BASE repository, so this workflow deliberately checks out no
|
|
20
|
+
# code at all and reads the title through the environment rather than interpolating it into the
|
|
21
|
+
# script. Nothing from a fork is executed here.
|
|
22
|
+
name: label
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
pull_request_target:
|
|
26
|
+
types: [opened, edited, reopened]
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
pull-requests: write
|
|
30
|
+
|
|
31
|
+
jobs:
|
|
32
|
+
label:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- name: derive the grouping label from the conventional-commit prefix
|
|
36
|
+
env:
|
|
37
|
+
GH_TOKEN: ${{ github.token }}
|
|
38
|
+
TITLE: ${{ github.event.pull_request.title }}
|
|
39
|
+
NUMBER: ${{ github.event.pull_request.number }}
|
|
40
|
+
REPO: ${{ github.repository }}
|
|
41
|
+
run: |
|
|
42
|
+
prefix=${TITLE%%:*}
|
|
43
|
+
|
|
44
|
+
breaking=""
|
|
45
|
+
case "$prefix" in *'!') breaking="breaking" ;; esac
|
|
46
|
+
prefix=${prefix%!}
|
|
47
|
+
prefix=${prefix%%(*}
|
|
48
|
+
|
|
49
|
+
case "$prefix" in
|
|
50
|
+
feat) label="feat" ;;
|
|
51
|
+
fix) label="fix" ;;
|
|
52
|
+
docs) label="documentation" ;;
|
|
53
|
+
deps|build) label="dependencies" ;;
|
|
54
|
+
*) label="" ;;
|
|
55
|
+
esac
|
|
56
|
+
|
|
57
|
+
wanted=""
|
|
58
|
+
[ -n "$label" ] && wanted="$label"
|
|
59
|
+
[ -n "$breaking" ] && wanted="$wanted $breaking"
|
|
60
|
+
|
|
61
|
+
if [ -z "$wanted" ]; then
|
|
62
|
+
echo "prefix '$prefix' derives no grouping label; the notes file this under Other"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Everything this workflow is allowed to decide. A label outside this set belongs to
|
|
66
|
+
# whoever put it there. There is no early exit above: an empty derivation still has to
|
|
67
|
+
# REMOVE a label an earlier title produced.
|
|
68
|
+
managed="breaking dependencies documentation feat fix"
|
|
69
|
+
held=" $(gh api "repos/$REPO/issues/$NUMBER/labels" --jq '.[].name' | tr '\n' ' ')"
|
|
70
|
+
|
|
71
|
+
for name in $managed; do
|
|
72
|
+
case " $wanted " in *" $name "*) want=1 ;; *) want=0 ;; esac
|
|
73
|
+
case "$held" in *" $name "*) have=1 ;; *) have=0 ;; esac
|
|
74
|
+
|
|
75
|
+
if [ "$want" = 1 ] && [ "$have" = 0 ]; then
|
|
76
|
+
# A JSON body on stdin, not `-f 'labels[]=x'`: that form is not understood by
|
|
77
|
+
# every gh version and 422s where it is not, failing this step for a reason that
|
|
78
|
+
# has nothing to do with the label.
|
|
79
|
+
printf '{"labels":["%s"]}' "$name" |
|
|
80
|
+
gh api "repos/$REPO/issues/$NUMBER/labels" --input - --silent
|
|
81
|
+
echo "added $name"
|
|
82
|
+
elif [ "$want" = 0 ] && [ "$have" = 1 ]; then
|
|
83
|
+
gh api -X DELETE "repos/$REPO/issues/$NUMBER/labels/$name" --silent
|
|
84
|
+
echo "removed $name, which the title no longer derives"
|
|
85
|
+
fi
|
|
86
|
+
done
|
|
87
|
+
|
|
88
|
+
set -- $managed
|
|
89
|
+
echo "reconciled $# derivable labels against the title"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# A tag `vX.Y.Z` on main produces a release. The version inside the built distribution comes
|
|
2
|
+
# from that same tag through setuptools-scm, so the two cannot disagree. Nothing is published to
|
|
3
|
+
# a package index from here; that is a separate decision with its own owner.
|
|
4
|
+
#
|
|
5
|
+
# Three things a consumer of a release can check now, and could not before:
|
|
6
|
+
#
|
|
7
|
+
# * the tag carries a signature from a key listed in `.github/allowed_signers`. Note that
|
|
8
|
+
# `gh release create --verify-tag`, at the end of this file, does NOT do this. It asserts the
|
|
9
|
+
# tag exists on the remote and reads no signature at all; the name invites the opposite
|
|
10
|
+
# reading. The rule starts at the next tag, because `v0.1.0` predates commit signing and the
|
|
11
|
+
# tag ruleset makes it immutable, so nobody can quietly re-sign it and close the gap;
|
|
12
|
+
# * the artifact installs and imports on the consumer's Python floor. The suite reads `src/`,
|
|
13
|
+
# so it cannot see a defect that exists only in the built wheel;
|
|
14
|
+
# * the artifact carries build provenance. Without it the chain of custody stops at the tag,
|
|
15
|
+
# and anyone with write access can replace a release asset with nothing to detect it.
|
|
16
|
+
name: release
|
|
17
|
+
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
tags: ["v*.*.*"]
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: write
|
|
24
|
+
# `attest-build-provenance` mints a short-lived signing certificate for this run and writes the
|
|
25
|
+
# attestation back to the repository. Both are required. With `contents: write` alone the build
|
|
26
|
+
# succeeds and the attest step fails at the end of it, having spent the whole job first.
|
|
27
|
+
id-token: write
|
|
28
|
+
attestations: write
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
release:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0
|
|
37
|
+
# The tag OBJECT, not just the commit it points at: the signature lives on the object.
|
|
38
|
+
fetch-tags: true
|
|
39
|
+
|
|
40
|
+
- name: the tag must be signed by a listed signer
|
|
41
|
+
run: |
|
|
42
|
+
git config gpg.ssh.allowedSignersFile .github/allowed_signers
|
|
43
|
+
git verify-tag "${GITHUB_REF_NAME}"
|
|
44
|
+
|
|
45
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
46
|
+
- run: uv build
|
|
47
|
+
|
|
48
|
+
- name: the built version must equal the tag
|
|
49
|
+
run: |
|
|
50
|
+
built=$(ls dist/*.tar.gz | sed -E 's|.*/surf_agentic_base-(.*)\.tar\.gz|\1|')
|
|
51
|
+
test "v$built" = "${GITHUB_REF_NAME}" || { echo "built $built, tag ${GITHUB_REF_NAME}"; exit 1; }
|
|
52
|
+
|
|
53
|
+
- name: the wheel must install and import on the consumer floor
|
|
54
|
+
run: |
|
|
55
|
+
uv venv --python 3.10 /tmp/floor
|
|
56
|
+
uv pip install --python /tmp/floor/bin/python --quiet dist/*.whl
|
|
57
|
+
python3 scripts/check_wheel_imports.py /tmp/floor/bin/python
|
|
58
|
+
|
|
59
|
+
- uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
60
|
+
with:
|
|
61
|
+
subject-path: dist/*
|
|
62
|
+
|
|
63
|
+
- run: gh release create "${GITHUB_REF_NAME}" dist/* --generate-notes --verify-tag
|
|
64
|
+
env:
|
|
65
|
+
GH_TOKEN: ${{ github.token }}
|
|
66
|
+
|
|
67
|
+
# Trusted publishing: no token stored anywhere, PyPI accepts this workflow's OIDC identity
|
|
68
|
+
# for the project `surf-agentic-base` once that publisher is registered on pypi.org. A
|
|
69
|
+
# separate job so a publish failure is a red job on a release that already exists, never a
|
|
70
|
+
# release that silently does not.
|
|
71
|
+
publish:
|
|
72
|
+
needs: release
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
environment: pypi
|
|
75
|
+
permissions:
|
|
76
|
+
id-token: write
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
79
|
+
with:
|
|
80
|
+
fetch-depth: 0
|
|
81
|
+
fetch-tags: true
|
|
82
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
83
|
+
- run: uv build
|
|
84
|
+
- name: the built version must equal the tag, here too
|
|
85
|
+
run: |
|
|
86
|
+
built=$(ls dist/*.tar.gz | sed -E 's|.*/surf_agentic_base-(.*)\.tar\.gz|\1|')
|
|
87
|
+
test "v$built" = "${GITHUB_REF_NAME}" || { echo "built $built, tag ${GITHUB_REF_NAME}"; exit 1; }
|
|
88
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
venv
|
|
3
|
+
.venv
|
|
4
|
+
coverage.xml
|
|
5
|
+
.coverage
|
|
6
|
+
__pycache__/
|
|
7
|
+
.mypy_cache
|
|
8
|
+
.dmypy.json
|
|
9
|
+
.ruff_cache
|
|
10
|
+
.pytest_cache
|
|
11
|
+
test-results.xml
|
|
12
|
+
gl-code-quality-report.json
|
|
13
|
+
gl-code-quality-report-ruff.json
|
|
14
|
+
*.egg-info
|
|
15
|
+
|
|
16
|
+
# OS generated files
|
|
17
|
+
.DS_Store
|
|
18
|
+
.DS_Store?
|
|
19
|
+
._*
|
|
20
|
+
.Spotlight-V100
|
|
21
|
+
.Trashes
|
|
22
|
+
ehthumbs.db
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# User-specific stuff
|
|
26
|
+
.idea/**/workspace.xml
|
|
27
|
+
.idea/**/tasks.xml
|
|
28
|
+
.idea/**/usage.statistics.xml
|
|
29
|
+
.idea/**/dictionaries
|
|
30
|
+
.idea/**/shelf
|
|
31
|
+
.idea/**/*.iml
|
|
32
|
+
.idea/**/misc.xml
|
|
33
|
+
.idea/**/modules.xml
|
|
34
|
+
|
|
35
|
+
# AWS User-specific
|
|
36
|
+
.idea/**/aws.xml
|
|
37
|
+
|
|
38
|
+
# Generated files
|
|
39
|
+
.idea/**/contentModel.xml
|
|
40
|
+
|
|
41
|
+
# Sensitive or high-churn files
|
|
42
|
+
.idea/**/dataSources/
|
|
43
|
+
.idea/**/dataSources.ids
|
|
44
|
+
.idea/**/dataSources.local.xml
|
|
45
|
+
.idea/**/sqlDataSources.xml
|
|
46
|
+
.idea/**/dynamic.xml
|
|
47
|
+
.idea/**/uiDesigner.xml
|
|
48
|
+
.idea/**/dbnavigator.xml
|
|
49
|
+
|
|
50
|
+
# IntelliJ
|
|
51
|
+
out/
|
|
52
|
+
|
|
53
|
+
# JIRA plugin
|
|
54
|
+
atlassian-ide-plugin.xml
|
|
55
|
+
|
|
56
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
57
|
+
com_crashlytics_export_strings.xml
|
|
58
|
+
crashlytics.properties
|
|
59
|
+
crashlytics-build.properties
|
|
60
|
+
fabric.properties
|
|
61
|
+
|
|
62
|
+
# mkdocs
|
|
63
|
+
site
|
|
64
|
+
|
|
65
|
+
# Local databases. The service defaults to `sqlite:///./agentic-base.db` and the pipeline uses
|
|
66
|
+
# `./ci.db`, so both land in the working directory. The only `.db` entries here were Windows
|
|
67
|
+
# thumbnail caches by exact name, which meant a local run followed by `git add -A` could commit
|
|
68
|
+
# a database. Names, not just the two we happen to create today.
|
|
69
|
+
*.db
|
|
70
|
+
*.db-wal
|
|
71
|
+
*.db-shm
|
|
72
|
+
*.sqlite
|
|
73
|
+
*.sqlite3
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
exclude: ^.*docs/.*\.md|.*foo.txt$
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
args: [ '--multi' ]
|
|
10
|
+
exclude: ^charts/.*/templates/.*\.yaml|\.gitlab-ci\.yml$
|
|
11
|
+
- id: check-added-large-files
|
|
12
|
+
- repo: https://github.com/codespell-project/codespell
|
|
13
|
+
rev: v2.4.1
|
|
14
|
+
hooks:
|
|
15
|
+
- id: codespell
|
|
16
|
+
files: ^.*\.md$
|
|
17
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
18
|
+
rev: v0.12.7
|
|
19
|
+
hooks:
|
|
20
|
+
- id: ruff-check
|
|
21
|
+
- repo: https://github.com/DavidAnson/markdownlint-cli2
|
|
22
|
+
rev: v0.18.1
|
|
23
|
+
hooks:
|
|
24
|
+
- id: markdownlint-cli2
|
|
25
|
+
files: ^.*\.md$
|
|
26
|
+
exclude: ^.*docs/.*\.md$
|
|
27
|
+
- repo: https://github.com/hadolint/hadolint
|
|
28
|
+
rev: v2.12.0
|
|
29
|
+
hooks:
|
|
30
|
+
- id: hadolint-docker
|
|
31
|
+
- repo: https://github.com/gruntwork-io/pre-commit
|
|
32
|
+
rev: v0.1.30
|
|
33
|
+
hooks:
|
|
34
|
+
- id: helmlint
|
|
35
|
+
types: [ yaml ]
|
|
36
|
+
files: ^(charts/app/templates/.*\.yaml)$
|
|
37
|
+
- repo: https://github.com/commitizen-tools/commitizen
|
|
38
|
+
rev: v4.9.1
|
|
39
|
+
hooks:
|
|
40
|
+
- id: commitizen
|
|
41
|
+
stages: [ commit-msg ]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using the metadata from this file."
|
|
3
|
+
type: software
|
|
4
|
+
title: "surf-agentic-base"
|
|
5
|
+
abstract: >-
|
|
6
|
+
A base layer for agentic work on research infrastructure. It records what actually ran with the
|
|
7
|
+
provenance needed to replay and audit it, adjudicates whether a comparison between two agent
|
|
8
|
+
configurations is sound enough to publish, and carries the primitives every consumer would
|
|
9
|
+
otherwise write separately and differently.
|
|
10
|
+
authors:
|
|
11
|
+
- family-names: Podareanu
|
|
12
|
+
given-names: Damian
|
|
13
|
+
affiliation: SURF
|
|
14
|
+
version: 0.3.0
|
|
15
|
+
date-released: "2026-09-12"
|
|
16
|
+
license: EUPL-1.2
|
|
17
|
+
repository-code: "https://github.com/saradamian/agentic-base"
|
|
18
|
+
keywords:
|
|
19
|
+
- agents
|
|
20
|
+
- provenance
|
|
21
|
+
- reproducibility
|
|
22
|
+
- evaluation
|
|
23
|
+
- model-context-protocol
|
|
24
|
+
- opentelemetry
|
|
25
|
+
- hpc
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
This project follows the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/),
|
|
4
|
+
version 2.1. It applies in every space this project runs: the repository, its issues and pull
|
|
5
|
+
requests, and any discussion of it that identifies as such.
|
|
6
|
+
|
|
7
|
+
In short: be considerate and constructive, accept responsibility and apologise when you get it
|
|
8
|
+
wrong, and take the interests of the community over your own convenience. Harassment,
|
|
9
|
+
discriminatory remarks, publishing others' private information, and sustained disruption are
|
|
10
|
+
not tolerated.
|
|
11
|
+
|
|
12
|
+
Report violations privately through the repository's security advisory channel at
|
|
13
|
+
<https://github.com/saradamian/agentic-base/security/advisories/new>, which reaches the
|
|
14
|
+
maintainers only. Reports are handled in confidence. The maintainers follow the Covenant's
|
|
15
|
+
enforcement ladder: correction, warning, temporary ban, permanent ban.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
House rules, kept short so the next reader has less to hold in their head. Most exist because
|
|
4
|
+
something specific went wrong once; `docs/ENGINEERING.md` says what each one cost and which test
|
|
5
|
+
fails when it stops being true.
|
|
6
|
+
|
|
7
|
+
## Where the reasoning lives
|
|
8
|
+
|
|
9
|
+
Code is terse. A docstring states purpose and constraints, what a reader needs to use or change
|
|
10
|
+
the thing safely. It does not carry history, justification, or an argument that the change is
|
|
11
|
+
correct; that goes in the commit message and, when it is a decision, in `docs/decisions.md` or
|
|
12
|
+
`docs/architecture/`. A reader who wants to know *why* looks there.
|
|
13
|
+
|
|
14
|
+
## Two halves, one repository
|
|
15
|
+
|
|
16
|
+
The **library half** is what other projects import. It installs on Python 3.10 with pydantic,
|
|
17
|
+
pydantic-settings, httpx and PyYAML, and nothing else. Which modules are portable is a list in
|
|
18
|
+
`tests/test_portable_surface.py`, not a paragraph, and its guards fail when the list stops being
|
|
19
|
+
true. The **service half** is an optional extra. If your change makes a portable module import a
|
|
20
|
+
service dependency, the suite tells you.
|
|
21
|
+
|
|
22
|
+
## Values that belong somewhere else
|
|
23
|
+
|
|
24
|
+
Anything that is a fact about *where* this runs rather than *what* it does, a registry, a
|
|
25
|
+
hostname, a pull secret, an environment name, does not go in code, chart defaults or the
|
|
26
|
+
Dockerfile. It goes in a deployment overlay outside this repository, and the overlay may only
|
|
27
|
+
add files, never modify ours; `overlay.cfg` declares which paths it owns and a guard refuses a
|
|
28
|
+
change here that creates one. The Dockerfile takes registries as build arguments for this reason.
|
|
29
|
+
The pattern and its tool are in `docs/architecture/deployment-overlay.md`.
|
|
30
|
+
|
|
31
|
+
## Running the gate
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
uv sync --group dev --extra service --extra provenance
|
|
35
|
+
.venv/bin/ruff check src tests
|
|
36
|
+
.venv/bin/ruff format --check src tests
|
|
37
|
+
.venv/bin/mypy src tests
|
|
38
|
+
.venv/bin/pytest tests -q
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run pytest **without** `-W ignore`. The repository treats warnings as errors on purpose, and a
|
|
42
|
+
run that suppresses them is a run that cannot fail on the class of defect the policy exists for.
|
|
43
|
+
Two real defects reached the public gate that way in one day. If a warning must be tolerated,
|
|
44
|
+
scope a `filterwarnings` entry to that one message, name the upstream cause, and say when it can
|
|
45
|
+
be removed.
|
|
46
|
+
|
|
47
|
+
Continuous integration runs the same five steps on 3.10 and 3.14. If your machine has only one,
|
|
48
|
+
the runner is the other.
|
|
49
|
+
|
|
50
|
+
## Tests
|
|
51
|
+
|
|
52
|
+
One test per behaviour, at the highest seam that can actually fail. Name the behaviour and the
|
|
53
|
+
condition: `test_a_verdict_from_a_degraded_instrument_is_not_citable`, not `test_citable`. A test
|
|
54
|
+
that no plausible bug would fail should be deleted. A new module needs a test module of the same
|
|
55
|
+
name in the same change; a guard checks.
|
|
56
|
+
|
|
57
|
+
**Break every guard you add.** Before trusting a check, make the thing it checks wrong and watch
|
|
58
|
+
it go red. A guard that cannot fail is worse than none, because it gets cited.
|
|
59
|
+
|
|
60
|
+
## Commits and pull requests
|
|
61
|
+
|
|
62
|
+
Conventional commits: `type(scope): description`, with `feat`, `fix`, `test`, `refactor`,
|
|
63
|
+
`docs`, `chore`, `ci`, `perf`. The pre-commit hook enforces it; install it with
|
|
64
|
+
`pre-commit install --hook-type commit-msg`.
|
|
65
|
+
|
|
66
|
+
One behaviour per pull request. The description says what changes, what does not, and what is
|
|
67
|
+
deliberately left for later. It must not claim more than the diff delivers.
|
|
68
|
+
|
|
69
|
+
`main` accepts pull requests only, with the gate green on both interpreters, every review thread
|
|
70
|
+
resolved, and a linear history. There is no required reviewer count while the repository has one
|
|
71
|
+
maintainer; the gate is the reviewer. That changes the day there is a second.
|
|
72
|
+
|
|
73
|
+
## Versioning and releases
|
|
74
|
+
|
|
75
|
+
Semantic versioning. The version is the git tag; nothing is edited to cut a release.
|
|
76
|
+
|
|
77
|
+
- `0.y.z` while the interface is settling. A minor bump may change the public interface, and it
|
|
78
|
+
is stated in the notes when it does. The import name is `agentic_base`; it was `app` until
|
|
79
|
+
`0.2.0`. The distribution name is `surf-agentic-base` since `0.3.0`, because `agentic-base` on
|
|
80
|
+
PyPI belongs to an unrelated project and a pin against it would have installed theirs.
|
|
81
|
+
- Patch releases fix without changing an interface.
|
|
82
|
+
- Tag `vX.Y.Z` on `main`. The release workflow builds the distribution, refuses if the built
|
|
83
|
+
version differs from the tag, and publishes a GitHub release with generated notes. Nothing goes
|
|
84
|
+
to a package index from here.
|
|
85
|
+
- `CITATION.cff` carries the version and the release date; update both in the same change that
|
|
86
|
+
tags.
|
|
87
|
+
|
|
88
|
+
## Dependencies
|
|
89
|
+
|
|
90
|
+
Dependabot handles security alerts and the workflow actions. Renovate handles ordinary version
|
|
91
|
+
updates and lock-file maintenance. They are split so they never open the same pull request.
|
|
92
|
+
Actions are pinned by commit hash with the version in a trailing comment; bump both together.
|