Skip to content

Commit 2bbd2c5

Browse files
authored
Merge main into feat/sr-187-ban-datetime-utcnow (auto-rebase to clear behind status)
2 parents b3731c4 + 146128b commit 2bbd2c5

8 files changed

Lines changed: 802 additions & 260 deletions

File tree

.github/dependabot.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Dependabot configuration — keep CI surface and runtime deps current.
2+
#
3+
# Why this file exists:
4+
# path_guard.py pins behaviour to specific Action versions
5+
# (actions/checkout@v5, actions/setup-python@v6). Without dependabot
6+
# those pins quietly rot and we either run on EOL'd runners or stop
7+
# getting security patches. Two-line yaml is cheap insurance.
8+
#
9+
# Why weekly, not daily:
10+
# The repo has a single maintainer; daily PR noise drowns signal.
11+
# Weekly keeps the queue manageable while still surfacing CVEs within
12+
# ~7 days.
13+
#
14+
# Scope:
15+
# - github-actions: every workflow under .github/workflows/
16+
# - pip: pyproject.toml + survey-cli/requirements.txt
17+
#
18+
# Doctrine: see survey-cli/AGENTS.md § PATH DOCTRINE (SR-159) for why
19+
# anything we add to .github/ must be justified inline.
20+
21+
version: 2
22+
23+
updates:
24+
# GitHub Actions used in .github/workflows/*
25+
- package-ecosystem: "github-actions"
26+
directory: "/"
27+
schedule:
28+
interval: "weekly"
29+
day: "monday"
30+
open-pull-requests-limit: 5
31+
commit-message:
32+
prefix: "ci"
33+
include: "scope"
34+
labels:
35+
- "dependencies"
36+
- "ci"
37+
# Group all action bumps into a single PR per week to avoid noise.
38+
groups:
39+
github-actions:
40+
patterns:
41+
- "*"
42+
43+
# Python deps at repo root (pyproject.toml — the stealth-sync package).
44+
- package-ecosystem: "pip"
45+
directory: "/"
46+
schedule:
47+
interval: "weekly"
48+
day: "monday"
49+
open-pull-requests-limit: 5
50+
commit-message:
51+
prefix: "deps"
52+
include: "scope"
53+
labels:
54+
- "dependencies"
55+
- "python"
56+
groups:
57+
python-minor-patch:
58+
update-types:
59+
- "minor"
60+
- "patch"
61+
62+
# Python deps inside survey-cli/ (the canonical survey engine).
63+
- package-ecosystem: "pip"
64+
directory: "/survey-cli"
65+
schedule:
66+
interval: "weekly"
67+
day: "monday"
68+
open-pull-requests-limit: 5
69+
commit-message:
70+
prefix: "deps(survey-cli)"
71+
include: "scope"
72+
labels:
73+
- "dependencies"
74+
- "python"
75+
- "survey-cli"
76+
groups:
77+
survey-cli-minor-patch:
78+
update-types:
79+
- "minor"
80+
- "patch"

