Skip to content

Knowledge layer v2: notes, update-in-place, stats, tags#4

Merged
cmeans merged 2 commits into
mainfrom
knowledge-layer-v2
Mar 21, 2026
Merged

Knowledge layer v2: notes, update-in-place, stats, tags#4
cmeans merged 2 commits into
mainfrom
knowledge-layer-v2

Conversation

@cmeans
Copy link
Copy Markdown
Owner

@cmeans cmeans commented Mar 21, 2026

Summary

  • New note entry type: General-purpose permanent knowledge with optional content payload and MIME content_type. The catch-all for anything that isn't an operational pattern or time-limited context.
  • remember tool: Create notes — personal facts, project notes, skill backups, config snapshots.
  • update_entry tool: Update knowledge entries (note/pattern/context/preference) in place with _changelog tracking. Status/alert/suppression are immutable.
  • get_stats tool: Entry counts by type, sources list, total count.
  • get_tags tool: All tags with usage counts — prevents tag drift.
  • get_knowledge enhanced: New include_history param (false/true/only) controls _changelog visibility.
  • Docs updated: README tools table (14→18 tools), CLAUDE.md architecture notes.

Design decisions

  • note vs other names: chosen as the simplest general-purpose name; may evolve
  • remember vs add_note: remember is more natural for agent interaction
  • Immutable types: status (upserted, not edited), alert (fact record), suppression (time-bound contract) — editing these would rewrite history
  • _changelog stored inline in entry data, not a separate table — lightweight, co-located with the entry
  • include_history="only" returns only entries with changelog — useful for "what's changed?" queries

Test plan

  • 148 tests pass (19 new tests covering all new tools and edge cases)
  • ruff check clean
  • ruff format clean
  • mypy strict clean
  • Verify remember creates entries visible in get_knowledge
  • Verify update_entry rejects immutable types
  • Verify get_tags returns correct counts
  • Verify include_history filtering works

🤖 Generated with Claude Code

cmeans and others added 2 commits March 20, 2026 22:28
New entry type 'note' for general-purpose knowledge storage with
optional content payload and MIME type. New tools:
- remember: create notes (permanent, tagged, searchable)
- update_entry: update knowledge types in place with _changelog tracking
  (status/alert/suppression are immutable)
- get_stats: entry counts by type, sources, total
- get_tags: all tags with usage counts (prevents tag drift)

Enhanced get_knowledge with include_history param (false/true/only)
to control _changelog visibility in results.

