Compare commits

...
4 Commits
Author SHA1 Message Date
bvandeusen 9ddc418f5f Merge pull request 'main publishes :latest + ACL _INSTRUCTIONS guard' (#59) from dev into main
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 49s
CI & Build / Build & push image (push) Successful in 15s
2026-06-03 14:41:15 -04:00
bvandeusenandClaude Opus 4.8 1d4c206563 ci: main publishes :latest (main is the production line)
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 54s
CI & Build / Build & push image (push) Successful in 18s
main pushes now move :latest (in addition to the immutable :<sha>), so a
merge to main updates production's pointer directly — no separate release
needed just to refresh :latest. The v* release tag's distinct job becomes
the dated :<version> marker (it still refreshes :latest harmlessly). Still
no :main tag. Rules 47/46 + 10/4 updated to match on both instances.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:27:53 -04:00
bvandeusen 301352f628 Merge pull request 'MCP _INSTRUCTIONS: multi-user sharing ACL guard' (#58) from dev into main
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Python tests (push) Successful in 55s
CI & Build / Build & push image (push) Successful in 25s
2026-06-03 14:18:01 -04:00
bvandeusenandClaude Opus 4.8 d4666bea7f feat(mcp): add multi-user sharing ACL guard to _INSTRUCTIONS
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 44s
CI & Build / Build & push image (push) Successful in 1m15s
The ACL constraint (scope every read/mutation by owner + shares via
services/access.py) is a security-correctness invariant that should
always be loaded, and it's FabledScribe-specific — so it belongs in
Scribe's own contained _INSTRUCTIONS, not the cross-project FabledSword
rulebook. The redundant rulebook rule will be retired once this ships
to prod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:47:02 -04:00
2 changed files with 18 additions and 10 deletions
+11 -10
View File
@@ -1,15 +1,15 @@
# CI runs first; build only proceeds if all checks pass. # CI runs first; build only proceeds if all checks pass.
# #
# Push to dev: typecheck + lint + test + build :dev + :<sha> # Push to dev: typecheck + lint + test + build :dev + :<sha>
# Push to main: typecheck + lint + test + build :<sha> (no moving tag) # Push to main: typecheck + lint + test + build :latest + :<sha>
# Tag v* (release): typecheck + lint + test + build :latest + :<version> + :<sha> # Tag v* (release): typecheck + lint + test + build :latest + :<version> + :<sha>
# #
# Both dev and main are gated AND built. dev pushes move the :dev tag; main # Both dev and main are gated AND built. dev pushes move :dev; main pushes move
# pushes publish only the immutable :<sha> image — no :main tag, because # :latest — main IS the production line, so :latest tracks main's tip and there
# :latest (release-only) is the single production pointer and a :main alias # is no separate :main tag. Every push also gets an immutable :<sha> (the
# would just duplicate it. Running CI on the main merge commit is intentional: # rollback point). A v* release tag additionally publishes the dated :<version>;
# main is validated and its :<sha> image is the rollback point. The v* release # since main already moved :latest, the release tag's distinct job is that
# tag is the ONLY trigger that publishes :latest plus the immutable :<version>. # :<version> marker (it refreshes :latest too, harmlessly).
# #
# Successive pushes to the SAME ref supersede each other (see concurrency # Successive pushes to the SAME ref supersede each other (see concurrency
# below), so rapid pushes don't stack identical work; dev and main runs are # below), so rapid pushes don't stack identical work; dev and main runs are
@@ -142,7 +142,7 @@ jobs:
build: build:
name: Build & push image name: Build & push image
needs: [typecheck, lint, test] needs: [typecheck, lint, test]
# Build on dev, main, and v* tag pushes. dev → :dev, main → (sha only), # Build on dev, main, and v* tag pushes. dev → :dev, main → :latest,
# tag → :latest + :<version>; every build also gets an immutable :<sha>. # tag → :latest + :<version>; every build also gets an immutable :<sha>.
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
runs-on: python-ci runs-on: python-ci
@@ -171,8 +171,9 @@ jobs:
TAGS="$TAGS,${{ env.IMAGE }}:dev" TAGS="$TAGS,${{ env.IMAGE }}:dev"
;; ;;
refs/heads/main) refs/heads/main)
# main publishes only the immutable :<sha> image (set above) — # main IS the production line: publish :latest (plus the :<sha>
# no :main tag; :latest (release-only) is the production pointer. # set above). No separate :main tag.
TAGS="$TAGS,${{ env.IMAGE }}:latest"
BUILD_VERSION="main" BUILD_VERSION="main"
;; ;;
refs/tags/*) refs/tags/*)
+7
View File
@@ -122,6 +122,13 @@ X process" or otherwise references a saved process, call list_processes() /
get_process(name) and follow the returned prompt verbatim, including any get_process(name) and follow the returned prompt verbatim, including any
"clarify first" steps it contains. Author a new one with create_process(title, "clarify first" steps it contains. Author a new one with create_process(title,
body); edit with update_process. body); edit with update_process.
When you are developing Scribe itself (not just using it as a data store),
honor the multi-user sharing ACL: every read or mutation of user data must
scope by owner + direct shares + group shares through services/access.py
(can_read_* / can_write_* / can_admin_*) — never assume a single operator. An
unscoped query (a fetch-by-id with no ownership check) is a cross-user data
leak; "works for one user" is not done.
""" """