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.
Files changed (96) hide show
  1. cobalt_grinding-0.1.0/.dockerignore +21 -0
  2. cobalt_grinding-0.1.0/.github/workflows/dev-release.yml +103 -0
  3. cobalt_grinding-0.1.0/.github/workflows/release.yml +171 -0
  4. cobalt_grinding-0.1.0/.github/workflows/reuse.yml +26 -0
  5. cobalt_grinding-0.1.0/.github/workflows/test.yml +38 -0
  6. cobalt_grinding-0.1.0/.github/workflows/version-guard.yml +48 -0
  7. cobalt_grinding-0.1.0/.gitignore +38 -0
  8. cobalt_grinding-0.1.0/.mailmap +4 -0
  9. cobalt_grinding-0.1.0/.mcp.json +8 -0
  10. cobalt_grinding-0.1.0/.python-version +1 -0
  11. cobalt_grinding-0.1.0/AGENTS.md +29 -0
  12. cobalt_grinding-0.1.0/CHANGELOG.md +24 -0
  13. cobalt_grinding-0.1.0/CLAUDE.md +29 -0
  14. cobalt_grinding-0.1.0/Dockerfile +46 -0
  15. cobalt_grinding-0.1.0/LICENSE +661 -0
  16. cobalt_grinding-0.1.0/LICENSES/AGPL-3.0-or-later.txt +661 -0
  17. cobalt_grinding-0.1.0/LICENSING.md +23 -0
  18. cobalt_grinding-0.1.0/PKG-INFO +110 -0
  19. cobalt_grinding-0.1.0/README.md +90 -0
  20. cobalt_grinding-0.1.0/REUSE.toml +9 -0
  21. cobalt_grinding-0.1.0/cliff.toml +55 -0
  22. cobalt_grinding-0.1.0/docs/CONTRIBUTING.md +71 -0
  23. cobalt_grinding-0.1.0/docs/architecture.dot +213 -0
  24. cobalt_grinding-0.1.0/docs/existing_MCP_servers_to_consider/anthropic_model_info_roll_your_own.md +94 -0
  25. cobalt_grinding-0.1.0/docs/existing_MCP_servers_to_consider/llama-mcp-server-analysis.md +591 -0
  26. cobalt_grinding-0.1.0/docs/ideation.md +314 -0
  27. cobalt_grinding-0.1.0/docs/m3-deco-assaying-findings.md +281 -0
  28. cobalt_grinding-0.1.0/docs/northstar.md +184 -0
  29. cobalt_grinding-0.1.0/docs/plan.md +1281 -0
  30. cobalt_grinding-0.1.0/docs/what_an_agent_needs_to_know.md +67 -0
  31. cobalt_grinding-0.1.0/pyproject.toml +97 -0
  32. cobalt_grinding-0.1.0/scripts/generate_changelog.py +269 -0
  33. cobalt_grinding-0.1.0/src/cobalt_grinding/__init__.py +7 -0
  34. cobalt_grinding-0.1.0/src/cobalt_grinding/app.py +224 -0
  35. cobalt_grinding-0.1.0/src/cobalt_grinding/common/__init__.py +3 -0
  36. cobalt_grinding-0.1.0/src/cobalt_grinding/config.py +283 -0
  37. cobalt_grinding-0.1.0/src/cobalt_grinding/converse/__init__.py +3 -0
  38. cobalt_grinding-0.1.0/src/cobalt_grinding/converse/orchestrator.py +359 -0
  39. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/__init__.py +5 -0
  40. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/bootstrap.py +149 -0
  41. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/main.py +314 -0
  42. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/mcp_clients.py +420 -0
  43. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/mutex.py +80 -0
  44. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/scheduler.py +262 -0
  45. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/server.py +84 -0
  46. cobalt_grinding-0.1.0/src/cobalt_grinding/daemon/tools.py +505 -0
  47. cobalt_grinding-0.1.0/src/cobalt_grinding/host/__init__.py +25 -0
  48. cobalt_grinding-0.1.0/src/cobalt_grinding/host/api.py +143 -0
  49. cobalt_grinding-0.1.0/src/cobalt_grinding/host/dispatch.py +80 -0
  50. cobalt_grinding-0.1.0/src/cobalt_grinding/host/embedder.py +92 -0
  51. cobalt_grinding-0.1.0/src/cobalt_grinding/host/loop.py +216 -0
  52. cobalt_grinding-0.1.0/src/cobalt_grinding/host/provider.py +104 -0
  53. cobalt_grinding-0.1.0/src/cobalt_grinding/host/tools_index.py +275 -0
  54. cobalt_grinding-0.1.0/src/cobalt_grinding/host/tools_index_db.py +52 -0
  55. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/__init__.py +3 -0
  56. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/agents.py +350 -0
  57. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/deco_client.py +190 -0
  58. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/flint_client.py +160 -0
  59. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/format_classifier.py +97 -0
  60. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/handlers.py +206 -0
  61. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/orchestrator.py +1184 -0
  62. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/smalt_client.py +352 -0
  63. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/source_fetcher.py +257 -0
  64. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/structure_extractor.py +146 -0
  65. cobalt_grinding-0.1.0/src/cobalt_grinding/ingest/url_fetcher.py +157 -0
  66. cobalt_grinding-0.1.0/src/cobalt_grinding/retrieve/__init__.py +3 -0
  67. cobalt_grinding-0.1.0/src/cobalt_grinding/retrieve/orchestrator.py +317 -0
  68. cobalt_grinding-0.1.0/tests/__init__.py +3 -0
  69. cobalt_grinding-0.1.0/tests/conftest.py +78 -0
  70. cobalt_grinding-0.1.0/tests/fixtures/stub_mcp_server.py +55 -0
  71. cobalt_grinding-0.1.0/tests/test_app.py +161 -0
  72. cobalt_grinding-0.1.0/tests/test_config.py +163 -0
  73. cobalt_grinding-0.1.0/tests/test_converse_orchestrator.py +432 -0
  74. cobalt_grinding-0.1.0/tests/test_daemon_integration.py +357 -0
  75. cobalt_grinding-0.1.0/tests/test_daemon_mcp_clients.py +260 -0
  76. cobalt_grinding-0.1.0/tests/test_daemon_tools.py +541 -0
  77. cobalt_grinding-0.1.0/tests/test_host_api.py +193 -0
  78. cobalt_grinding-0.1.0/tests/test_host_dispatch.py +131 -0
  79. cobalt_grinding-0.1.0/tests/test_host_integration.py +113 -0
  80. cobalt_grinding-0.1.0/tests/test_host_loop.py +401 -0
  81. cobalt_grinding-0.1.0/tests/test_host_provider.py +152 -0
  82. cobalt_grinding-0.1.0/tests/test_host_tools_index.py +190 -0
  83. cobalt_grinding-0.1.0/tests/test_ingest_agents.py +231 -0
  84. cobalt_grinding-0.1.0/tests/test_ingest_deco_client.py +198 -0
  85. cobalt_grinding-0.1.0/tests/test_ingest_flint_client.py +230 -0
  86. cobalt_grinding-0.1.0/tests/test_ingest_format_classifier.py +93 -0
  87. cobalt_grinding-0.1.0/tests/test_ingest_handlers.py +188 -0
  88. cobalt_grinding-0.1.0/tests/test_ingest_orchestrator.py +1010 -0
  89. cobalt_grinding-0.1.0/tests/test_ingest_smalt_client.py +283 -0
  90. cobalt_grinding-0.1.0/tests/test_ingest_source_fetcher.py +187 -0
  91. cobalt_grinding-0.1.0/tests/test_ingest_structure_extractor.py +124 -0
  92. cobalt_grinding-0.1.0/tests/test_ingest_url_fetcher.py +204 -0
  93. cobalt_grinding-0.1.0/tests/test_mutex.py +64 -0
  94. cobalt_grinding-0.1.0/tests/test_retrieve_orchestrator.py +394 -0
  95. cobalt_grinding-0.1.0/tests/test_scheduler.py +151 -0
  96. 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,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "deco-assaying": {
4
+ "type": "http",
5
+ "url": "http://127.0.0.1:35832/sse"
6
+ }
7
+ }
8
+ }
@@ -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"]