Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 2 additions & 19 deletions .github/workflows/pr.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,25 +24,8 @@ jobs:
with:
fetch-depth: 0

- name: Validate changed SDLC artifacts
env:
EVENT_NAME: ${{ github.event_name }}
BASE_REF: ${{ github.base_ref }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python3 scripts/sdlc.py validate --base "origin/$BASE_REF"
elif [ "$EVENT_NAME" = "push" ]; then
if [ -z "$BEFORE_SHA" ] || [[ "$BEFORE_SHA" =~ ^0+$ ]]; then
echo "Push event has no usable previous SHA; refusing schema-only validation"
exit 1
fi
git fetch --no-tags origin "$BEFORE_SHA"
git cat-file -e "$BEFORE_SHA^{commit}"
python3 scripts/sdlc.py validate --push-base "$BEFORE_SHA"
else
python3 scripts/sdlc.py validate
fi
- name: Validate SDLC artifact gates
run: bash ./scripts/sdlc-checks.sh

- name: Run linked repository checks
run: bash ./scripts/ci-basic-checks.sh
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,9 +49,9 @@ Utter is a macOS menu bar voice input app built with Swift 6 / SwiftUI / AppKit.

- Read `docs/sdlc/README.md` before non-trivial implementation, automation, test, UI, dependency, permission, or release changes.
- Create or update `docs/sdlc/changes/<yyyy-mm-dd-slug>/`. Low risk requires intent, plan, and verification; medium/high risk also requires a spec.
- Keep `state.json` honest. An agent may advance work to `verified` with current evidence, but may not record its own work as human approval.
- **Every stage requires explicit human approval before the next one starts.** Set the artifact's `Status: pending approval` and stop for the user's decision; never mark a stage `approved` on the user's behalf. `approved` requires `Approved-by` and `Approved-date`. Artifacts without a Status header are legacy merged bundles.
- Begin implementation only after intent has observable acceptance criteria and medium/high-risk design choices have been reviewed.
- Always run `python3 scripts/sdlc.py validate --worktree`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- Always run `bash scripts/sdlc-checks.sh`, `bash scripts/ci-basic-checks.sh`, and `swift test`. Add release-style build, real-window visual QA, permission/privacy paths, or clean-machine checks in proportion to risk.
- High-risk changes require an independent verifier, explicit rollback, PR approval, and protected production approval.
- A production incident must link a corrective intent and add a regression test, deterministic guardrail, eval case, or explicit reason automation is impossible.
- Never fall back to ad-hoc signing when configured signing fails. A self-signed release must use the configured identity, pass the same artifact/checksum checks, and be labeled as not Apple-notarized.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ Material changes follow the artifact-driven workflow in
[`docs/sdlc/README.md`](docs/sdlc/README.md). Before opening a pull request, run:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -96,7 +96,7 @@ open Package.swift
提交 Pull Request 前请运行:

```bash
python3 scripts/sdlc.py validate --worktree
bash scripts/sdlc-checks.sh
bash scripts/ci-basic-checks.sh
swift test
```
Expand Down
33 changes: 17 additions & 16 deletions docs/sdlc/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,27 +34,28 @@ risk, and a one-line permission or release change can be high risk.
Non-trivial work lives at `docs/sdlc/changes/<yyyy-mm-dd-slug>/`:

```text
state.json Machine-readable identity, risk, status, governed paths, and artifact paths
intent.md Problem, outcome, scope, constraints, and acceptance criteria
spec.md Design and failure analysis; required for medium/high risk
plan.md Executable work items and verification plan
verification.md Commands, results, visual/runtime evidence, and residual risk
```

Copy starting points from `docs/sdlc/templates/`. Status transitions are:
`intent -> designed -> planned -> implementing -> verified -> released -> closed`.
Only change the status when the corresponding artifact exists and its evidence
is current. An agent may record evidence, but may not represent its own output as
human approval.
Copy starting points from `docs/sdlc/templates/`. Stage state lives in each
artifact's header fields — the single source of status for the bundle:

`python3 scripts/sdlc.py validate --worktree` validates local work. Pull-request
CI compares the branch to its base and requires a changed verified bundle when
governed paths change. Trivial documentation-only changes stay on the fast path.
- `Status`: one of `draft | pending approval | approved | rejected | blocked`;
`approved` must be paired with `Approved-by` and `Approved-date`.
- **Strict per-stage approval.** A stage may only become `approved` after the
previous stage is `approved` (intent -> spec -> plan -> verification ->
release). Set `Status: pending approval` and stop for the human decision;
an agent may record evidence, but may never represent its own output as
human approval. Rejections and blocks stay in the artifact with a reason.
- Bundles merged before this gate existed carry no `Status` header and are
treated as historical archives by the checks.

The validator also enforces a minimum risk for deterministic control surfaces:
entitlements, workflows, signing/build/release verification, the SDLC validator,
and its CI guard are high risk; dependencies, agent context, issue/review policy,
and other automation are at least medium risk.
`bash scripts/sdlc-checks.sh` validates the gate locally. Pull-request CI runs
the same script in the `SDLC Gate` job. Trivial documentation-only changes
stay on the fast path.

## Definition of ready

Expand DownExpand Up@@ -83,9 +84,9 @@ A change is ready for PR approval when:

Repository instructions and this document are guidance. Enforcement lives in:

- `scripts/sdlc.py`: artifact schema and governed-change check;
- `scripts/ci-basic-checks.sh`: linked resources, localization, identifiers, and
repository invariants;
- `scripts/sdlc-checks.sh`: stage order and approval-field gate;
- `scripts/ci-basic-checks.sh`: SDLC gate, linked resources, localization,
identifiers, and repository invariants;
- `.github/workflows/pr.yml`: artifact validation, unit tests, and release-style
app build, summarized by the stable `SDLC Gate` job;
- `.github/workflows/release.yml`: main-ancestry, tests, explicit signing-mode
Expand Down
31 changes: 0 additions & 31 deletions docs/sdlc/changes/2026-08-25-ai-native-sdlc/state.json

This file was deleted.

28 changes: 0 additions & 28 deletions docs/sdlc/changes/2026-08-26-dmg-installer-polish/state.json

This file was deleted.

25 changes: 0 additions & 25 deletions docs/sdlc/changes/2026-08-30-dev-app-icon-packaging/state.json

This file was deleted.

27 changes: 0 additions & 27 deletions docs/sdlc/changes/2026-08-30-history-mode-switch/state.json

This file was deleted.

33 changes: 0 additions & 33 deletions docs/sdlc/changes/2026-08-30-model-type-selection/state.json

This file was deleted.

35 changes: 0 additions & 35 deletions docs/sdlc/changes/2026-08-30-settings-window-layout/state.json

This file was deleted.

40 changes: 0 additions & 40 deletions docs/sdlc/changes/2026-08-30-system-settings-groups/state.json

This file was deleted.

25 changes: 25 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/intent.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
# Intent: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31

## Problem

The SDLC validator stack (`scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, per-bundle `state.json`) is Python-based and duplicates the strict per-stage approval model the maintainer wants enforced. Stage approval should live in the artifacts themselves, checked by shell only.

## Outcome

- Stage order and approval fields are enforced by `scripts/sdlc-checks.sh` (pure bash) in CI and locally.
- No Python in the SDLC toolchain.

## Constraints

- Historical bundles merged before the gate keep their recorded evidence as-is (including past `sdlc.py` runs) and are grandfathered by the checks.
- The governed-changed-paths feature of `sdlc.py` is not reimplemented in shell; residual risk is recorded in verification.

## Acceptance criteria

- `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py` and all `state.json` files are removed.
- `bash scripts/sdlc-checks.sh` passes and rejects later-stage approvals before earlier ones.
- No `sdlc.py` references remain in AGENTS.md, READMEs, docs/sdlc prose, CI workflows, or scripts (historical bundle evidence excepted).
23 changes: 23 additions & 0 deletions docs/sdlc/changes/2026-08-31-shell-sdlc-gate/plan.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
# Plan: Replace the Python SDLC validator with a strict shell gate

**Status:** approved
**Approved-by:** chenli
**Approved-date:** 2026-08-31
**Upstream:** [spec.md](spec.md)

## Work items

- [x] Add `Status`/`Approved-by`/`Approved-date`/`Upstream` headers to templates
- [x] Write `scripts/sdlc-checks.sh`; wire into `scripts/ci-basic-checks.sh` and `.github/workflows/pr.yml`
- [x] Remove `scripts/sdlc.py`, `scripts/sdlc_policy.py`, `scripts/tests/test_sdlc.py`, all `state.json`
- [x] Update AGENTS.md, `docs/sdlc/README.md`, README.md, README_zh.md references

## Verification plan

- [x] `bash scripts/sdlc-checks.sh`
- [x] `bash scripts/ci-basic-checks.sh`
- [ ] `swift test` (see verification for local environment blocker)

## Human gates

Merge approval — granted by the maintainer's instruction to open and merge the PR.
Loading
Loading