Skip to content

ci(release): drop the head_commit.message guard that broke workflow parsing - #6

Merged
thorwhalen merged 1 commit into
mainfrom
ci/fix-release-yml
May 13, 2026
Merged

thorwhalen merged 1 commit into
mainfrom
ci/fix-release-yml

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

Summary

The release.yml that merged in #5 failed to even start (0 jobs, 0s duration, conclusion failure). The GitHub Actions parser rejected the file at L36 with "Invalid workflow file" — the if-expression

if: github.event.head_commit.message != 'chore(release): bump versions'

has a colon inside the single-quoted literal that the GH Actions expression parser couldn't swallow.

The guard wasn't load-bearing anyway: changesets/action is idempotent — with no pending changesets and versions in repo matching the registries, it no-ops in a few seconds. Removing the guard lets the workflow load.

What this unblocks

After merge, the next push to main triggers release.yml. Since packages/*/package.json are at v0.2.0 (ahead of npm) and there are no pending changesets, changesets/action will go straight to publishing the 10 npm packages and the Python stub.

Test plan

  • YAML is valid locally (the offending line is gone)
  • On merge, watch release.yml start successfully and publish v0.2.0

…arsing

GitHub Actions rejected the workflow file at L36 ("Invalid workflow file") because
the if-expression `github.event.head_commit.message != 'chore(release): bump
versions'` confused the YAML parser — the single-quoted string contains a colon
that ran afoul of GH Actions' expression parsing.

The guard wasn't load-bearing anyway: changesets/action is idempotent. With no
pending changesets and versions matching the registries, the workflow no-ops in
a couple seconds. Removing the guard lets release.yml load.
@thorwhalen
thorwhalen merged commit ab09157 into main May 13, 2026
1 check passed
@thorwhalen
thorwhalen deleted the ci/fix-release-yml branch May 13, 2026 16:49
thorwhalen added a commit that referenced this pull request May 15, 2026
Settled with the user on 2026-05-15: pull `acture-telemetry` and
`acture-undo` forward from "Post-v1 (deferred)" into v1.x, in that order
(telemetry first because smaller; reuses `acture-devtools`'s
`instrumentRegistry` hook). undo consumes `PatchCapableAdapter`, which
both state adapters already implement.

The next session's handoff (docs/next_session.md) declares the meta-
decision settled (no AskUserQuestion for Step 1's direction), but keeps
the rule-of-three callers gate explicit: the next agent asks the user to
name three concrete callers per item before deep work, records them in
each package's README, and defers any item that can't clear the gate.

Lays out the proposed shapes (telemetry: single console sink, redact /
sampler options, closed record shape; undo: linear patch stack,
transaction grouping, host-supplied effect hook), the three escalation
points (caller-gate misses, the `instrumentRegistry` hook-chain question,
effect-semantics call), and the hard-don'ts that bind (#2 no god-package,
#3 translate not decide, #6 no React in core-instrument code). Each
package gets a `docs/hand-written-*.md` reference doc and a consumer skill
— the dev-tool-first promise non-negotiable.

The roadmap "Next" section updated to point at the handoff rather than
"do not pull forward unilaterally" (now stale — the user has directed).
thorwhalen added a commit that referenced this pull request May 15, 2026
First post-v1 promotions in the suite. Both shape decisions surfaced
with the user via AskUserQuestion before code (Step 1 of the v1.11
handoff); all four landed on the simpler / more flexible option —
single-function callbacks (no mini-DSLs), errors-as-data preserved,
"translates, doesn't decide" applied inward. Full write-up in
docs/v1_11-reflection.md.

acture-telemetry (new package, 1.0.0, +18 tests)

Observes every dispatch via a configurable sink. Optional pass-through
redact and sampler callbacks. One built-in consoleSink for reference;
multi-destination is user-side sink composition. Errors-as-data
preserved end-to-end — record.result is the full Result<unknown> from
the dispatcher. Telemetry never breaks dispatch (sampler/redact/sink
each in defensive try/catch). Idempotent per registry; the disposer
restores the dispatch captured at install time. Hand-written equivalent
~30 lines (docs/hand-written-telemetry.md). Consumer skill:
acture-telemetry.

acture-undo (new package, 1.0.0, +19 tests)

Patch-based undo/redo over a PatchCapableAdapter. createUndoHistory
observes the adapter's setStateWithPatches calls and the registry's
dispatch boundaries; multiple patches per dispatch become ONE undo
entry; transaction(fn) groups N dispatches. Partial-failure semantics
(settled with user): mid-transaction failure leaves prior mutations
applied; the entry is still pushed; caller can undo() to rewind.
Effects flow through optional onEffect(effect, { isUndo, isRedo }) host
callback at apply/undo/redo lifecycle points — acture-undo never enacts
effects itself. Inverse patches are pre-reversed at push time so
applyPatches(state, inversePatches) is one call per undo. Limit default
100; oldest dropped when exceeded. Hand-written equivalent ~80 lines
(docs/hand-written-undo.md). Consumer skill: acture-undo.

Composition

Both packages wrap registry.dispatch via the same monkey-patch pattern
as acture-devtools's instrumentRegistry and enableTierWarnings — install
order is install order; dispose in reverse install order. No core change
was needed; investigation confirmed the existing pattern composes
naturally at install time. The dispose-order contract is documented in
all three READMEs.

Consistency updates

- acture-architecture-primer consumer-surface list: #5 telemetry and #6
  undo/redo now reference the shipped artifacts (no longer "post-v1").
- acture-consumer-integration: per-tool table gained telemetry and undo
  rows; "See also" enumerates all eight per-surface skills.
- acture-state-adapter: no longer marks undo as "post-v1"; references
  acture-undo as the PatchCapableAdapter consumer.
- docs/roadmap.md: telemetry/undo moved from Post-v1 to v1.11 Done;
  tracking table updated; "Next" reframed around the remaining post-v1
  options (Python companion, test-property, additional state adapters,
  sandbox).

Workspace: 18 packages (was 16). 460 package tests + 41 example tests
green. Hard-don'ts audit clean. Pending changesets: acture-telemetry
minor + acture-undo minor, no cascade.
thorwhalen added a commit that referenced this pull request May 15, 2026
New package pulled forward from Post-v1 as the first half of the
autonomous v1.12 + v1.13 chain (per the rewritten docs/next_session.md).
Shape decisions made autonomously per the handoff; no AskUserQuestion
fired; no "truly stuck" criterion fired. Full write-up in
docs/v1_12-reflection.md.

acture-test-property (new package, 1.0.0, +29 tests)

fast-check arbitraries over the command registry; random
CommandSequences replayed via acture-e2e-playwright's replaySequence;
invariants asserted end-of-sequence. On a counter-example the thrown
PropertyTestFailure carries the shrunk failing sequence and the
invariant name -- the user replays via replaySequence(registry,
err.sequence) for deterministic reproduction.

Three layers:

  1. zodToArbitrary(schema) -- in-package mapper covering the
     JSON-Schema-representable subset acture's toJsonSchema already
     serializes (string, number, boolean, literal, enum, array, object,
     union, optional, nullable). Probes both Zod 3's _def.typeName and
     Zod 4's _def.type. Unsupported types throw UnsupportedZodTypeError
     with a clear hint -- silent skipping would mean a "valid" failing
     sequence the user couldn't reproduce. (The handoff's preferred
     option was @fast-check/zod, but npm view confirms it does not
     exist on npm; shipped the YAGNI-respecting in-package fallback.)
  2. commandArbitrary / sequenceArbitrary -- draw commands from
     registry.list({ tiers }) and chain zodToArbitrary on the schema.
     Tier filter respected (default ['stable']).
  3. propertyTest({ registry, adapter, invariants, runs?,
     sequenceLength?, tiers?, schemaToArbitrary?, ctx?, resetState?,
     seed? }) -- glues fc.assert(fc.asyncProperty(...)),
     replaySequence, adapter.getState(), and the user's invariants.
     State reset between runs (default JSON-clone snapshot of
     adapter.getState() at start; resetState hook for non-JSON state).
     A failing dispatch is treated as a property failure with the
     sequence preserved.

Tested against both acture-state-zustand and acture-state-redux
adapters in the same suite. 29 tests covering: every primitive Zod
type, composite Zod types end-to-end (generated values validate
against the source schema), registry/tier filters, sequence-length
bounds, the unsupported-Zod throw, happy-path, counter-example with
sequence attached, deterministic shrinking with a fixed seed,
replay-determinism of the shrunk sequence, dispatch-failure-as-
property-failure, both adapters, state reset (default + custom hook).

Builds *on*, doesn't re-derive, the v1.7 sequence engine. Depends on
acture-e2e-playwright for the pure replaySequence (Playwright is type-
only in that package's main entry; no Playwright runtime is pulled in).

No god-package. One fast-check binding only. No Vitest/Jest matcher,
no HTML report, no CI integration, no per-step invariants, no
fc.commands stateful-model surface -- each is its own future package
if real demand surfaces (hard-don't #2).

minor changeset. Hand-written equivalent: docs/hand-written-test-
property.md (~60 lines, faithful to the package's exported shapes).
Consumer skill: acture-test-property (mirrors the
acture-telemetry/acture-undo template).

Consistency: roadmap status snapshot (19 packages, 489 tests, 25
skills, 6 reference docs), v1.12 Done entry, tracking table; the
architecture primer's consumer-surface list is deliberately NOT grown
-- per the handoff, test-property is the e2e surface's property-test
variant, not a 9th surface.

Pre-merge hard-don'ts check: #1 (no inner-platform DSLs for invariants
or redact-like options), #2 (one fast-check binding only), #3 (the
package projects the registry; user owns invariants, schemaToArbitrary,
resetState, tier filter), #6 (no React anywhere). All four pass.
thorwhalen added a commit that referenced this pull request May 15, 2026
End of the autonomous v1.12 + v1.13 chain. The PyPI `acture`
distribution -- which had been a name-reservation placeholder since
the initial publish -- is now a real, thin MCP-client facade per
research-6. Shape decisions made autonomously; no AskUserQuestion
fired; no "truly stuck" criterion fired. Full write-up:
docs/v1_13-reflection.md.

The Python client (PyPI `acture`)

Three modules under python/acture/ (~300 LoC total):

  - types.py: ActureError -- errors-as-data across the language
    boundary. Stable, programmable fields (code, message,
    command_id, details) mirroring acture core's CommandError.
  - transport.py: stdio_transport / http_transport. Async context
    managers yielding (read, write) streams the mcp.ClientSession
    constructor expects. Lazy import of mcp.client.streamable_http
    so the base package imports cleanly on older SDK builds.
  - client.py: ActureClient (Mapping[str, Command] facade) plus
    Command (callable with __call__/call_raw, plus description and
    input_schema properties). from_stdio / from_http / connect
    factories; refresh() for tools/list_changed; session property
    for MCP features the facade doesn't wrap.

The dict-like surface is the dol / py2mcp idiom. iter(client)
yields known command ids; len(client) counts them; client['cmd.id']
returns a callable. New commands on the server are automatically
available -- no codegen step in the loop.

Errors-as-data preserved across the boundary: a TS dispatch's
{ ok: false, error: { code, message } } is packaged by
acture-mcp-server's formatToolResponse as an MCP result with
isError=true and the JSON-stringified CommandError in a text content
block; the Python client unwraps this into ActureError. Callers who
want the raw dict use Command.call_raw.

23 tests using the SDK's in-memory transport
(create_connected_server_and_client_session). Covers Mapping
protocol, Command projection, dispatch happy-path,
error-with-typed-exception, call_raw, refresh, transport-factory
shapes, ActureError formatting. The conftest works around the
anyio/pytest-asyncio "cancel scope exited in different task" trap by
having tests use the echo_server() async context manager directly
inside each test (not as a yielded fixture).

Out of scope for v1 per research-6: Pydantic-codegen SDK, OpenAPI
emitter, CLI shim, FastAPI shim, inverse-direction skill kit. Each
is post-v1 if real demand surfaces. No god-package (hard-don't #2).

One dependency: the official mcp SDK (>= 1.10). No Pydantic dep.

The patch changeset on npm `acture`

Cross-language semver is in lockstep with npm `acture` via the
existing scripts/sync-python-version.mjs. The patch changeset on
npm `acture` carries no source change to packages/core -- it drives
the sync step in the release workflow, which keeps PyPI `acture` at
the same version as npm `acture`. Starting with this version, PyPI
`acture` is no longer a name-reservation placeholder -- it is the
real client. Decoupling cross-language semver is a deliberate
reversible future decision; v1.13 stays in lockstep.

Consistency updates

  - docs/positioning.md -- section 1 now names PyPI as the third
    delivery surface (after skills/patterns and npm packages).
  - docs/roadmap.md -- status snapshot (19 npm + 1 PyPI; +23 Python
    tests; 26 skills; 7 reference docs), v1.13 Done entry, tracking
    table updated, Post-v1 Python-companion bullet struck through.
  - docs/v1_13-reflection.md -- the full write-up.
  - docs/hand-written-python-client.md -- the ~50-line agent-written
    equivalent reference; mirrors hand-written-registry.md and the
    other reproducibility docs in status, structure, and
    faithfulness commitment.
  - .claude/skills/acture-python/SKILL.md -- consumer skill; mirrors
    the acture-test-property / acture-telemetry / acture-undo
    template.
  - .claude/skills/acture-consumer-integration/SKILL.md -- per-tool
    table gained a Python-consumption row; See also enumerates the
    new skill.
  - docs/next_session.md -- rewritten as a fresh handoff. The
    autonomous chain is over. Three remaining post-v1 options
    (acture-state-jotai, acture-state-valtio, acture-sandbox) are
    surfaced with honest trade-offs; the user steers from here.
  - python/pyproject.toml -- graduated from "placeholder, Pre-Alpha"
    to "real client, Beta"; added mcp >= 1.10 dep, test extras,
    pytest asyncio_mode config, Python 3.10-3.13 classifiers,
    Framework :: AsyncIO, npm package URL.
  - python/README.md -- rewritten from "name reservation only" to a
    real quickstart.
  - python/acture/__init__.py -- public barrel exporting
    ActureClient, Command, ActureError, stdio_transport,
    http_transport. __version__ preserved (still synced by
    scripts/sync-python-version.mjs).

Hard-don'ts check (pre-merge ritual)

  - #1 inner-platform creep: no re-shaping of MCP errors into
    different field names; no client-side tier filter (server-side
    is authoritative); no cloner option for non-JSON state (caller
    picks call_raw if needed). PASS
  - #2 god-package: one MCP SDK binding (mcp). No Pydantic helper,
    no OpenAPI emitter, no CLI shim, no FastAPI shim, no
    inverse-direction skill kit. PASS
  - #3 translate, don't decide: the package projects MCP into
    Python; the user owns transport choice, invariants, error
    handling style, structured-content interpretation. PASS
  - #6 (no React in core): N/A -- Python. Equivalent: no Pydantic
    dependency forced on Python consumers. PASS
  - Dev-tool-first: hand-written equivalent shipped first
    (docs/hand-written-python-client.md); README leads with the
    dev-tool-first banner. PASS

Chain end. v1.12 + v1.13 ship; from here, the user steers.
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