@delegance/claude-autopilot 7.4.2 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -0
- package/dist/src/cli/scaffold/python.d.ts +11 -1
- package/dist/src/cli/scaffold/python.js +20 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,150 @@
|
|
|
2
2
|
|
|
3
3
|
- v5.6 Phase 7 (docs reconciliation) — pending.
|
|
4
4
|
|
|
5
|
+
## 7.5.0 (2026-05-10)
|
|
6
|
+
|
|
7
|
+
**v7.5.0 — route-sensitivity-tiered membership revocation.** Minor
|
|
8
|
+
release. Closes W4 from the v7.4.1 codex strategic review without
|
|
9
|
+
adding infrastructure (Redis / Realtime / KV all rejected — see
|
|
10
|
+
`docs/specs/v7.5.0-route-sensitivity.md` for the trade-off analysis).
|
|
11
|
+
|
|
12
|
+
**Problem.** v7.0 Phase 6 caches `check_membership_status` for 60s
|
|
13
|
+
in an HMAC-signed cookie. Worst-case revocation window = 60s for
|
|
14
|
+
EVERY dashboard request, including admin-class mutations and
|
|
15
|
+
sensitive reads where ≤1-request revocation is the bar.
|
|
16
|
+
|
|
17
|
+
**Solution.** Split the policy by route sensitivity instead of
|
|
18
|
+
tightening globally:
|
|
19
|
+
|
|
20
|
+
| Tier | Revocation window | Behavior |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| LOW (default) | ≤60s | v7.0 cookie cache (no change) |
|
|
23
|
+
| HIGH (mutations + sensitive reads) | ≤1 request | Skip cookie, always RPC |
|
|
24
|
+
|
|
25
|
+
HIGH list (locked in code, codex-reviewed): mutations on any
|
|
26
|
+
`/api/dashboard/*` route + GETs under
|
|
27
|
+
`/api/dashboard/orgs/:id/{audit,cost,cost.csv,sso/**,members/**,billing/**}`
|
|
28
|
+
+ all `/api/dashboard/api-keys/*` (including GET — codex pass-2 W4
|
|
29
|
+
flagged the listing as sensitive).
|
|
30
|
+
|
|
31
|
+
**Defense in depth (codex pass-2 CRITICAL #3).** Middleware regex
|
|
32
|
+
matching is brittle — a new sensitive handler that's not yet in
|
|
33
|
+
`HIGH_SENSITIVITY_PATTERNS` would default to LOW. Mitigation: every
|
|
34
|
+
high-sensitivity route handler now calls
|
|
35
|
+
`assertActiveMembershipForOrg()` at the top as the inner correctness
|
|
36
|
+
gate. Middleware is the outer optimization (skips the cookie cache);
|
|
37
|
+
the handler call is the inner gate (doesn't depend on the regex
|
|
38
|
+
list staying in sync).
|
|
39
|
+
|
|
40
|
+
**Path-vs-active-org assertion (codex pass-2 CRITICAL #2).** For
|
|
41
|
+
high-sensitivity org-scoped routes the middleware also extracts
|
|
42
|
+
`:orgId` from the request path and asserts it matches the
|
|
43
|
+
`cao_active_org` cookie. A user active in Org A reaching an Org B
|
|
44
|
+
URL gets a 403/302 immediately — defense against a sloppy
|
|
45
|
+
downstream handler.
|
|
46
|
+
|
|
47
|
+
**New files:**
|
|
48
|
+
|
|
49
|
+
- `apps/web/lib/middleware/route-sensitivity.ts` (~85 LOC) —
|
|
50
|
+
`HIGH_SENSITIVITY_PATTERNS` + `isHighSensitivityRoute()`.
|
|
51
|
+
- `apps/web/lib/dashboard/assert-active-membership-for-org.ts`
|
|
52
|
+
(~165 LOC) — defense-in-depth helper + `respondToMembershipError()`
|
|
53
|
+
response builder.
|
|
54
|
+
- `apps/web/__tests__/middleware/route-sensitivity.test.ts` (24
|
|
55
|
+
cases — spec list of 7 + non-GET coverage + boundary regex tests
|
|
56
|
+
for codex pass-2 W4/W5).
|
|
57
|
+
- `apps/web/__tests__/middleware/revocation-integration.test.ts`
|
|
58
|
+
(8 cases — disabled-with-fresh-cookie + low-tier cache-still-wins
|
|
59
|
+
+ helper error-mapping integration).
|
|
60
|
+
- `apps/web/__tests__/lib/dashboard/assert-active-membership-for-org.test.ts`
|
|
61
|
+
(16 cases — 4 error codes + happy path + UUID validation +
|
|
62
|
+
respondToMembershipError variants).
|
|
63
|
+
|
|
64
|
+
**Modified files:**
|
|
65
|
+
|
|
66
|
+
- `apps/web/middleware.ts` — sensitivity branch BEFORE cookie verify;
|
|
67
|
+
parseOrgIdFromPath check on high-sensitivity routes; skip cookie
|
|
68
|
+
mint on high-sensitivity success.
|
|
69
|
+
- 10 route handlers wired with `assertActiveMembershipForOrg()` as
|
|
70
|
+
the first authorization step (members CRUD + invite + enable +
|
|
71
|
+
disable; org PATCH; audit; cost JSON + CSV; SSO disconnect +
|
|
72
|
+
required + setup + domains + verify).
|
|
73
|
+
|
|
74
|
+
**Test regressions promoted to v7.5.0 expectations:** 8 existing
|
|
75
|
+
tests that asserted the OLD pre-helper behavior (non-member → 404
|
|
76
|
+
via RPC's `not_admin`, disabled-user → `not_owner`/`not_admin`) now
|
|
77
|
+
assert the NEW uniform helper behavior (non-member → 403
|
|
78
|
+
`no_membership`; disabled-user → 403 `member_disabled`). Status
|
|
79
|
+
codes unchanged for the disabled-user cases; status code for
|
|
80
|
+
non-member shifted from 404 → 403 with a non-enumerating body code.
|
|
81
|
+
Comments inline in each updated test point at this v7.5.0 trade-off.
|
|
82
|
+
|
|
83
|
+
**Test count:** 621 → 669 (+48 web). CLI suite unchanged at 1606
|
|
84
|
+
(no CLI changes).
|
|
85
|
+
|
|
86
|
+
**Codex traceability:**
|
|
87
|
+
|
|
88
|
+
| Finding | Resolution |
|
|
89
|
+
|---|---|
|
|
90
|
+
| Pass-1 C1 (W4 not closed) | Reframed: route-tier closes the security/compliance subset of W4. |
|
|
91
|
+
| Pass-1 C2 (Vercel/ECS confusion) | False positive — explicit deployment-context section in spec. |
|
|
92
|
+
| Pass-1 W1 (Redis ROI) | False positive — autopilot.dev has no Redis. |
|
|
93
|
+
| Pass-1 W3 (route-sensitivity split) | **Adopted as the central design.** |
|
|
94
|
+
| Pass-2 CRITICAL #2 (path-vs-activeOrg) | `parseOrgIdFromPath` + middleware assertion. |
|
|
95
|
+
| Pass-2 CRITICAL #3 (regex bypass risk) | `assertActiveMembershipForOrg()` defense-in-depth helper called at top of every HIGH handler. |
|
|
96
|
+
| Pass-2 W4 (api-keys GET sensitivity) | `/api/dashboard/api-keys/*` (including GET) added to HIGH list. |
|
|
97
|
+
| Pass-2 W5 (boundary pattern correctness) | Anchored regex with explicit `/`/`$` terminator; tests cover `/costume`, `/auditor`, trailing-slash, nested-path. |
|
|
98
|
+
|
|
99
|
+
**Out of scope.** Vercel KV / Realtime websocket (deferred), JWT-
|
|
100
|
+
side revocation (v7.1 already collapses ingest to ≤1 request),
|
|
101
|
+
configurable sensitivity classification (locked in code on purpose),
|
|
102
|
+
wrapper `withActiveMembershipRequired()` decorator (deferred to
|
|
103
|
+
v7.6+ refactor; the explicit call is sufficient for v7.5.0).
|
|
104
|
+
|
|
105
|
+
## 7.4.3 (2026-05-11)
|
|
106
|
+
|
|
107
|
+
**v7.4.3 — FastAPI scaffold respects spec-derived package name.**
|
|
108
|
+
Patch release. Real-package end-to-end test on v7.4.2 surfaced
|
|
109
|
+
this regression: the v7.4.0 Python/FastAPI scaffolder always used
|
|
110
|
+
`basename(cwd)` for the package directory and ignored spec-listed
|
|
111
|
+
`src/<pkg>/main.py` paths. Result: scaffolding a spec that listed
|
|
112
|
+
`src/fastapi_test/main.py` from a directory called `v742-fastapi`
|
|
113
|
+
produced TWO competing trees:
|
|
114
|
+
|
|
115
|
+
* `src/v742_fastapi/` — auto-generated FastAPI app (correct
|
|
116
|
+
content, wrong location)
|
|
117
|
+
* `src/fastapi_test/main.py` — empty placeholder from the spec's
|
|
118
|
+
bullet (right location, no content)
|
|
119
|
+
|
|
120
|
+
`pyproject.toml`'s `[project.scripts]` pointed at the
|
|
121
|
+
auto-generated tree (`v742_fastapi.main:run`) — the spec's intent
|
|
122
|
+
was clearly the named package.
|
|
123
|
+
|
|
124
|
+
**Fix:** new `packageNameFromSpec(parsed)` extracts the package
|
|
125
|
+
name from the first `src/<pkg>/<*>.py` entry in the spec's
|
|
126
|
+
`## Files` section. Falls back to the cwd-derived default only
|
|
127
|
+
when the spec doesn't list any `src/<pkg>/` path.
|
|
128
|
+
|
|
129
|
+
8 new tests in `tests/scaffold-python.test.ts` cover:
|
|
130
|
+
* extraction from `src/<pkg>/main.py` (the conventional case)
|
|
131
|
+
* extraction from `src/<pkg>/<other>.py`
|
|
132
|
+
* null when no `src/<pkg>/<*>.py` listed
|
|
133
|
+
* null for non-`src/` paths
|
|
134
|
+
* first-match-wins on multiple `src/<pkg>/` entries
|
|
135
|
+
* rejects invalid Python identifier characters in the path
|
|
136
|
+
* the exact regression case (`src/fastapi_test/main.py` from
|
|
137
|
+
cwd `v742-fastapi`)
|
|
138
|
+
* fallback to cwd basename when spec has no `src/<pkg>/`
|
|
139
|
+
|
|
140
|
+
Plus 2 end-to-end tests verifying:
|
|
141
|
+
* scaffold from `cwd=v742-real` + `src/intentional_pkg/main.py`
|
|
142
|
+
spec → SINGLE `src/intentional_pkg/` directory (no competing
|
|
143
|
+
tree); `pyproject.toml` consistent throughout
|
|
144
|
+
* scaffold from `cwd=myapp` + spec without `src/<pkg>/` → still
|
|
145
|
+
uses `src/myapp/` (preserves v7.4.0 default behavior)
|
|
146
|
+
|
|
147
|
+
1597 → 1606 CLI tests (+9). tsc clean. build clean.
|
|
148
|
+
|
|
5
149
|
## 7.4.2 (2026-05-11)
|
|
6
150
|
|
|
7
151
|
**v7.4.2 — risk-tiered codex pass policy in autopilot skill.**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScaffoldResult, ScaffoldRunContext } from './types.ts';
|
|
1
|
+
import type { ParsedFiles, ScaffoldResult, ScaffoldRunContext } from './types.ts';
|
|
2
2
|
/**
|
|
3
3
|
* PEP 503 distribution-name normalization, restricted to what we need
|
|
4
4
|
* here. Lowercase, runs of `[._-]+` collapse to a single `-`, leading +
|
|
@@ -65,6 +65,16 @@ export declare function buildFastapiTest(packageName: string): string;
|
|
|
65
65
|
* not listed in `## Files` — without them the generated pyproject.toml
|
|
66
66
|
* is invalid (missing package dir) or has dead config (no tests).
|
|
67
67
|
*/
|
|
68
|
+
/**
|
|
69
|
+
* Extract the Python package name from a spec's `## Files` paths if the
|
|
70
|
+
* spec lists a `src/<pkg>/<*>.py` entry. Returns null if no spec-derived
|
|
71
|
+
* package name is present — caller falls back to the cwd-derived default.
|
|
72
|
+
*
|
|
73
|
+
* v7.4.3 hotfix — the v7.4.0 scaffolder always used basename(cwd) and
|
|
74
|
+
* ignored spec-listed src/<pkg>/ paths, producing two competing trees
|
|
75
|
+
* (one auto-generated, one empty placeholder from the spec).
|
|
76
|
+
*/
|
|
77
|
+
export declare function packageNameFromSpec(parsed: ParsedFiles): string | null;
|
|
68
78
|
export declare function scaffoldPython(ctx: ScaffoldRunContext, opts: {
|
|
69
79
|
isFastapi: boolean;
|
|
70
80
|
}): Promise<ScaffoldResult>;
|
|
@@ -206,11 +206,30 @@ pytest
|
|
|
206
206
|
* not listed in `## Files` — without them the generated pyproject.toml
|
|
207
207
|
* is invalid (missing package dir) or has dead config (no tests).
|
|
208
208
|
*/
|
|
209
|
+
/**
|
|
210
|
+
* Extract the Python package name from a spec's `## Files` paths if the
|
|
211
|
+
* spec lists a `src/<pkg>/<*>.py` entry. Returns null if no spec-derived
|
|
212
|
+
* package name is present — caller falls back to the cwd-derived default.
|
|
213
|
+
*
|
|
214
|
+
* v7.4.3 hotfix — the v7.4.0 scaffolder always used basename(cwd) and
|
|
215
|
+
* ignored spec-listed src/<pkg>/ paths, producing two competing trees
|
|
216
|
+
* (one auto-generated, one empty placeholder from the spec).
|
|
217
|
+
*/
|
|
218
|
+
export function packageNameFromSpec(parsed) {
|
|
219
|
+
for (const p of parsed.paths) {
|
|
220
|
+
const m = /^src\/([a-zA-Z_][a-zA-Z0-9_]*)\/[^/]+\.py$/.exec(p);
|
|
221
|
+
if (m && m[1])
|
|
222
|
+
return m[1];
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
209
226
|
export async function scaffoldPython(ctx, opts) {
|
|
210
227
|
const { cwd, parsed, dryRun } = ctx;
|
|
211
228
|
const { isFastapi } = opts;
|
|
229
|
+
// v7.4.3: prefer spec-derived package name; fall back to cwd basename.
|
|
230
|
+
const specPackage = packageNameFromSpec(parsed);
|
|
212
231
|
const distributionName = normalizeDistributionName(path.basename(cwd));
|
|
213
|
-
const packageName = packageNameFromDistribution(distributionName);
|
|
232
|
+
const packageName = specPackage ?? packageNameFromDistribution(distributionName);
|
|
214
233
|
const filesCreated = [];
|
|
215
234
|
const filesSkippedExisting = [];
|
|
216
235
|
const dirsCreated = [];
|