.github/workflows/path-guard.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# PATH-GUARD WORKFLOW — SR-159
2+
#
3+
# Enforces the repo's path doctrine on every PR. The doctrine itself lives
4+
# in two places that MUST stay in sync:
5+
#
6+
# 1. survey-cli/AGENTS.md § PATH DOCTRINE (human-readable spec)
7+
# 2. scripts/path_guard.py (machine-readable manifest + checker)
8+
#
9+
# This workflow is a thin shim around `scripts/path_guard.py --diff`.
10+
# Keep it small. All policy logic belongs in the Python script so the
11+
# pre-commit hook and CI run the exact same code path.
12+
#
13+
# History:
14+
# - 2026-05-13 SR-159: created. Reason: SR-154 (100-file PR landed in a
15+
# non-existent dir) + SR-162 (daemon.py vs daemon/ shadow killed CI for
16+
# a week). Both could have been blocked by a 5-line CI check.
17+
#
18+
# Style notes (mirroring .github/workflows/ci.yml §13.8.4):
19+
# - Action versions are Brain-property. Bumped explicitly, never @latest.
20+
# - actions/checkout@v5, setup-python@v6 to stay on Node-24 runtime.
21+
# - fetch-depth: 0 is REQUIRED because the guard runs `git diff
22+
# $base...HEAD` and needs the merge-base reachable.
23+
24+
name: path-guard
25+
26+
on:
27+
pull_request:
28+
# No `branches:` filter — see ci.yml: PRs against integration branches
29+
# must also be gated, otherwise drift sneaks in via long-running feat
30+
# branches (SR-50..SR-55 precedent in PR #54).
31+
push:
32+
branches:
33+
- main
34+
- master
35+
36+
permissions:
37+
contents: read
38+
pull-requests: write # for the failure comment
39+
40+
jobs:
41+
path-guard:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- uses: actions/checkout@v5
45+
with:
46+
# Needed for `git diff $base...HEAD` to resolve the merge-base.
47+
fetch-depth: 0
48+
49+
- name: Set up Python
50+
uses: actions/setup-python@v6
51+
with:
52+
python-version: "3.13"
53+
54+
- name: Run path-guard (diff mode)
55+
id: guard
56+
# The script uses $GITHUB_BASE_REF on pull_request events and falls
57+
# back to origin/main on push events. No `pip install` — pure stdlib.
58+
#
59+
# `set -o pipefail` is REQUIRED — without it the exit code of `tee`
60+
# (always 0) masks a non-zero exit from path_guard.py and the guard
61+
# silently passes on real violations. This is not theoretical: the
62+
# first iteration of this workflow shipped without pipefail, the
63+
# demo probe PR (#174) showed the violations in the log but the
64+
# check was marked green. Do NOT remove this line.
65+
shell: bash
66+
run: |
67+
set -o pipefail
68+
python scripts/path_guard.py --diff | tee path_guard.out
69+
env:
70+
# Explicit even though Actions sets it automatically — makes the
71+
# script's behaviour obvious to anyone reading the workflow.
72+
GITHUB_BASE_REF: ${{ github.base_ref }}
73+
74+
- name: Comment on PR when guard fails
75+
if: failure() && github.event_name == 'pull_request'
76+
uses: actions/github-script@v7
77+
with:
78+
script: |
79+
const fs = require('fs');
80+
const out = fs.existsSync('path_guard.out')
81+
? fs.readFileSync('path_guard.out', 'utf8')
82+
: '(no output captured)';
83+
const body = [
84+
'### path-guard failed (SR-159)',
85+
'',
86+
'This PR adds files outside the canonical repo layout. The doctrine',
87+
'lives in [`survey-cli/AGENTS.md` § PATH DOCTRINE](https://github.com/' +
88+
context.repo.owner + '/' + context.repo.repo +
89+
'/blob/main/survey-cli/AGENTS.md#path-doctrine-sr-159).',
90+
'',
91+
'<details><summary>Guard output</summary>',
92+
'',
93+
'```',
94+
out,
95+
'```',
96+
'',
97+
'</details>',
98+
'',
99+
'If the verdict is wrong (e.g. a genuinely new canonical area),',
100+
'**STOP** and comment on the issue with rationale — do not work',
101+
'around the guard.',
102+
].join('\n');
103+
await github.rest.issues.createComment({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
issue_number: context.issue.number,
107+
body,
108+
});

.pre-commit-config.yaml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
# Pre-Commit Configuration (SR-73, 2026-05-11)
2-
# Hooks enforce AGENTS.md Brain-Rules before commits
1+
# Pre-Commit Configuration
2+
#
3+
# Hooks enforce AGENTS.md Brain-Rules before commits. The CI workflows
4+
# in .github/workflows/ run the same checks in cloud — local hooks are
5+
# the fast-feedback mirror, not a substitute.
6+
#
7+
# History:
8+
# - SR-73 (2026-05-11): initial — submodule + black + pylint + standard hooks.
9+
# - SR-159 (2026-05-13): added `path-guard` local hook. Mirrors
10+
# .github/workflows/path-guard.yml exactly. Doctrine lives in
11+
# survey-cli/AGENTS.md § PATH DOCTRINE and scripts/path_guard.py.
312

413
repos:
514
# Submodule Validation (SR-73 §11.9)
@@ -13,6 +22,17 @@ repos:
1322
stages: [commit]
1423
always_run: true
1524

25+
# Path Doctrine (SR-159) — local mirror of .github/workflows/path-guard.yml
26+
- repo: local
27+
hooks:
28+
- id: path-guard
29+
name: "Path-Guard: repo layout doctrine (SR-159)"
30+
entry: python scripts/path_guard.py --diff
31+
language: system
32+
pass_filenames: false
33+
stages: [commit]
34+
always_run: true
35+
1636
# Standard pre-commit hooks
1737
- repo: https://github.com/pre-commit/pre-commit-hooks
1838
rev: v4.5.0

0 commit comments

Comments
 (0)