148 tests, all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document new tools (remember, update_entry, get_stats, get_tags),
note entry type, include_history param, and changelog tracking.
Update tool count from 14 to 18, test count from 124 to 148.
Remove 'first-class knowledge entry type' from not-yet-implemented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cmeans cmeans merged commit 1168998 into main Mar 21, 2026
5 checks passed
@cmeans cmeans deleted the knowledge-layer-v2 branch March 21, 2026 22:33
cmeans pushed a commit that referenced this pull request Mar 31, 2026
1. Remove pull_policy: always — contradicts build: . (SUBSTANTIVE #1)
2. Default Ollama volume to ~/awareness-ollama-oauth to avoid
   concurrent access with production (SUBSTANTIVE #2)
3. Parameterize AWARENESS_PUBLIC_URL with env var (OBSERVATION #3)
4. Replace real WorkOS domain with placeholder in template (NIT #4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmeans pushed a commit that referenced this pull request Apr 1, 2026
1. Remove pull_policy: always — contradicts build: . (SUBSTANTIVE #1)
2. Default Ollama volume to ~/awareness-ollama-oauth to avoid
   concurrent access with production (SUBSTANTIVE #2)
3. Parameterize AWARENESS_PUBLIC_URL with env var (OBSERVATION #3)
4. Replace real WorkOS domain with placeholder in template (NIT #4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmeans pushed a commit that referenced this pull request Apr 7, 2026
- Fix maintenance deploy: sudo drops env vars, use sudo -u awareness
  bash -c with sourced env to preserve AWARENESS_DATABASE_URL (#1 blocker)
- Fix double-update of first node in maintenance deploy: update_node
  runs once, remaining nodes loop skips first entry (#2)
- Add usage comment to create-ct200.sh (#4)
- Update deployment plan to match fixed script

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmeans pushed a commit that referenced this pull request Apr 7, 2026
- #1: Specify two-step re-init (synthetic initialize + replay original)
  using stored registry metadata (capabilities, client_info, protocol_version)
- #2: Add session_redirects table for old→new session_id mapping with
  5-min grace period. Middleware transparently rewrites request headers.
- #3: Clarify sliding-window TTL — touch() extends expires_at
- #4: Document HAProxy stick table gap after rotation (self-healing)
- #5: Note deliberate LOGGED deviation from issue with rationale
- #6: Specify lookup() filters expired sessions (no re-init for zombies)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cmeans-claude-dev Bot added a commit that referenced this pull request Apr 21, 2026
…334) (#351)

Closes [#334](#334). Also
closes CodeQL alerts #1, #2, #3 (three flags of
`actions/missing-workflow-permissions` in `ci.yml`).

## Summary

Two workflow-hardening fixes bundled because they're the same theme
(least-privilege + contributor-controlled-input discipline) and both
surfaced from the same security review:

### 1. `pr-labels.yml` — cascade the `#333` env-routing pattern (closes
#334)

Three job steps in `pr-labels.yml` (`on-push`, `on-unlabel`, `on-label`)
previously inlined contributor-visible fields as `${{ ... }}`
expressions inside `run:` bodies:

```yaml
# Before
run: |
  PR=${{ github.event.pull_request.number }}
  REPO=${{ github.repository }}
  HEAD_SHA=${{ github.event.pull_request.head.sha }}
  # ... shell script references PR / REPO / HEAD_SHA
```

Now they route through step-level `env:` and are referenced as shell
variables:

```yaml
# After
env:
  PR: ${{ github.event.pull_request.number }}
  REPO: ${{ github.repository }}
  HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
  # ... shell script references "$PR" / "$REPO" / "$HEAD_SHA"
```

**Not a currently-exploitable bug.** The `on: pull_request:` trigger
means fork PRs get a read-only `GITHUB_TOKEN` — the `gh pr edit
--add-label` / `--remove-label` calls would be rejected from a fork PR
regardless of what `PR`/`REPO`/`HEAD_SHA` contained. And all three
values are typed (numeric PR, repo name validated by GHA, hex SHA) —
none come from user-authored text like titles or bodies.

**Why change it anyway**, per #334's rationale:

- **Trigger-drift risk.** If `pr-labels.yml` ever switches to
`pull_request_target` (to allow label automation on fork PRs), the same
injection class that #333 closed on `pr-labels-ci.yml` reappears — and
now the hardening would already be in place.
- **Parameterization-drift risk.** A future maintainer adding a
contributor-authored string field (label name, PR title fragment, branch
name) to a `run:` block won't be prompted to route via `env:` first
because the file already establishes the inline `${{ ... }}` style as
"fine here."
- **Cascade consistency.** `pr-labels-ci.yml` uses env-routing since
#333; having the sibling workflow use a different style is a readability
cost for anyone auditing the repo.

### 2. `ci.yml` — add workflow-level `permissions: contents: read`
(closes CodeQL #1/#2/#3)

`ci.yml` had no `permissions:` block at workflow or job level, so all
three jobs (`lint`, `typecheck`, `test`) inherited whatever repo-level
default `GITHUB_TOKEN` scope is configured. CodeQL flagged this three
times (one per job).

Fix: declare `permissions: contents: read` at the workflow level. Every
job inherits read-only content access, which is sufficient for lint /
typecheck / pytest / codecov. No job actually needs write access to
anything.

## Audit sweep results

While touching workflow files, checked all six for missing
`permissions:`:

| Workflow | Had `permissions:`? | This PR's action |
|----------|---------------------|------------------|
| `ci.yml` | No (CodeQL flagged 3x) | Added `contents: read` at workflow
level |
| `docker-publish.yml` | Yes, line 23 | No change |
| `docker-smoke.yml` | Yes, line 40 (from #350) | No change |
| `pr-labels-ci.yml` | Yes, line 35 (from #333) | No change |
| `pr-labels.yml` | Yes, line 26 | No change to permissions block;
env-routing changes only |
| `qa-gate.yml` | Yes, line 24 | No change |

`ci.yml` was the last gap. Sweep is complete.

## Scope

- `.github/workflows/ci.yml` — `+7 lines` (permissions block with inline
rationale comment)
- `.github/workflows/pr-labels.yml` — `+10, -11` (three `run:` bodies
lose two shell-assignment lines each; three `env:` blocks gain two-three
entries each; explanatory comment added in the `on-unlabel` case)
- `CHANGELOG.md` — `+4 lines` (new `### Security` subsection under
`[Unreleased]`)

No source, no tests, no migrations.

## References

- Closes [#334](#334)
- Closes CodeQL alerts #1, #2, #3
(`actions/missing-workflow-permissions` on `ci.yml:27/41/53`)
- Cascade source: PR
[#333](#333) (same pattern
for `pr-labels-ci.yml`, which closed #332)
- Related CodeQL alerts not addressed by this PR: #5/#6/#7/#8 (OAuth
clear-text logging in `oauth.py` and `oauth_proxy.py`) — separate audit
PR, coming next. #4 (socket bind in tests) — dismiss via UI.

## QA

### Prerequisites

None. Pure workflow-YAML changes.

### Automated checks

- `lint`, `typecheck`, `test (3.10/3.11/3.12)` — none touch YAML, should
remain green.
- `CodeQL (actions)` — will re-scan `ci.yml` and `pr-labels.yml` on this
PR. Expected outcome: alerts #1/#2/#3 flip to "fixed" on merge; no new
alerts introduced.
- `docker-smoke` — not triggered (no changes under `Dockerfile` /
`pyproject.toml` / `uv.lock` / `.dockerignore`).

### Manual tests

1. - [x] **Both workflow files parse.**
     ```
python3 -c "import yaml; [yaml.safe_load(open(f)) for f in
['.github/workflows/ci.yml', '.github/workflows/pr-labels.yml']];
print('OK')"
     ```
     Expected: `OK`.

2. - [x] **`ci.yml` now has `permissions: contents: read`.**
     ```
     grep -A1 '^permissions:' .github/workflows/ci.yml
     ```
     Expected: `permissions:` header followed by `  contents: read`.

3. - [x] **No contributor-controlled inputs in `pr-labels.yml` `run:`
bodies.**
     ```
awk '/^[[:space:]]+run: \|/,/^[[:space:]]+-
name:|^[[:space:]]{2,6}[a-z-]+:$/' .github/workflows/pr-labels.yml |
grep -nE '\$\{\{ *github\.(event|repository|head_ref)' || echo "(none —
good)"
     ```
Expected: `(none — good)`. All `github.event.*` / `github.repository`
references are now in `env:` blocks (and in job-level `if:`
conditionals, which is safe context).

4. - [x] **All six workflows now have `permissions:`.**
     ```
     for f in .github/workflows/*.yml; do
if ! grep -q '^permissions:\|^ permissions:\|^ permissions:' "$f"; then
         echo "$f: MISSING permissions"
       fi
     done
     echo "(if no 'MISSING' lines above, sweep is complete)"
     ```
     Expected: no `MISSING` lines.

5. - [x] **Label automation still functions on this PR.** When I push,
`pr-labels.yml`'s `on-push` should reset labels to `Awaiting CI` and
strip any stale QA labels. When `Dev Active` is removed, `on-unlabel`
should promote to `Ready for QA` after CI passes. Empirically validated
if the label transitions on this PR itself behave identically to recent
merged PRs (self-test).

6. - [x] **`permissions: contents: read` doesn't break anything.** Lint
/ typecheck / pytest / codecov upload only need read access to
`GITHUB_TOKEN` — none of them push labels, create comments, or mutate
repo state. If any of the existing CI checks start failing on this PR
with "resource not accessible" errors, that's a signal the permissions
block is too tight (unlikely, but the empirical test is: does this PR's
CI go green?).

7. - [x] **Diff review.**
     ```
     git diff --stat origin/main
     ```
Expected: `.github/workflows/ci.yml` (+7),
`.github/workflows/pr-labels.yml` (+10, -11), `CHANGELOG.md` (+4).
Nothing else.

### Acceptance

- ✅ `#334` — symmetric env-routing cascade landed in `pr-labels.yml`
- ✅ CodeQL `#1`, `#2`, `#3` — `ci.yml` now has explicit `permissions:`
- ☐ CodeQL re-scan confirmation — post-merge, the three alerts flip from
Open → Fixed automatically on the next `Analyze (actions)` run against
`main`

Post-merge, also worth a look at CodeQL's /security/code-scanning
dashboard to confirm Open count drops from 8 → 5 (just the four
OAuth-logging + the one test-file socket-bind remaining).

Co-authored-by: cmeans-claude-dev[bot] <3223881+cmeans-claude-dev[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cmeans cmeans mentioned this pull request Apr 22, 2026
cmeans-claude-dev Bot added a commit that referenced this pull request Apr 22, 2026
…tor link (#357)

Closes the high-severity docs gaps from the 2026-04-21
contribution-safety audit (`audit-contribution-safety-2026-04-21` in
awareness). First of three planned PRs from that audit; #B (ruleset
changes) and #C (workflow SHA-pinning) follow separately.

## Summary

Makes contributor expectations discoverable at the moment a contributor
is about to open a PR, not buried in `CLAUDE.md` where only maintainers
look.

- **`.github/PULL_REQUEST_TEMPLATE.md`** — standard template
auto-populated on PR creation.
- **`CONTRIBUTING.md`** — three new sections (engagement-first, AI
disclosure, secrets).
- **`README.md`** — new `## Contributing` section pointing at
`CONTRIBUTING.md` and `SECURITY.md`.
- **`CHANGELOG.md`** — `### Added` / `### Changed` entries under
`[Unreleased]`.

## What the template prompts for

- **Linked issue** (engagement-first) — with a comment-block reminder
that typo/doc fixes are exempt and that issues stay permissive, so bug
reporting isn't gated.
- **Summary, scope** — same as the maintainer-side convention already in
use.
- **AI-assistance disclosure checkboxes** — four categories: none, code
generation, review assist, PR body / commit messages. Mirrors the new
CONTRIBUTING section's stance: disclosure, not prohibition.
- **QA section** — prerequisites + MCP-tool-driven manual tests.
Consistent with `CLAUDE.md`'s requirement that testing go through MCP
tools, not raw HTTP.
- **Checklist** — CHANGELOG entry, README/data-dictionary update if
affected, no-secrets affirmation, local `ruff`/`mypy`/`pytest`, CLA
signature via `cla-assistant` bot.

## Why these three places

The audit's concern #4 was "setting contributor expectations upfront so
people self-filter." Three discovery surfaces:

1. **PR template** — fires at the moment of PR creation,
highest-leverage.
2. **CONTRIBUTING.md** — canonical source of truth, linked from PR
template.
3. **README.md** — for contributors browsing the repo landing page
before deciding to file anything.

Previously only the CLA flow was surfaced in CONTRIBUTING.md, and
neither CONTRIBUTING.md nor SECURITY.md was linked from the README.

## Risks and unknowns

- **PR template friction.** A long template can discourage drive-by
contributions we actually want (small doc fixes). Counter-weighted by:
the typo/doc-fix exemption is in the linked-issue comment block, and
most sections are short prompts (1-3 sentences) rather than formal
fields.
- **AI disclosure phrasing.** The tone is deliberately non-judgmental
("disclosure, not prohibition"). If a contributor reads it as
gatekeeping, the language is too heavy. Easy to soften in a follow-up
based on real feedback.
- **"No secrets" duplicated between CONTRIBUTING and the PR template
checklist.** Intentional redundancy — CONTRIBUTING explains _why_ and
the checklist makes the affirmation explicit. Not actually
contradictory.

## Scope

- `.github/PULL_REQUEST_TEMPLATE.md` — **new file**, 76 lines
- `CONTRIBUTING.md` — `+39, -0` (three new sections inserted at natural
points)
- `README.md` — `+4, -0` (new `## Contributing` block above `##
License`)
- `CHANGELOG.md` — `+11, -0` (three entries under `[Unreleased]`)

No source, no tests, no workflows, no compose changes, no migrations.
Pure docs.

## References

- Audit report: awareness entry `audit-contribution-safety-2026-04-21`
(id `0c79b026`)
- Originating handoff: awareness intention `598956c6` (from claude.ai,
2026-04-19)
- Phase 2 follow-ons: ruleset changes (PR #B), workflow SHA-pinning (PR
#C), manual Settings UI tightening

## QA

### Prerequisites

None. Pure docs change.

### Automated checks

- `lint`, `typecheck`, `test (3.10–3.13)` — docs-only change; expected
green, no behavior exercise.
- `docker-smoke` — **skipped** (no `Dockerfile` / `pyproject.toml` /
`uv.lock` / `.dockerignore` / workflow-file matching its trigger
change).
- `CodeQL (actions)` — no workflow files touched; skipped or no-op.
- `codecov/patch` — docs-only; expected to report 100% (no coverable
lines changed).
- `license/cla` — should stay green (maintainer-authored commit).

### Manual tests

1. - [ ] **PR template renders on a new PR.** Open a throwaway PR on any
branch after this merges and verify the body pre-populates with the
template (GitHub auto-loads `.github/PULL_REQUEST_TEMPLATE.md`). _QA:
not verifiable pre-merge; file placement at
`.github/PULL_REQUEST_TEMPLATE.md` is correct so GitHub will auto-load.
Post-merge verification expected to pass._

2. - [x] **CONTRIBUTING.md section order and wording are correct.** Read
through the rendered markdown:
     - "Before opening a pull request" sits above "Development setup" ✓
- "AI-assistance disclosure" and "Do not commit secrets" sit below "Code
style" ✓
- Engagement-first wording doesn't read as gatekeeping (has the
"typo/doc-fix exempt" + "issues stay permissive" softeners)

3. - [x] **README `## Contributing` section renders between "How it's
built" and "License"**, with both `CONTRIBUTING.md` and `SECURITY.md` as
clickable links.

4. - [x] **No stray file changes.** `git diff --stat origin/main` shows
exactly the four files above (`.github/PULL_REQUEST_TEMPLATE.md`,
`CONTRIBUTING.md`, `README.md`, `CHANGELOG.md`). Nothing else.

### Acceptance

- ✅ CI green (all above automated checks)
- ✅ Template prompts for linked issue (engagement-first)
- ✅ Template prompts for AI-assistance disclosure
- ✅ Template includes no-secrets affirmation and QA section
- ✅ CONTRIBUTING.md has engagement-first, AI-disclosure, secrets
sections
- ✅ README.md links to both CONTRIBUTING.md and SECURITY.md
- ✅ Single-concern; no code / test / workflow changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: cmeans-claude-dev[bot] <3223881+cmeans-claude-dev[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmeans-claude-dev Bot added a commit that referenced this pull request Apr 28, 2026
…#378) (#410)

## Summary

Closes #378. Two stale-label traps in `pr-labels-ci.yml` fixed
symmetrically; both rooted in narrow outer guards that only fired on
`Awaiting CI`, missing the post-`CI Failed` recovery arc and the `Ready
for QA → CI Failed` regression arc.

| Job | Today | Now |
| --- | --- | --- |
| `on-ci-pass` | Promotes only when `Awaiting CI` is present | Promotes
when `Awaiting CI` OR `CI Failed` is present |
| `on-ci-fail` | Adds `CI Failed` only when `Awaiting CI` is present |
Adds `CI Failed` when `Awaiting CI` OR `Ready for QA` is present |

### Bug 1 — `CI Failed → CI pass` silently no-ops (issue #378)

Reproduction trail in #377 (2026-04-22): a lint-failing push moved
labels to `CI Failed`; the fix-up push made CI go green; `on-ci-pass`
fired and ran, but its outer `if echo "$LABELS" | grep -q "^Awaiting
CI$"` was false (only `CI Failed` was present), so it silently no-op'd.
PR sat at `CI Failed` while CI was actually green. Required a manual `gh
pr edit --remove-label "CI Failed" --add-label "Ready for QA"` to
unstick.

### Bug 2 — `Ready for QA → CI re-fail` keeps the green label
(symmetric)

Mirror trap on `on-ci-fail`: a CI re-run on a PR sitting at `Ready for
QA` (e.g., manual re-trigger after a flake, or a workflow change forcing
a re-run) that turns red leaves the PR labelled `Ready for QA` because
the outer `if echo "$LABELS" | grep -q "^Awaiting CI$"` is false. The
status check goes red but the label still says ready — QA might pick it
up assuming CI is green.

### Review-state preservation

Broadening the triggers introduces a new risk: if a `QA Active` / `Ready
for QA Signoff` / `QA Approved` label coexists with a CI label (race, or
manual mistake), the broader trigger could overwrite review-machine
state with `Ready for QA` (on pass) or `CI Failed` (on fail). To prevent
that, both jobs now short-circuit explicitly when any of those three
labels is present:

```bash
for QA_STATE in "QA Active" "Ready for QA Signoff" "QA Approved"; do
  if echo "$LABELS" | grep -q "^$QA_STATE$"; then
    echo "$QA_STATE present — skipping (review in progress)"
    exit 0
  fi
done
```

Rationale: review state advances independently of CI re-runs. A passing
or failing CI re-run on a PR that's already in QA review is visible via
the check itself; the label transition would be redundant on success and
destructive on failure. `Dev Active` short-circuit preserved unchanged.

### Safety

- Trigger remains `workflow_run` — base-branch context, immune to
PR-branch edit attacks (same protection class as the
`pull_request_target` migration in #409).
- No new contributor-controlled inputs. Label list still read via `gh pr
view --json labels` (repo-owned strings, not fork-controlled).
- All grep patterns remain anchored (`^Label$`) so labels like `Awaiting
CI Failed` (if one ever existed) cannot accidentally satisfy a
`^Awaiting CI$` check.
- Existing env-routing of `HEAD_BRANCH` / `RUN_ID` / `PR` / `REPO`
(hardened in #332/#333) is unchanged. Nothing I add interpolates new
contributor-controlled values into shell.

### State-machine trace (full)

Pre-state → CI conclusion → resulting transition (✓ = covered, ✗ =
no-op, * = new):

| Pre-state | CI = success | CI = failure |
| --- | --- | --- |
| `Awaiting CI` | → `Ready for QA` ✓ | → `CI Failed` ✓ |
| `CI Failed` | → `Ready for QA` ✓* | stays `CI Failed` ✓ |
| `Ready for QA` | stays `Ready for QA` ✓ | → `CI Failed` ✓* |
| `Dev Active` | no-op (skip) ✓ | no-op (skip) ✓ |
| `QA Active` | no-op (skip) ✓* | no-op (skip) ✓* |
| `Ready for QA Signoff` | no-op (skip) ✓* | no-op (skip) ✓* |
| `QA Approved` | no-op (skip) ✓* | no-op (skip) ✓* |

The * entries are new in this PR. The `Dev Active` and "no pre-state"
cases were already correct.

## Test plan

Workflow YAML only. No tests to add.

## QA

### Prerequisites
- None — pure workflow YAML change.

### Manual tests

1. - [x] **Workflow YAML parses cleanly.** Confirm the Actions tab on
this PR shows no parse-error annotations on `pr-labels-ci.yml`.

2. - [x] **Diff matches the state-machine trace table above.** Read
`.github/workflows/pr-labels-ci.yml` head-to-toe; for each row of the
trace, confirm the corresponding code path emits the expected transition
(or skip).

3. - [x] **#409 migration live-validation (deferred from #409 QA test
plan #4).** This is the first PR opened against `main` since the
`pr-labels.yml` / `qa-gate.yml` migration to `pull_request_target`.
Confirm:
- `pr-labels.yml` `on-push` fired on opening: `Awaiting CI` was applied
automatically (no manual addition required this time).
- `qa-gate.yml` posted a `QA Gate` status on this PR's head SHA from app
`15368` (GitHub Actions). Visible in the status-check rollup.
- These two observations together confirm #409's migration works
end-to-end on a real PR — not just on the introduction PR's
bootstrap-skipped path.

4. - [ ] **Verification of the bug-fix itself is post-merge.**
`workflow_run` triggers always run from the default branch (per the
`LIMITATION` comment at the top of `pr-labels-ci.yml`), so this PR's
changes do not run on this PR. The natural validation is the next
CI-fail-then-pass PR after this lands — when that happens, the PR should
auto-promote `CI Failed → Ready for QA` without manual intervention.
Reviewer should add a follow-up note here (or in the awareness milestone
for this PR) once that natural validation occurs.

### Out-of-scope follow-ups (not for this PR)

- The `dismiss_stale_reviews_on_push` setting interacts with these
transitions in subtle ways (review approvals get auto-dismissed on push,
then CI re-runs). No change proposed; just flagging for awareness.
- A future enhancement could add a `QA Invalidated` style label for the
case where CI re-fails on a PR in QA review, but doing so requires
designing the QA recovery path. Out of scope for #378.

Co-authored-by: cmeans-claude-dev[bot] <272174644+cmeans-claude-dev[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant