Skip to content

openai-admin release#1

Merged
jeffreyaven merged 15 commits into
mainfrom
feature/provider-dev
Jul 15, 2026
Merged

openai-admin release#1
jeffreyaven merged 15 commits into
mainfrom
feature/provider-dev

Conversation

@jeffreyaven

Copy link
Copy Markdown
Contributor

No description provided.

jeffreyaven and others added 14 commits July 15, 2026 08:39
Directory skeleton, npm scripts through node wrappers, latest
@stackql/provider-utils (0.7.6), fetch/clean scripts adapted from the
sibling openai build with the inverse (keep-admin-surface) filter,
README with the applicability note up front.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins openai/openai-openapi at a3276900 (2026-07-14), the same ref and
sha256 the sibling openai build pins, recorded independently in
spec_pin.json. clean_specs.mjs keeps the /organization subtree (46
paths / 71 ops) plus the six /projects/{project_id} role/group paths
the sibling's filter dispatches here (10 ops); 162 paths / 242 ops ->
52 paths / 81 ops, every disposition reason-coded in filter_report.csv
and the partition reconciling exactly against the sibling's report.
swagger-parser validates the filtered artifact clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Blocked on OPENAI_ADMIN_KEY (not present on this machine - the
nvidia/vsphere blocked-on-key pattern): executes unchanged once keys
land. Probes: admin-key accept + standard-key reject on list-projects,
the bucketed idiom with group_by form-explode encoding and next_page
traversal, the derived-cursor directory traversal, audit-log bracketed
filter pass-through, and the opt-in --lifecycle governance run
(stackql-smoke-<stamp> project -> service account -> archive).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
81 operations, all mapped, none skipped: 37 select, 15 insert, 8
update, 16 delete, 5 exec across 29 resources / 10 services. Idiom
classified per resource: 9 bucketed lists (8 usage capabilities +
costs, page/next_page, group_by on all 9), 20 directory lists (list
envelope, derived cursor). Update-POST bodies flagged; two rename-only
exceptions (groups, certificates) annotated. clean_specs re-tags
/organization/costs 'Usage' -> 'Costs' for the split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CLAUDE.md candidates adjusted on subtree evidence: groups and roles
added (the RBAC surface exists in the pin); certificates confirmed
exposed (org and project scopes); usage capabilities enumerated as 8
(audio splits into speeches and transcriptions). The 15 fine-grained
spec tags consolidate through service_names.json; split emits 10
service specs carrying all 81 operations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
all_services.csv populated from the endpoint inventory (joined by
operationId - one source of truth). All validations pass: coverage both
directions, unique method keys, unique path-param signatures per SQL
verb, object keys on every list. Pilots green: usage+costs (bucketed,
$.data, group_by), projects (governance lifecycle mapped; live run
owed on key), audit_logs (derived cursor, filter pass-through).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… draft

Evidence-backed answers to the phase 1 open questions: 8 usage
capabilities enumerated from the pin; group_by wire name is plain
'group_by' (no bracket suffix - OpenAI delta vs anthropic); bucket
projection decided as row-per-bucket with results as JSON (result
items carry no time fields; objectKey cannot flatten parent fields -
anthropic_admin evidence transferred); update-POSTs partial by
construction with two rename-only exceptions; zero-activity and
skip-with-notice postures recorded; the four-way token-spend union
drafted against actual resource names as the docs acceptance artifact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onfig

Reframes the build on the sibling openai repo as the archetype (k8s
references removed throughout).

Pagination corrected on generate-stage evidence: the surface carries
THREE idioms, not two. The RBAC family (groups, roles, role
assignments - 9 lists) returns {object, data, has_more, next} with an
explicit next cursor that nulls out on the last page - no first_id/
last_id, and no overshoot request. build_inventory now derives the
idiom structurally from the response envelope (next_page -> bucketed,
last_id -> cursor-derived, next -> cursor-next) and fails on a list
with no cursor field.

projects and users mix idioms, so a single --service-config cannot
cover them: post_process.mjs stamps the dominant idiom per service and
overrides the 4 deviating list methods at method level, per any-sdk's
operation -> resource -> service resolution chain, then validates all
29 lists resolve to their inventory idiom.

pre_normalize expands the object-typed effective_at query param into
its bracketed wire params (effective_at[gt] etc); org/project headers
are deliberately not injected (the admin key IS the org scope).
normalize: 175 allOf flattened, 172 anyOf nullable resolved, 4 opaque
objects lowered, zero residual variants.

Provider resolves: 10 services, 29 resources, 81 methods; meta-routes
suite passes with 0 non-selectable resources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Docgen over the generated provider: 10 service pages, 29 resource
pages, landing page. Site scaffold follows the sibling openai build
(shared docusaurus-config vendored at build time, thin wrappers,
provider identity in website/provider.js); CNAME and the slug-derived
url agree on openai-admin-provider.stackql.io. yarn build passes.

The landing content leads with the applicability note (organizations,
admin key class, disjoint from standard keys, sibling openai pointer),
then token spend and cost as rows (bucket + json_each idiom), the
single-dimension group_by posture, governance and admin-key hygiene,
and the audit log with double-quoted bracketed params.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tests/smoke_test.py (pystackql): resolution, key-class evidence (the
standard key rejected through the provider via custom_auth - the
disjointness proven provider-routed, not by a side-channel call),
directory and project-child reads, the flagship (usage buckets,
group_by, USD costs, all 8 capabilities), and cursor traversal
assertions (limit=1 must return the unbounded row count - proves the
cursor configs walk, for both entity idioms). Skip-with-notice on
401/403/404; zero-activity orgs pass with 0 buckets.

The governance lifecycle is opt-in (--with-lifecycle): the API has no
project delete, so archive is terminal and every run would leave a
permanently archived project.

group_by encoding settled on the wire rather than inferred, captured
with --http.log.enabled and a dummy key: a scalar emits
group_by=project_id; JSON-array and comma-joined values arrive as
single literal strings (no fan-out), so one dimension per query is the
honest posture. The pre_normalize effective_at expansion is confirmed
the same way (effective_at%5Bgt%5D=...).

Pipeline verified reproducible from clean: same counts throughout and a
byte-identical generated tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three defects, all from examples written but never run - the mistake the
dummy-key routing checks existed to prevent.

1. strftime()/date() in WHERE does not bind. StackQL evaluates no SQL
   functions when binding request params: start_time was silently
   dropped, emitting 'GET /organization/costs?' and a rejection. Only
   literals bind; examples now use literal epochs and say so.

2. date()/datetime() do not evaluate over a COLUMN - they fold constant
   args only, so date(1781481600,'unixepoch') works (per the
   maintainer's google example) but date(c.start_time,'unixepoch')
   returns 0, and can null out neighbouring columns in the same SELECT.
   strftime() handles columns correctly. All examples use strftime now.

3. limit defaults to 7 buckets, so any window over a week paginates -
   and bucketed auto-pagination is broken upstream (NOTES 12b): any-sdk
   re-escapes the base64 page token's '=' padding to %3D via Go's
   url.Values.Encode(); httpx (OpenAI's own client) sends '=' raw, and
   their cursor parser rejects the escaped form -> 400 'The page token
   is invalid'. Both encodings are legal per RFC 3986. Ruled out
   config, param drift and token corruption first, against a new mock.
   Workaround everywhere: set limit to cover the window.

Adds tests/integration/mock_admin_server.mjs (all three idioms, request
recording, page-token contract, bearer enforcement) - built to diagnose
this. The smoke suite now sets limit on every bucketed query (it would
have hit the same 400) and asserts the defect explicitly, flipping to
PASS if it is ever fixed upstream.

Both flagship examples now verified end to end: extracted verbatim from
the generated docs page and run, returning real dates, project ids and
amounts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ects

Adds work-orders/ with two fileable specs (no gh CLI on this machine):

WO-001 any-sdk - pagination config switch to send the request token
unencoded. SetNextPage re-renders the query with Go's
url.Values.Encode(), escaping a token's reserved characters; servers
whose cursor parser does not percent-decode reject the result.
Proposes requestToken.encoding: none|url (default url, backwards
compatible) with an implementation sketch against standardTokenSemantic
and the QueryParam branch. Carries the live evidence, the cross-client
comparison, and what was ruled out first.

WO-002 stackql - date()/datetime()/typeof() return 0 for a bare column
argument. Narrowed with isolated probes: literal args work, an
expression-wrapped column works (datetime(start_time + 0, ...)), a
column in arg 2 works (strftime), and abs/upper/length/max/coalesce/
json_extract all handle a bare column in arg 1 - so it is neither the
modifier nor argument position in general. Records the contagion (one
bad call nulls neighbouring computed columns) and notes the functions
are stock SQLite builtins via the vendored driver, so what reaches
SQLite is the suspect rather than the implementation.

Docs now read as capability statements: the usage/cost preamble states
the four conventions (literal start_time, size limit to the window,
one group_by dimension, strftime for epochs) with no commentary on
engine gaps. Removes the pagination warning admonition; the only
admonition left is the applicability note. Engine detail stays in
NOTES.md and the work orders, where it belongs.

All three published examples re-extracted from the generated page and
run: real dates, project ids and amounts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e suite

The live --with-lifecycle run exposed two real problems behind the
reported failure.

1. projects.archive never dispatched. stackql v0.10.542 SIGSEGVs while
   preparing an EXEC whose resolved response schema carries no
   array-typed property (drm.GenerateSelectDML -> wrappedSchema.GetType
   on a nil inner schema); archive-project returns a bare Project
   object. The SQL layer reported no error, so the smoke test recorded
   'archive project - PASS' while nothing was sent - three smoke
   projects had been 'archived' across runs and were all still active.
   This is the anthropic repo's documented v0.10.542 finding, reused
   not re-derived: post_process.mjs now stamps response.schema_override
   to a synthetic envelope carrying an array property, on exec-only
   methods whose response lacks one. Exactly 1 method qualifies
   (projects.archive); the 4 certificate execs return list envelopes
   and are untouched. Verified live: POST .../archive -> 200
   {status:archived}, and the project reads back archived.

2. Writes are not read-after-write consistent. A created project took
   19s to become listable, which is what the original FAIL was. Every
   read-back now polls with a bound (poll_until, --timeout 60s,
   --poll-interval 3s): finding the created project, listing the new
   service account, confirming the archived status. Timeouts report
   diagnostics rather than a bare 'not found'.

Also drops the work-order references from smoke output (internal
business, independent of the tests) and the dangling work-orders/ paths
from README/NOTES.

Live run now green: 31 PASS, 1 SKIP (standard-key rejection, needs
OPENAI_API_KEY), 0 FAIL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeffreyaven jeffreyaven self-assigned this Jul 15, 2026
Copied verbatim from the sibling openai build (the archetype): they are
provider-agnostic, keying off website/** only.

- prod-web-deploy.yml: push to main touching website/** -> yarn build ->
  upload-pages-artifact -> deploy-pages, with the pages/id-token
  permissions and the github-pages environment.
- test-web-deploy.yml: PR to main touching website/** -> build only, no
  deploy.

Verified against this repo: website/yarn.lock is tracked (both the yarn
cache key and --frozen-lockfile depend on it, and a frozen install
passes with the lockfile unchanged); website/build is gitignored so CI
builds fresh; static/CNAME (openai-admin-provider.stackql.io) and
static/.nojekyll both land in the build output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeffreyaven
jeffreyaven merged commit 8163568 into main Jul 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant