cobalt-grinding 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.
- cobalt_grinding-0.1.0/.dockerignore +21 -0
- cobalt_grinding-0.1.0/.github/workflows/dev-release.yml +103 -0
- cobalt_grinding-0.1.0/.github/workflows/release.yml +171 -0
- cobalt_grinding-0.1.0/.github/workflows/reuse.yml +26 -0
- cobalt_grinding-0.1.0/.github/workflows/test.yml +38 -0
- cobalt_grinding-0.1.0/.github/workflows/version-guard.yml +48 -0
- cobalt_grinding-0.1.0/.gitignore +38 -0
- cobalt_grinding-0.1.0/.mailmap +4 -0
- cobalt_grinding-0.1.0/.mcp.json +8 -0
- cobalt_grinding-0.1.0/.python-version +1 -0
- cobalt_grinding-0.1.0/AGENTS.md +29 -0
- cobalt_grinding-0.1.0/CHANGELOG.md +24 -0
- cobalt_grinding-0.1.0/CLAUDE.md +29 -0
- cobalt_grinding-0.1.0/Dockerfile +46 -0
- cobalt_grinding-0.1.0/LICENSE +661 -0
- cobalt_grinding-0.1.0/LICENSES/AGPL-3.0-or-later.txt +661 -0
- cobalt_grinding-0.1.0/LICENSING.md +23 -0
- cobalt_grinding-0.1.0/PKG-INFO +110 -0
- cobalt_grinding-0.1.0/README.md +90 -0
- cobalt_grinding-0.1.0/REUSE.toml +9 -0
- cobalt_grinding-0.1.0/cliff.toml +55 -0
- cobalt_grinding-0.1.0/docs/CONTRIBUTING.md +71 -0
- cobalt_grinding-0.1.0/docs/architecture.dot +213 -0
- cobalt_grinding-0.1.0/docs/existing_MCP_servers_to_consider/anthropic_model_info_roll_your_own.md +94 -0
- cobalt_grinding-0.1.0/docs/existing_MCP_servers_to_consider/llama-mcp-server-analysis.md +591 -0
- cobalt_grinding-0.1.0/docs/ideation.md +314 -0
- cobalt_grinding-0.1.0/docs/m3-deco-assaying-findings.md +281 -0
- cobalt_grinding-0.1.0/docs/northstar.md +184 -0
- cobalt_grinding-0.1.0/docs/plan.md +1281 -0
- cobalt_grinding-0.1.0/docs/what_an_agent_needs_to_know.md +67 -0
- cobalt_grinding-0.1.0/pyproject.toml +97 -0
- cobalt_grinding-0.1.0/scripts/generate_changelog.py +269 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/__init__.py +7 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/app.py +224 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/common/__init__.py +3 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/config.py +283 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/converse/__init__.py +3 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/converse/orchestrator.py +359 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/__init__.py +5 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/bootstrap.py +149 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/main.py +314 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/mcp_clients.py +420 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/mutex.py +80 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/scheduler.py +262 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/server.py +84 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/tools.py +505 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/__init__.py +25 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/api.py +143 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/dispatch.py +80 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/embedder.py +92 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/loop.py +216 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/provider.py +104 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/tools_index.py +275 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/host/tools_index_db.py +52 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/__init__.py +3 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/agents.py +350 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/deco_client.py +190 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/flint_client.py +160 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/format_classifier.py +97 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/handlers.py +206 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/orchestrator.py +1184 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/smalt_client.py +352 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/source_fetcher.py +257 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/structure_extractor.py +146 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/url_fetcher.py +157 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/retrieve/__init__.py +3 -0
- cobalt_grinding-0.1.0/src/cobalt_grinding/retrieve/orchestrator.py +317 -0
- cobalt_grinding-0.1.0/tests/__init__.py +3 -0
- cobalt_grinding-0.1.0/tests/conftest.py +78 -0
- cobalt_grinding-0.1.0/tests/fixtures/stub_mcp_server.py +55 -0
- cobalt_grinding-0.1.0/tests/test_app.py +161 -0
- cobalt_grinding-0.1.0/tests/test_config.py +163 -0
- cobalt_grinding-0.1.0/tests/test_converse_orchestrator.py +432 -0
- cobalt_grinding-0.1.0/tests/test_daemon_integration.py +357 -0
- cobalt_grinding-0.1.0/tests/test_daemon_mcp_clients.py +260 -0
- cobalt_grinding-0.1.0/tests/test_daemon_tools.py +541 -0
- cobalt_grinding-0.1.0/tests/test_host_api.py +193 -0
- cobalt_grinding-0.1.0/tests/test_host_dispatch.py +131 -0
- cobalt_grinding-0.1.0/tests/test_host_integration.py +113 -0
- cobalt_grinding-0.1.0/tests/test_host_loop.py +401 -0
- cobalt_grinding-0.1.0/tests/test_host_provider.py +152 -0
- cobalt_grinding-0.1.0/tests/test_host_tools_index.py +190 -0
- cobalt_grinding-0.1.0/tests/test_ingest_agents.py +231 -0
- cobalt_grinding-0.1.0/tests/test_ingest_deco_client.py +198 -0
- cobalt_grinding-0.1.0/tests/test_ingest_flint_client.py +230 -0
- cobalt_grinding-0.1.0/tests/test_ingest_format_classifier.py +93 -0
- cobalt_grinding-0.1.0/tests/test_ingest_handlers.py +188 -0
- cobalt_grinding-0.1.0/tests/test_ingest_orchestrator.py +1010 -0
- cobalt_grinding-0.1.0/tests/test_ingest_smalt_client.py +283 -0
- cobalt_grinding-0.1.0/tests/test_ingest_source_fetcher.py +187 -0
- cobalt_grinding-0.1.0/tests/test_ingest_structure_extractor.py +124 -0
- cobalt_grinding-0.1.0/tests/test_ingest_url_fetcher.py +204 -0
- cobalt_grinding-0.1.0/tests/test_mutex.py +64 -0
- cobalt_grinding-0.1.0/tests/test_retrieve_orchestrator.py +394 -0
- cobalt_grinding-0.1.0/tests/test_scheduler.py +151 -0
- cobalt_grinding-0.1.0/uv.lock +1536 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Exclude things that don't belong in the published image.
|
|
2
|
+
# pyproject.toml / uv.lock / README.md / src/ are explicitly COPY'd
|
|
3
|
+
# by the Dockerfile, so we don't need an allow-list here — just a
|
|
4
|
+
# denylist for the obvious noise.
|
|
5
|
+
|
|
6
|
+
.git
|
|
7
|
+
.github
|
|
8
|
+
.venv
|
|
9
|
+
.gitignore
|
|
10
|
+
.dockerignore
|
|
11
|
+
__pycache__
|
|
12
|
+
*.pyc
|
|
13
|
+
.pytest_cache
|
|
14
|
+
.ruff_cache
|
|
15
|
+
.mypy_cache
|
|
16
|
+
.coverage
|
|
17
|
+
htmlcov
|
|
18
|
+
docs
|
|
19
|
+
tests
|
|
20
|
+
humans_notes.md
|
|
21
|
+
worktrees
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
name: Dev release
|
|
6
|
+
|
|
7
|
+
# On-demand PRE-RELEASE publish from `develop` — a dev build to exercise a candidate
|
|
8
|
+
# before the real release. Manually triggered (workflow_dispatch; run it with
|
|
9
|
+
# `--ref develop`, or via `git dev-release`). Publishes a GHCR `:dev` image and the
|
|
10
|
+
# `X.Y.Z.devN` version to TestPyPI. It creates NO `v*` tag, so the real release.yml
|
|
11
|
+
# gate is untouched, and runs no changelog job. See the handbook's releases.md
|
|
12
|
+
# ("Development versioning") + ci.md.
|
|
13
|
+
#
|
|
14
|
+
# Python/pyproject + TestPyPI variant; a Node repo would publish an npm dist-tag instead.
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
packages: write # GHCR push
|
|
22
|
+
id-token: write # TestPyPI trusted publishing (OIDC)
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
# Gate: dev builds come from develop and must carry a dev version. Both publish
|
|
26
|
+
# jobs need it, so a bad invocation ships nothing.
|
|
27
|
+
gate:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
outputs:
|
|
30
|
+
version: ${{ steps.v.outputs.version }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v6
|
|
33
|
+
- name: Verify dev build from develop
|
|
34
|
+
id: v
|
|
35
|
+
run: |
|
|
36
|
+
if [ "$GITHUB_REF" != "refs/heads/develop" ]; then
|
|
37
|
+
echo "dev-release runs from develop only (got $GITHUB_REF)"; exit 1
|
|
38
|
+
fi
|
|
39
|
+
VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
40
|
+
case "$VERSION" in
|
|
41
|
+
*.dev*|*-dev) ;;
|
|
42
|
+
*) echo "version '$VERSION' is not a dev version (expected X.Y.Z.devN) — open the dev cycle first"; exit 1 ;;
|
|
43
|
+
esac
|
|
44
|
+
echo "dev version: $VERSION"
|
|
45
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
46
|
+
|
|
47
|
+
docker:
|
|
48
|
+
needs: gate
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v6
|
|
52
|
+
|
|
53
|
+
- name: Set up QEMU (arm64 emulation)
|
|
54
|
+
uses: docker/setup-qemu-action@v4
|
|
55
|
+
|
|
56
|
+
- name: Set up Docker Buildx
|
|
57
|
+
uses: docker/setup-buildx-action@v4
|
|
58
|
+
|
|
59
|
+
- name: Log in to GHCR
|
|
60
|
+
uses: docker/login-action@v4
|
|
61
|
+
with:
|
|
62
|
+
registry: ghcr.io
|
|
63
|
+
username: ${{ github.actor }}
|
|
64
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
65
|
+
|
|
66
|
+
- name: Extract metadata (dev tags — NOT latest)
|
|
67
|
+
id: meta
|
|
68
|
+
uses: docker/metadata-action@v6
|
|
69
|
+
with:
|
|
70
|
+
images: ghcr.io/${{ github.repository }}
|
|
71
|
+
tags: |
|
|
72
|
+
type=raw,value=dev
|
|
73
|
+
type=raw,value=${{ needs.gate.outputs.version }}
|
|
74
|
+
type=sha,prefix=sha-
|
|
75
|
+
|
|
76
|
+
- name: Build and push (linux/amd64 + linux/arm64)
|
|
77
|
+
uses: docker/build-push-action@v7
|
|
78
|
+
with:
|
|
79
|
+
context: .
|
|
80
|
+
platforms: linux/amd64,linux/arm64
|
|
81
|
+
push: true
|
|
82
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
83
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
84
|
+
cache-from: type=gha
|
|
85
|
+
cache-to: type=gha,mode=max
|
|
86
|
+
|
|
87
|
+
testpypi:
|
|
88
|
+
needs: gate
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
environment: testpypi # matches the trusted-publisher config on TestPyPI
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v6
|
|
93
|
+
|
|
94
|
+
- name: Install uv
|
|
95
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
96
|
+
|
|
97
|
+
- name: Build wheel + sdist
|
|
98
|
+
run: uv build
|
|
99
|
+
|
|
100
|
+
- name: Publish to TestPyPI
|
|
101
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
102
|
+
with:
|
|
103
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read # default — gate/docker/pypi only read the tree
|
|
9
|
+
packages: write # GHCR push
|
|
10
|
+
id-token: write # PyPI trusted publishing
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
# Pre-flight checks. Both publish jobs depend on this, so a failed gate
|
|
14
|
+
# prevents *any* artifact from shipping (no half-shipped state).
|
|
15
|
+
gate:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0 # full history needed for the ancestor check below
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
24
|
+
|
|
25
|
+
- name: Verify tag matches pyproject version
|
|
26
|
+
run: |
|
|
27
|
+
TAG="${GITHUB_REF#refs/tags/v}"
|
|
28
|
+
PROJECT_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
29
|
+
if [ "$TAG" != "$PROJECT_VERSION" ]; then
|
|
30
|
+
echo "tag $TAG != pyproject $PROJECT_VERSION"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
- name: Verify tag is reachable from origin/main
|
|
35
|
+
run: |
|
|
36
|
+
git fetch --no-tags origin main
|
|
37
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
38
|
+
echo "tagged commit $GITHUB_SHA is not on origin/main"
|
|
39
|
+
echo "release tags must come from main — back-merge through, then re-tag from main"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
docker:
|
|
44
|
+
needs: gate
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v6
|
|
48
|
+
|
|
49
|
+
- name: Set up QEMU (arm64 emulation)
|
|
50
|
+
uses: docker/setup-qemu-action@v4
|
|
51
|
+
|
|
52
|
+
- name: Set up Docker Buildx
|
|
53
|
+
uses: docker/setup-buildx-action@v4
|
|
54
|
+
|
|
55
|
+
- name: Log in to GHCR
|
|
56
|
+
uses: docker/login-action@v4
|
|
57
|
+
with:
|
|
58
|
+
registry: ghcr.io
|
|
59
|
+
username: ${{ github.actor }}
|
|
60
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
61
|
+
|
|
62
|
+
- name: Extract metadata (tags + labels)
|
|
63
|
+
id: meta
|
|
64
|
+
uses: docker/metadata-action@v6
|
|
65
|
+
with:
|
|
66
|
+
images: ghcr.io/${{ github.repository }}
|
|
67
|
+
tags: |
|
|
68
|
+
type=semver,pattern={{version}}
|
|
69
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
70
|
+
type=raw,value=latest
|
|
71
|
+
|
|
72
|
+
- name: Build and push (linux/amd64 + linux/arm64)
|
|
73
|
+
uses: docker/build-push-action@v7
|
|
74
|
+
with:
|
|
75
|
+
context: .
|
|
76
|
+
platforms: linux/amd64,linux/arm64
|
|
77
|
+
push: true
|
|
78
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
79
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
80
|
+
cache-from: type=gha
|
|
81
|
+
cache-to: type=gha,mode=max
|
|
82
|
+
|
|
83
|
+
pypi:
|
|
84
|
+
needs: gate
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
environment: pypi # matches the trusted-publisher config on PyPI
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v6
|
|
89
|
+
|
|
90
|
+
- name: Install uv
|
|
91
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
92
|
+
|
|
93
|
+
- name: Build wheel + sdist
|
|
94
|
+
run: uv build
|
|
95
|
+
|
|
96
|
+
- name: Publish to PyPI
|
|
97
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
98
|
+
|
|
99
|
+
# Generate the Highlights paragraph + categorized list, commit the new
|
|
100
|
+
# CHANGELOG.md section back to main, and create the GitHub Release. Runs
|
|
101
|
+
# AFTER docker + pypi so the Release page points at artifacts that already
|
|
102
|
+
# exist. Two-phase invocation of generate_changelog.py keeps the LLM call
|
|
103
|
+
# to a single round-trip even though the commit-back happens onto a fresh
|
|
104
|
+
# main checkout.
|
|
105
|
+
changelog:
|
|
106
|
+
needs: [gate, docker, pypi]
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
permissions:
|
|
109
|
+
contents: write # scoped here — only this job commits CHANGELOG.md back to main
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/checkout@v6
|
|
112
|
+
with:
|
|
113
|
+
fetch-depth: 0 # full history + all tags for git-cliff
|
|
114
|
+
# default ref = the tag, which is what we want for `--mode=generate`
|
|
115
|
+
|
|
116
|
+
- name: Install uv
|
|
117
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
118
|
+
|
|
119
|
+
- name: Install git-cliff
|
|
120
|
+
uses: taiki-e/install-action@v2
|
|
121
|
+
with:
|
|
122
|
+
tool: git-cliff
|
|
123
|
+
|
|
124
|
+
- name: Generate release-body.md (LLM call happens here)
|
|
125
|
+
env:
|
|
126
|
+
# Set at the ParkviewLab org level — inherited by every repo
|
|
127
|
+
# under the org. Missing key → script writes a placeholder
|
|
128
|
+
# paragraph and the release still ships.
|
|
129
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
130
|
+
GITHUB_REF: ${{ github.ref }}
|
|
131
|
+
# `uv run --with anthropic` injects the SDK for just this invocation,
|
|
132
|
+
# without polluting pyproject.toml. Lets this workflow snippet be
|
|
133
|
+
# identical across every ParkviewLab repo regardless of whether
|
|
134
|
+
# `anthropic` is otherwise a project dep.
|
|
135
|
+
run: uv run --with anthropic python scripts/generate_changelog.py --mode=generate
|
|
136
|
+
|
|
137
|
+
- name: Switch to fresh origin/main and insert into CHANGELOG.md
|
|
138
|
+
run: |
|
|
139
|
+
# Stash the just-generated release body so it survives the checkout.
|
|
140
|
+
cp release-body.md /tmp/release-body.md
|
|
141
|
+
git fetch origin main
|
|
142
|
+
git checkout origin/main
|
|
143
|
+
mv /tmp/release-body.md release-body.md
|
|
144
|
+
# Re-runs only the file-manipulation half — no LLM call, no anthropic SDK needed.
|
|
145
|
+
uv run python scripts/generate_changelog.py --mode=insert
|
|
146
|
+
|
|
147
|
+
- name: Commit CHANGELOG.md back to main
|
|
148
|
+
run: |
|
|
149
|
+
git config user.name "github-actions[bot]"
|
|
150
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
151
|
+
git add CHANGELOG.md
|
|
152
|
+
if git diff --staged --quiet; then
|
|
153
|
+
echo "no changelog change to commit"
|
|
154
|
+
exit 0
|
|
155
|
+
fi
|
|
156
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
157
|
+
# [skip ci] guards against any future non-tag-triggered workflows;
|
|
158
|
+
# this release workflow only fires on `push: tags` so isn't at risk
|
|
159
|
+
# of recursing, but the marker is cheap insurance.
|
|
160
|
+
git commit -m "docs(changelog): $TAG [skip ci]"
|
|
161
|
+
git push origin HEAD:main
|
|
162
|
+
|
|
163
|
+
- name: Create GitHub Release
|
|
164
|
+
env:
|
|
165
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
166
|
+
run: |
|
|
167
|
+
TAG="${GITHUB_REF#refs/tags/}"
|
|
168
|
+
gh release create "$TAG" \
|
|
169
|
+
--title "$TAG" \
|
|
170
|
+
--notes-file release-body.md \
|
|
171
|
+
--target main
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
name: REUSE
|
|
6
|
+
|
|
7
|
+
# REUSE/SPDX compliance — a required check in every repo (code and docs).
|
|
8
|
+
# See the handbook's licensing.md and ci.md.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: [main, develop]
|
|
13
|
+
push:
|
|
14
|
+
branches: [main, develop]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
reuse:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
- uses: astral-sh/setup-uv@v8.1.0
|
|
25
|
+
- name: reuse lint
|
|
26
|
+
run: uvx --from "reuse[charset-normalizer]" reuse lint
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
name: Test
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [main, develop]
|
|
10
|
+
push:
|
|
11
|
+
branches: [main, develop]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v6
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
24
|
+
|
|
25
|
+
- name: Sync deps
|
|
26
|
+
run: uv sync
|
|
27
|
+
|
|
28
|
+
- name: Lint
|
|
29
|
+
run: uv run ruff check src tests
|
|
30
|
+
|
|
31
|
+
- name: Format check
|
|
32
|
+
run: uv run ruff format --check src tests
|
|
33
|
+
|
|
34
|
+
- name: Type check
|
|
35
|
+
run: uv run ty check # ty must be a dev dependency
|
|
36
|
+
|
|
37
|
+
- name: Tests (excluding network + docling marker)
|
|
38
|
+
run: uv run pytest -m "not integration" -q
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
name: Version guard
|
|
6
|
+
|
|
7
|
+
# Feature PRs into `develop` must NOT change the version source-of-truth — the
|
|
8
|
+
# bump happens at release, on `main` (see the handbook's releases.md / ci.md).
|
|
9
|
+
# The release back-merge (main→develop) is a direct push, not a PR, so it is not
|
|
10
|
+
# subject to this check.
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [develop]
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
no-version-change:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
- name: Version SoT must be unchanged
|
|
27
|
+
env:
|
|
28
|
+
BASE: ${{ github.base_ref }}
|
|
29
|
+
run: |
|
|
30
|
+
git fetch --no-tags --depth=1 origin "$BASE"
|
|
31
|
+
fail=0
|
|
32
|
+
flag() { echo "::error::$1 changed in a feature PR — version bumps happen at release on main, not in feature work (handbook releases.md)."; fail=1; }
|
|
33
|
+
if [ -f VERSION.txt ]; then
|
|
34
|
+
old=$(git show "origin/$BASE:VERSION.txt" 2>/dev/null || echo "")
|
|
35
|
+
[ "$(cat VERSION.txt)" = "$old" ] || flag VERSION.txt
|
|
36
|
+
fi
|
|
37
|
+
if [ -f pyproject.toml ]; then
|
|
38
|
+
new=$(grep -E '^version *=' pyproject.toml | head -1 || true)
|
|
39
|
+
old=$(git show "origin/$BASE:pyproject.toml" 2>/dev/null | grep -E '^version *=' | head -1 || true)
|
|
40
|
+
[ "$new" = "$old" ] || flag "pyproject.toml [project].version"
|
|
41
|
+
fi
|
|
42
|
+
if [ -f package.json ]; then
|
|
43
|
+
new=$(grep -E '"version"' package.json | head -1 || true)
|
|
44
|
+
old=$(git show "origin/$BASE:package.json" 2>/dev/null | grep -E '"version"' | head -1 || true)
|
|
45
|
+
[ "$new" = "$old" ] || flag "package.json version"
|
|
46
|
+
fi
|
|
47
|
+
if [ "$fail" = 0 ]; then echo "version unchanged ✓"; fi
|
|
48
|
+
exit $fail
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Build artifacts (uv build / hatchling)
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.ty_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
|
|
18
|
+
# venv / uv
|
|
19
|
+
.venv/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
|
|
25
|
+
# OS
|
|
26
|
+
.DS_Store
|
|
27
|
+
Thumbs.db
|
|
28
|
+
|
|
29
|
+
# Local test artifacts
|
|
30
|
+
/tmp-wiki/
|
|
31
|
+
|
|
32
|
+
# Transient release artifact: scripts/generate_changelog.py writes this for
|
|
33
|
+
# `gh release create --notes-file`; it's regenerated every release, never committed.
|
|
34
|
+
release-body.md
|
|
35
|
+
|
|
36
|
+
# Secrets
|
|
37
|
+
.env
|
|
38
|
+
.env.*
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Canonicalize commit identity to the public project identity.
|
|
2
|
+
# Maps the personal GitHub handle email to the ParkviewLab identity so any
|
|
3
|
+
# tooling (git shortlog, blame, forges) shows the canonical author/committer.
|
|
4
|
+
Gary Frattarola <garyf@parkviewlab.ai> <garycoding@gmail.com>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<!-- PARKVIEWLAB:BEGIN (managed by ParkviewLab/handbook — do not edit inside this block; run scripts/sync-agent-files.sh to update) -->
|
|
8
|
+
# ParkviewLab conventions
|
|
9
|
+
|
|
10
|
+
This repo follows the **[ParkviewLab handbook](https://github.com/ParkviewLab/handbook/tree/main)** —
|
|
11
|
+
the single source of truth for how we work. These pointer files **don't re-inline** its rules:
|
|
12
|
+
**read the handbook before non-trivial work**, starting with
|
|
13
|
+
**[`ai-collaboration.md`](https://github.com/ParkviewLab/handbook/blob/main/docs/ai-collaboration.md)**
|
|
14
|
+
(the behavioural contract). Only the safety-critical guardrails are summarized here.
|
|
15
|
+
|
|
16
|
+
**If present, read `docs/northstar.md` before working.** It states the project's intent and is authoritative.
|
|
17
|
+
|
|
18
|
+
## Shared-state writes need explicit authorization
|
|
19
|
+
- **Merging a PR into `develop` is the user's call.** A broad directive ("fix all that", "finish it") authorizes work on the branch, **not** the merge.
|
|
20
|
+
- **Tagging, cutting a release, force-pushing, or pushing to a protected branch each need an explicit, per-action go-ahead** — never inferred from a descriptive label (e.g. "→ v0.1.1"). One release ask covers the whole CLI release flow.
|
|
21
|
+
|
|
22
|
+
## Workflow basics
|
|
23
|
+
- Work in an ephemeral, **prefixed** worktree off `develop` (`feature-`/`fix-`/`doc-`/…) — don't commit on `develop`/`main` directly. Open a PR **into `develop`**.
|
|
24
|
+
- PRs are **squash-merged**, so the **PR title** carries the Conventional Commit prefix (`feat:`/`fix:`/`docs:`/…) the changelog is generated from.
|
|
25
|
+
|
|
26
|
+
**Everything else lives in the handbook** (don't rely on memory): branching, commits & changelogs, releases, Python tooling, CI, licensing, and the full communication norms — see <https://github.com/ParkviewLab/handbook/tree/main/docs>.
|
|
27
|
+
<!-- PARKVIEWLAB:END -->
|
|
28
|
+
|
|
29
|
+
<!-- Repo-specific guidance below this line is preserved by the sync script — add anything particular to this repo here. -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are recorded here. Each release entry
|
|
4
|
+
has two parts:
|
|
5
|
+
|
|
6
|
+
- **Highlights** — a 2-3 sentence "what's new" paragraph generated at
|
|
7
|
+
release time by an Anthropic-API call (see
|
|
8
|
+
`scripts/generate_changelog.py`).
|
|
9
|
+
- **Categorized changes** — a list of merged commits since the previous
|
|
10
|
+
tag, grouped by [Conventional Commit](https://www.conventionalcommits.org/)
|
|
11
|
+
prefix, produced by [git-cliff](https://git-cliff.org/) using
|
|
12
|
+
`cliff.toml`.
|
|
13
|
+
|
|
14
|
+
The release workflow on every tag push regenerates both, commits the new
|
|
15
|
+
section here, and uses the same content as the GitHub Release body.
|
|
16
|
+
|
|
17
|
+
<!--
|
|
18
|
+
Keep-a-Changelog ordering: [Unreleased] at the top, then newest
|
|
19
|
+
released version, then older versions. generate_changelog.py inserts
|
|
20
|
+
new "## [vX.Y.Z] - YYYY-MM-DD" sections directly below [Unreleased].
|
|
21
|
+
Don't remove the marker.
|
|
22
|
+
-->
|
|
23
|
+
|
|
24
|
+
## [Unreleased]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Gary Frattarola <garyf@parkviewlab.ai>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<!-- PARKVIEWLAB:BEGIN (managed by ParkviewLab/handbook — do not edit inside this block; run scripts/sync-agent-files.sh to update) -->
|
|
8
|
+
# ParkviewLab conventions
|
|
9
|
+
|
|
10
|
+
This repo follows the **[ParkviewLab handbook](https://github.com/ParkviewLab/handbook/tree/main)** —
|
|
11
|
+
the single source of truth for how we work. These pointer files **don't re-inline** its rules:
|
|
12
|
+
**read the handbook before non-trivial work**, starting with
|
|
13
|
+
**[`ai-collaboration.md`](https://github.com/ParkviewLab/handbook/blob/main/docs/ai-collaboration.md)**
|
|
14
|
+
(the behavioural contract). Only the safety-critical guardrails are summarized here.
|
|
15
|
+
|
|
16
|
+
**If present, read `docs/northstar.md` before working.** It states the project's intent and is authoritative.
|
|
17
|
+
|
|
18
|
+
## Shared-state writes need explicit authorization
|
|
19
|
+
- **Merging a PR into `develop` is the user's call.** A broad directive ("fix all that", "finish it") authorizes work on the branch, **not** the merge.
|
|
20
|
+
- **Tagging, cutting a release, force-pushing, or pushing to a protected branch each need an explicit, per-action go-ahead** — never inferred from a descriptive label (e.g. "→ v0.1.1"). One release ask covers the whole CLI release flow.
|
|
21
|
+
|
|
22
|
+
## Workflow basics
|
|
23
|
+
- Work in an ephemeral, **prefixed** worktree off `develop` (`feature-`/`fix-`/`doc-`/…) — don't commit on `develop`/`main` directly. Open a PR **into `develop`**.
|
|
24
|
+
- PRs are **squash-merged**, so the **PR title** carries the Conventional Commit prefix (`feat:`/`fix:`/`docs:`/…) the changelog is generated from.
|
|
25
|
+
|
|
26
|
+
**Everything else lives in the handbook** (don't rely on memory): branching, commits & changelogs, releases, Python tooling, CI, licensing, and the full communication norms — see <https://github.com/ParkviewLab/handbook/tree/main/docs>.
|
|
27
|
+
<!-- PARKVIEWLAB:END -->
|
|
28
|
+
|
|
29
|
+
<!-- Repo-specific guidance below this line is preserved by the sync script — add anything particular to this repo here. -->
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Layer 1: resolve project deps for cache stability across source changes.
|
|
6
|
+
COPY pyproject.toml uv.lock ./
|
|
7
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
8
|
+
uv sync --no-dev --no-install-project
|
|
9
|
+
|
|
10
|
+
# Layer 2: install the project itself + the three sibling MCP servers
|
|
11
|
+
# cobalt-grinding spawns as children. README.md is part of pyproject's
|
|
12
|
+
# metadata (project.readme); the first uv sync skipped reading it, but
|
|
13
|
+
# the second sync (with --install-project) needs it. Bundling the
|
|
14
|
+
# children keeps the "out of the box demo" working — `docker run …`
|
|
15
|
+
# starts a daemon that has a Smalt + lab notebook + code parser ready
|
|
16
|
+
# without extra installs. Operators who want to swap or omit a child
|
|
17
|
+
# can override the [mcp.clients.*] config or remove its env entry.
|
|
18
|
+
COPY README.md ./
|
|
19
|
+
COPY src/ src/
|
|
20
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
21
|
+
uv sync --no-dev && \
|
|
22
|
+
uv pip install --system smalt-mcp ebony-enriching deco-assaying
|
|
23
|
+
|
|
24
|
+
# Container env. The three substrate / capability dirs land under
|
|
25
|
+
# /data, mounted via VOLUME. cobalt-grinding's config layer reads
|
|
26
|
+
# `COBALT_GRINDING_SMALT_DIR` / `_EBONY_DIR` / `_COBALT_GRINDING_DIR`
|
|
27
|
+
# and injects them into the matching child's env at startup.
|
|
28
|
+
#
|
|
29
|
+
# `MCP_HOST=0.0.0.0` is the bind address for the HTTP transport —
|
|
30
|
+
# 127.0.0.1 (the default) wouldn't accept connections from outside
|
|
31
|
+
# the container.
|
|
32
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
33
|
+
COBALT_GRINDING_SMALT_DIR=/data/smalt \
|
|
34
|
+
COBALT_GRINDING_EBONY_DIR=/data/ebony \
|
|
35
|
+
COBALT_GRINDING_COBALT_GRINDING_DIR=/data/cobalt_grinding \
|
|
36
|
+
COBALT_GRINDING_MCP_HOST=0.0.0.0 \
|
|
37
|
+
COBALT_GRINDING_MCP_PORT=7474
|
|
38
|
+
|
|
39
|
+
EXPOSE 7474
|
|
40
|
+
VOLUME ["/data"]
|
|
41
|
+
|
|
42
|
+
# `ANTHROPIC_API_KEY` must be passed at run time
|
|
43
|
+
# (`docker run -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY …`). Without it
|
|
44
|
+
# the daemon still starts and substrate-bootstraps; cognitive-skill
|
|
45
|
+
# tools (wiki.ask, etc.) will return a clear error at call time.
|
|
46
|
+
CMD ["uv", "run", "cobalt-grinding"]
|