diff --git a/.github/workflows/architecture-docs-freshness.yml b/.github/workflows/architecture-docs-freshness.yml new file mode 100644 index 0000000..f2d4e89 --- /dev/null +++ b/.github/workflows/architecture-docs-freshness.yml @@ -0,0 +1,106 @@ +name: architecture-docs freshness + +on: + pull_request: + branches: [main] + push: + branches: [main] + +permissions: + contents: read + +jobs: + check-submodule-current: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Compare pinned submodule SHA against architecture-docs/main + env: + GH_TOKEN: ${{ secrets.ARCHITECTURE_DOCS_READ_PAT || secrets.ALL_REPO_CHECKOUT_TOKEN }} + EVENT_NAME: ${{ github.event_name }} + ACTOR: ${{ github.actor }} + REPOSITORY: ${{ github.repository }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + run: | + set -euo pipefail + + SKIP_REASON="" + PR_HEAD_REPOSITORY="${PR_HEAD_REPOSITORY:-$REPOSITORY}" + + if [ "$EVENT_NAME" = "pull_request" ]; then + if [ "$PR_HEAD_REPOSITORY" != "$REPOSITORY" ]; then + SKIP_REASON="fork pull_request runs do not receive repository secrets" + elif [ "$ACTOR" = "dependabot[bot]" ]; then + SKIP_REASON="Dependabot pull_request runs do not receive normal Actions secrets" + else + SKIP_REASON="ARCHITECTURE_DOCS_READ_PAT or ALL_REPO_CHECKOUT_TOKEN is not configured for this repository yet" + fi + else + SKIP_REASON="ARCHITECTURE_DOCS_READ_PAT or ALL_REPO_CHECKOUT_TOKEN is not configured for this repository yet" + fi + + if [ -z "${GH_TOKEN:-}" ]; then + echo "::warning::Skipping architecture-docs freshness check: $SKIP_REASON." + echo "This workflow enforces freshness only in runs that receive ARCHITECTURE_DOCS_READ_PAT or ALL_REPO_CHECKOUT_TOKEN." + echo "Configure one of those tokens with read access to NaradaAI/architecture-docs to enable enforcement." + exit 0 + fi + + PINNED=$(git ls-tree HEAD architecture-docs | awk '{print $3}') + if [ -z "$PINNED" ]; then + echo "::error::No architecture-docs submodule pointer found in this commit." + exit 1 + fi + + REMOTE=$(gh api repos/NaradaAI/architecture-docs/git/refs/heads/main --jq '.object.sha' 2>/dev/null || true) + if [ -z "$REMOTE" ]; then + echo "::error::Failed to read architecture-docs/main with the configured token." + echo "Check that ARCHITECTURE_DOCS_READ_PAT or ALL_REPO_CHECKOUT_TOKEN has read access to NaradaAI/architecture-docs." + exit 1 + fi + + echo "Pinned: $PINNED" + echo "Latest: $REMOTE" + + POINTER_CHANGED=false + if [ "$EVENT_NAME" = "pull_request" ]; then + if [ -n "${BASE_SHA:-}" ]; then + if git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null || git fetch --no-tags --depth=1 origin "$BASE_SHA"; then + BASE_PINNED=$(git ls-tree "$BASE_SHA" architecture-docs | awk '{print $3}') + echo "Base: ${BASE_PINNED:-none}" + if [ "$PINNED" != "$BASE_PINNED" ]; then + POINTER_CHANGED=true + fi + else + echo "::warning::Unable to fetch pull request base commit; treating the architecture-docs pointer as changed." + POINTER_CHANGED=true + fi + else + echo "::warning::Unable to determine pull request base SHA; treating the architecture-docs pointer as changed." + POINTER_CHANGED=true + fi + fi + + if [ "$PINNED" = "$REMOTE" ]; then + echo "architecture-docs submodule is at main HEAD." + exit 0 + fi + + if [ "$EVENT_NAME" = "pull_request" ] && [ "$POINTER_CHANGED" = "false" ]; then + echo "::warning::architecture-docs submodule is stale, but this pull request does not change the pointer." + echo "Freshness is enforced when a pull request changes architecture-docs and on pushes to main." + exit 0 + fi + + echo "::error::architecture-docs submodule is stale." + echo "" + echo "To bump the pointer:" + echo " git submodule update --remote architecture-docs" + echo " git add architecture-docs" + echo " git commit -m 'Bump architecture-docs'" + echo " git push" + exit 1 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2dfcd02 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "architecture-docs"] + path = architecture-docs + url = ../../NaradaAI/architecture-docs.git + branch = main diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7dd30c1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,36 @@ +# Project context + +`narada-python-sdk` is Narada's public Python SDK — a uv workspace with three packages (`narada`, `narada-core`, `narada-pyodide`) that lets external callers drive Narada agents programmatically. It's one of three sibling repos in the Narada system; shared cross-repo architecture lives in [`architecture-docs/`](./architecture-docs/) (a git submodule). + +## Bootstrap shared docs + +If `architecture-docs/CLAUDE.md` is missing, initialize the shared docs before following the links below: + +```bash +git submodule update --init architecture-docs +``` + +## Before changing code, read + +- [`architecture-docs/CLAUDE.md`](./architecture-docs/CLAUDE.md) — rules for AI coding agents (read **first**) +- [`architecture-docs/overview.md`](./architecture-docs/overview.md) — 10-minute orientation across the three-repo system +- [`architecture-docs/python-sdk.md`](./architecture-docs/python-sdk.md) — workspace layout, parity rule, public types (this repo) +- [`architecture-docs/api-contracts.md`](./architecture-docs/api-contracts.md) — `/remote-dispatch`, `/extension-actions`, and other endpoints this SDK calls +- [`architecture-docs/conventions.md`](./architecture-docs/conventions.md) — naming, code style +- Other docs in `architecture-docs/` for backend / browser-automation / agent-studio context + +## When to update the docs + +When you change a public type, add a new SDK action, change the wire shape between SDK and backend, or change the parity rule between `narada` and `narada-pyodide` — open the relevant docs change in `NaradaAI/architecture-docs` first. Merge that docs PR to `architecture-docs/main`, then bump this repo's `architecture-docs` submodule pointer in the code PR. The full trigger list is in `architecture-docs/CLAUDE.md` §3. + +## Updating the submodule pointer + +Merge shared documentation changes into `architecture-docs/main` first, then bump this repo's submodule pointer. CI fails PRs that change the `architecture-docs` pointer to anything other than `architecture-docs/main`, and it fails pushes to `main` when the pointer is stale. Unrelated PRs whose pointer is unchanged only receive a freshness warning if `architecture-docs/main` has moved. + +```bash +git submodule update --remote architecture-docs +git add architecture-docs +git commit -m "Bump architecture-docs" +``` + +CI runs a freshness check (`.github/workflows/architecture-docs-freshness.yml`) that enforces this paired-PR workflow without blocking unrelated PRs when only `architecture-docs/main` changed. diff --git a/architecture-docs b/architecture-docs new file mode 160000 index 0000000..d2a82e1 --- /dev/null +++ b/architecture-docs @@ -0,0 +1 @@ +Subproject commit d2a82e1c76af3e702a13cd760f39af02bdf2bc0e