diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69d5c97575..fa8f20fd07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -282,7 +282,7 @@ jobs: # TS2307 in CI while passing locally against a stale leftover dist/ -- first hit by PR #5082). Now ahead # of both. - name: Build engine package - if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' || needs.changes.outputs.engine == 'true' }} + if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' || needs.changes.outputs.engine == 'true' || needs.changes.outputs.ui == 'true' }} run: npm run build --workspace @jsonbored/gittensory-engine # .tsbuildinfo mutates every run (tsc's own incremental state), unlike node_modules above which is # immutable per lockfile -- so this needs the run_id-suffixed-key + restore-keys-prefix pattern (always diff --git a/.gittensory.yml.example b/.gittensory.yml.example index 7047f10e57..f4257cb726 100644 --- a/.gittensory.yml.example +++ b/.gittensory.yml.example @@ -1106,13 +1106,17 @@ settings: # an operator who sets nothing keeps today's behavior). Exception: `safety` is force-on-only -- an untrusted # repo-controlled `false` is treated as "no opinion" rather than an active force-off (#2269), since a # lower-trust actor must never be able to silently defeat the operator's own security-hardening enablement. -# `e2eTests` (#4190), `screenshots` (#4616), and `improvementSignal` (#4738, foundation phase of the #4737 -# PR-improvement-signal epic) are plain symmetric overrides like rag/reputation/unifiedComment -- none carries -# a security-hardening or full-file-fetch rationale (unlike safety/grounding) that would justify a force-on/ -# force-off asymmetry. `screenshots` sits UNDER the separate, richer `review.visual.*` block further below -# (route/preview-URL config, and `review.visual.enabled: false` as an always-available additional force-off); -# this key only answers "does capture run for this repo at all," the same question its siblings answer for -# their own feature. `improvementSignal` is activation wiring only for now -- no tier reads the resolved +# `screenshots` (#4616) is ALSO asymmetric as of #4990: allowlisted is now a hard requirement for force-ON, +# the same as safety/grounding -- real Browserless rendering cost and a publicly-rendered PR image mean a +# lower-trust repo must not be able to self-activate capture just by setting this to `true` in its own +# config; a repo-level `false` can still force it OFF within an allowlisted repo. `screenshots` sits UNDER +# the separate, richer `review.visual.*` block further below (route/preview-URL config, and +# `review.visual.enabled: false` as an always-available additional force-off); this key only answers "does +# capture run for this repo at all," the same question its siblings answer for their own feature. +# `e2eTests` (#4190) and `improvementSignal` (#4738, foundation phase of the #4737 PR-improvement-signal +# epic) remain plain symmetric overrides like rag/reputation/unifiedComment -- neither carries a +# security-hardening or full-file-fetch rationale that would justify a force-on/force-off asymmetry. +# `improvementSignal` is activation wiring only for now -- no tier reads the resolved # value yet. # features: # rag: true diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-ai-providers.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-ai-providers.tsx index 8711602164..80ffb75527 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-ai-providers.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-ai-providers.tsx @@ -186,6 +186,13 @@ CLAUDE_AI_EFFORT=medium`} instead:

+

+ Prefer not pasting the raw token into .env? Write it into{" "} + secrets/claude_code_oauth_token.txt instead (see{" "} + Security's secret-file section) and leave{" "} + CLAUDE_CODE_OAUTH_TOKEN unset in .env — the same recreate step + above picks it up either way. +

Codex (subscription)

@@ -216,7 +223,7 @@ CLAUDE_AI_EFFORT=medium`} { title: "claude_code_no_oauth_token", description: - "CLAUDE_CODE_OAUTH_TOKEN is unset. Add it to .env and recreate the service.", + "CLAUDE_CODE_OAUTH_TOKEN is unset and no secrets/claude_code_oauth_token.txt file is populated. Set either and recreate the service.", }, { title: "claude_code_error_401", diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-security.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-security.tsx index 83aeb69a4a..0a766f1b36 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-security.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-security.tsx @@ -56,12 +56,12 @@ function SelfHostingSecurity() { docker-compose.yml ships native Docker Compose secrets: mounts for the highest-value secrets (the GitHub App private key, webhook secret, API/MCP/internal-job tokens, the setup token, the two token-encryption master keys, the Orb enrollment secret, - and the PagerDuty routing key) — file-mounted at /run/secrets/<name>, - never exposed via docker inspect or docker compose config the way - a plain environment:/env_file value is. This is purely additive: - an inline .env value always takes priority if you set both, so you can migrate - one secret at a time, or not at all. See secrets/README.md for the full file - list. + the PagerDuty routing key, and the Claude Code subscription token) — file-mounted at{" "} + /run/secrets/<name>, never exposed via docker inspect or{" "} + docker compose config the way a plain environment:/ + env_file value is. This is purely additive: an inline .env value + always takes priority if you set both, so you can migrate one secret at a time, or not at + all. See secrets/README.md for the full file list.

{ it("redacts private lane signals from cached rationale before prompting the provider", async () => { const run = vi.fn(async () => ({ response: "Public-safe readiness answer." })); const env = createTestEnv({ AI_ADVISORY: { run } as unknown as Ai, AI_DAILY_NEURON_BUDGET: "10000" }); + // publicSafeSummary (not why/blockedBy -- compactChatSignalBundle never reads either of those, by design; + // see the redaction-boundary comment above PRIVATE_DECISION_BLOCKER_PATTERN) is the field that actually + // reaches the prompt, so it's the one that must exercise the new PRIVATE_LANE_SIGNAL_PATTERN end-to-end. const result = await generateChatQaAnswer(env, { bundle: bundleFixture(undefined, { - why: [ - "owner/repo: Maintainer cut: 1.", - "owner/repo: split lane (direct PR 1, issue-discovery 1); both lanes are useful here.", - "owner/repo: direct PR lane share 1 with no hard personal blocker.", - ], + publicSafeSummary: + "Maintainer cut: 1. Split lane (direct PR 1, issue-discovery 1); both lanes are useful here. Direct PR lane share 1 with no hard personal blocker.", }), question: "what should I know?", advisoryAiRouting: ADVISORY_ON,