Skip to content

feat(resync): wasm-only Resource Profile foundation - #351

Merged
schickling-assistant merged 21 commits into
mainfrom
schickling/2026-08-26-resource-profiles
Aug 27, 2026
Merged

feat(resync): wasm-only Resource Profile foundation#351
schickling-assistant merged 21 commits into
mainfrom
schickling/2026-08-26-resource-profiles

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Problem

Scheme-based agent-goal carriers such as dev.schickling.agent-goal:// were unwatchable by resync — the watcher had no principled way to resolve custom URI schemes, so declarations carried over them were invisible to the sync pipeline. The dotfiles dogfood effort needs a resolver foundation that is principled, not a pile of special cases.

Goal

Give st2 a Resource Profile foundation that lets arbitrary declaration carriers be resolved through pluggable resolvers, starting with a WebAssembly-based scheme resolver, without changing the default audit surface of the binary.

Decisions

  • Q8 — registry/SDK boundary. st2 owns an injectable exact-scheme resolver registry while schemes and their meaning remain downstream-owned. This amends the earlier rejection of a semantic registry and is now recorded in the Resource Profile VRS.

  • Q10 — wasm-only, dropping the template tier mid-flight. We measured all three options before committing:

    • exec cold-start: 760 µs – 2.901 ms
    • wasm instantiation + call: ~20.1 µs cold
    • template registry + resolve: ~2.647 µs cold

    Template was fastest but only saves ~18 µs vs wasm while adding an entire second tier to maintain and document. Wasm is fast enough to be indistinguishable in practice and strictly more expressive than templates. The accepted decision records the full comparison and consequences.

  • Feature gate. The wasm machinery (wasmtime + deps: +71 packages / +16.35 MB) sits behind the wasm-resolver feature. Default builds keep their exact dependency tree — verified with cargo tree: wasmtime is absent by default.

  • Complexity absorbed once. Per the principal's absorb-complexity-once directive, wasmtime's weight is taken into the foundation here rather than spread across future call sites.

Packaging

  • packages.st2-wasm-resolver is the production package for catalogs that declare wasm resource profiles. It compiles st2 with wasm-resolver, carries lld alongside the inherited Rust toolchain, and leaves packages.default / packages.st2 lightweight.
  • Downstream services must select packages.st2-wasm-resolver explicitly when profile resolution is required; the default package is intentionally unchanged.

Verification

  • Default feature mode: 477 lib tests green; cargo tree confirms no wasmtime in the dependency graph.
  • wasm-resolver feature mode: 477 lib + 14 + 11 isolation tests + 5 integration tests green, including a scheme-URI firing end-to-end test.
  • Nix production artifact: nix build .#packages.x86_64-linux.st2-wasm-resolver, checks.x86_64-linux.wasm-resolver-feature, and checks.x86_64-linux.wasm-resolver-artifact pass. The artifact gate runs the packaged st2 --version and strict-validates a catalog carrying the real demo wasm module; the feature gate runs all 20 profile_wasm and 5 wasm-enabled resync tests.
  • Measured boundary comparison: the experiment record records the exact declarative/exec/wasm methodology, latency percentiles, 12-test hostile-module prototype suite, LOC, dependency, binary-size, and compile-time results.
  • Real dotfiles shape: the two-run end-to-end record proves strict catalog validation, resident profile resolution, whole-file rename detection in ~600 ms, exactly one event, equal-rewrite silence, deterministic event identity, and clean teardown. It also documents the isolated-host safety deviation after scratch dev3 adopted production runtime entries.
  • VRS: axe vrs check --profile strict docs/vrs/07-resource-profile passes. The required full-tree strict check reports only pre-existing unrelated legacy-link/record-shape errors; none references the new subsystem, decision 0009, or either experiment.
  • Guest toolchain proven working via the demo-resolver-wasm example.
  • All code suites ran inside nix develop -c.

Complexity

Wasmtime's dependency weight (+71 pkgs / +16.35 MB) is accepted at the foundation layer and kept out of default builds via the feature gate, so consumers who never use custom schemes pay nothing.

Concerns

  • ABI versioning forever: guest modules compiled against our host ABI are a compatibility surface we own permanently (DQ-P1).
  • lld toolchain requirement: building guest wasm and the production resolver package requires lld; it is pinned in both the devshell and packages.st2-wasm-resolver.
  • Catalog profile blocks fail loudly on old binaries that predate profiles — opt-in, but operators on stale builds see hard errors rather than silent skips.

Follow-ups

  • structured resolver observability (DQ-P2)
  • same-path compiled-module cache invalidation semantics (DQ-P3)

References

Posted on behalf of @schickling
field value
agent_identity unknown
agent_persona unknown
agent_supervisor unavailable
agent_tool unknown
agent_tool_version unknown
agent_runtime unknown
tooling_profile dotfiles@e4789b0

@schickling-assistant schickling-assistant changed the title schickling/2026 08 26 resource profiles feat(resync): wasm-only Resource Profile foundation Aug 26, 2026
@schickling-assistant

Copy link
Copy Markdown
Contributor Author

@codex please review

Posted on behalf of @schickling
field value
agent_identity unknown
agent_persona unknown
agent_supervisor unavailable
agent_tool unknown
agent_tool_version unknown
agent_runtime unknown
tooling_profile dotfiles@e4789b0

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4c7b82f21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/agent-spec/src/profile_wasm.rs Outdated
@schickling-assistant
schickling-assistant marked this pull request as ready for review August 26, 2026 16:13
@schickling-assistant

Copy link
Copy Markdown
Contributor Author

@codex please review the current head

Posted on behalf of @schickling
field value
agent_identity unknown
agent_persona unknown
agent_supervisor unavailable
agent_tool unknown
agent_tool_version unknown
agent_runtime unknown
tooling_profile dotfiles@e4789b0

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4c7b82f21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Cargo.toml

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e12b284d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/agent-spec/src/profile_wasm.rs
Comment thread crates/agent-spec/src/profile_wasm.rs Outdated
Comment thread crates/agent-spec/src/profile_wasm.rs
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 7e12b28 to 7d02a3b Compare August 26, 2026 16:33

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d02a3b8b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/catalog.rs
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 7d02a3b to 3b81856 Compare August 26, 2026 16:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b818566ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/resync.rs
Comment thread crates/agent-spec/src/profile_wasm.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch 2 times, most recently from 7526ac8 to e6a753d Compare August 26, 2026 17:04

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6a753d7c5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/resync.rs
Comment thread src/resync.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from e6a753d to 0eed03e Compare August 26, 2026 17:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0eed03ee29

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/resync.rs
Comment thread crates/agent-spec/src/profile_wasm.rs
Comment thread src/catalog.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 0eed03e to 85d0144 Compare August 26, 2026 17:42

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85d01446a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/resync.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 85d0144 to 84293e3 Compare August 26, 2026 18:02

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84293e3591

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/agent-spec/src/profile_wasm.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 84293e3 to 48be570 Compare August 26, 2026 19:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

st2/src/resync.rs

Lines 748 to 752 in 48be570

loop {
let read = std::io::Read::read(&mut file, &mut buffer).ok()?;
if read == 0 {
break;
}

P2 Badge Bound hashing work for resolver-selected carriers

When an untrusted resolver selects a very large or continuously growing regular file, this loop reads until EOF without a size or time budget; O_NONBLOCK does not bound reads from regular files. Fresh evidence beyond the prior buffering fix is that streaming avoids the allocation spike but can still monopolize the sole resync worker indefinitely, and ResyncSupervisor::drop then hangs while joining it. Enforce a maximum hashed byte count or another interruptible work budget.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/agent-spec/src/profile.rs Outdated
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 48be570 to 645f1aa Compare August 26, 2026 19:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 645f1aa4a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/catalog.rs
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 645f1aa to 497d931 Compare August 26, 2026 19:50

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 497d93116e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/resync.rs Outdated
Comment thread crates/agent-spec/src/profile_wasm.rs
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 497d931 to f6e560b Compare August 26, 2026 20:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

st2/src/resync.rs

Lines 935 to 938 in f6e560b

let read = std::io::Read::read(&mut file, &mut buffer).ok()?;
if read == 0 {
break;
}

P1 Badge Bound digesting of large or growing carriers

When an untrusted resolver selects a very large regular file, or a writer appends faster than this loop hashes, reading until EOF has no byte or time bound; O_NONBLOCK does not make regular-file reads finite. Because the worker processes all watches serially and ResyncSupervisor::drop joins it, one carrier can indefinitely block every notification and supervisor shutdown despite the fixed-size buffer. Enforce a carrier/work limit or hash a bounded stable snapshot.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from f6e560b to 21d1ef5 Compare August 26, 2026 21:00

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21d1ef5fbf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/run.rs Outdated
schickling-assistant and others added 21 commits August 27, 2026 22:32
Per decision Q10, every resource profile is a sandboxed wasm module — no
declarative template tier. The registry (agent-spec::profile) maps URI
schemes to resolver .wasm modules run under wasmtime with fuel + memory
limits and host-side agent-dir containment; every failure (trap, fuel
exhaustion, malformed output, missing feature) is contained: the binding
stays unwatchable and the supervisor survives.

- all wasmtime code is optional: default builds carry no wasmtime;
  --features wasm-resolver opts in (re-exported at the st2 root crate)
- catalog.kdl gains a declaration surface:
    profile "<scheme>" { wasm "<path>" class "immediate|coalesced|silent" }
  class defaults to coalesced; malformed blocks fail validation loudly.
  Declared profiles flow into ResyncSupervisor::with_profiles from the up
  loop the same way the declared pty root flows into spawning.
- profile-resolved carriers use their DECLARED class instead of basename
  sniffing; silent profiles stay out of the watch set entirely
- guest toolchain: crates/demo-resolver-wasm documents and implements the
  resolve ABI; rebuild with lld (now in the devshell) and refresh the
  committed fixture (verified byte-identical)

agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: OMP
agent-tool-version: 18.0.3
agent-runtime: OMP 18.0.3
tooling-profile: dotfiles@a80129b-dirty
@schickling
schickling force-pushed the schickling/2026-08-26-resource-profiles branch from 648b4df to 5aa0a1f Compare August 27, 2026 20:32
@schickling-assistant
schickling-assistant merged commit a601331 into main Aug 27, 2026
1 of 2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5aa0a1f180

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/agent-spec/src/profile.rs
schickling-assistant added a commit that referenced this pull request Aug 28, 2026
#351 landed first and rewrote R20: a binding's URI scheme is now the exact
lookup key for an optional, catalog-declared Resource Profile, scheme meaning
stays downstream-owned, and st2 ships no built-in profiles.

Drop R35. It existed only because R20 then read "st2 does not register schemes",
which a scheme st2 resolves would have contradicted; it granted an exemption from
a clause R20 no longer contains, so rebasing it would have carried a
self-contradiction into requirements.md. `working-state` needs no exemption under
the merged R20 — st2 writes the carrier through `st2 context`, and resolving the
scheme is a catalog's choice. requirements.md is now identical to main: this
branch makes no constitutional edit.

Decision 0012 records the withdrawal as an amendment rather than rewriting its
history. Nothing about the carrier, its scheme name, or its realization changes.

Renumber this branch's decisions 0008/0009/0010 to 0011/0012/0013; #351 and #345
took 0008 and 0009 on main.

Correct three places that asserted the pre-#351 contract: the ontology's Resource
and working-state entries, and 07-resource/spec.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty
schickling-assistant added a commit that referenced this pull request Aug 28, 2026
"No Resource registry, no generic resolution" described st2 before #351 landed
one. Resolution is out of scope for 07-resource, not absent from st2: the
scheme-keyed profile registry lives in 07-resource-profile. The two are
orthogonal — a binding names a Resource whether or not a profile resolves its
scheme, and `st2 resource` neither reads nor requires one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty
schickling pushed a commit that referenced this pull request Aug 28, 2026
…rface (#361)

* docs(vrs): retire the linked-record plane; add R35 st2-owned Resource profiles

st2 carried two disjoint durable edges both called "resource": Agent Spec
Resource bindings, and the link records written by `st2 resource add`. Nothing
in the corpus said they were disjoint, and `ontology.md` — the disambiguation
index that exists to prevent exactly this — defined neither.

Measured on one live catalog of 655 declarations: 889 distinct binding URIs and
233 distinct link-record URLs share zero members, exactly and after
normalization on all five schemes both planes used. The planes are disjoint by
construction — bindings carry what an agent is for, link records carried what it
made — so a shared descriptor type deduplicates nothing.

The link plane was write-only (its only reader was its own `ls`/`read` verb),
adopted by 12.5% of declarations, decaying, and superseded by
`axe work update --artifact/--pty`. It is retired.

The drift between the planes was caused by write-cost asymmetry, not by two
concepts sharing a word: 45 of 241 link records were dependency edges filed in a
products store, because a binding needed publisher authority plus
whole-declaration CAS republication while a link record was one file write.

Adds the 07-resource tree, ontology entries for Resource, Resource binding,
linked record (retired), agent resource directory, and working state, and three
decision records. R35 names the st2-owned Resource profiles that R20's "st2 does
not register schemes" otherwise forbids, so `working-state` can exist without
leaving R20 contradicted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* feat(resource): repoint `st2 resource` at declared bindings with mediated writes

`st2 resource` managed link records under `<agent-dir>/resources/links/` — a
plane nothing read, that 12.5% of declarations used, and that `axe work update
--artifact/--pty` has superseded. It is removed, and the verb now reads and
writes the plane that has the spec authority.

    st2 resource ls [<identity>] [--json]
    st2 resource read [<identity>] <name> [--json]
    st2 resource add <name> --uri <uri> --reason <text> [--inactive-reason <text>]
    st2 resource remove <name>
    st2 resource rename <old> <new>

`ls`/`read` project declared bindings; bindings previously had no human surface
at all, which is why agents reaching for `st2 resource ls` measured the other
store. The write verbs do read-modify-CAS-publish internally so the caller never
renders KDL, and they are the fourth caller of the `agent_author` pattern after
streams, desired state, and presentation — full-catalog validation, exact-target
selection, compare-and-swap, fail-closed concurrent change, and the Nix-managed
refusal all come from the existing machinery. A binding-only change does not
relaunch healthy work (R21), and URI bytes are preserved without normalization
(R20).

`<agent-dir>/resources/` is untouched and remains canonical for an agent's
resource files; only `links/` goes.

Two `catalog_apply` tests used `st2 resource add` as a state-plane write probe.
That is now a declaration-plane write, so they probe `context append` and
`message send` instead — the writers that still route through
`with_resolved_state_dir`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* fix(resource): drop the dead `links` state-dir entry; correct the ls example ordering

`marker_state_exists` still validated `resources/links` as a real directory. st2
no longer owns that path — the linked-record plane is retired and nothing reads
or writes there — so asserting on it is stale vocabulary. The orphaned
directories remain on disk untouched; st2 simply stops making claims about them.

The 07-resource worked example listed bindings in declaration order. `ls` sorts
by name, which is correct: declaration order has no meaning. Verified against the
real catalog byte-for-byte, including the width-aware name padding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* fix(resource): handle a binding's trailing line comment on remove and update

A hand-authored `resource "work" uri="…" reason="…" // why it is here` could not
be removed: `remove_field`'s standalone-line path accepted only whitespace after
the node, so it fell through to the compact path and refused with
`unsafe-source-shape`. The line's own trailing `//` comment is now removable
trivia and goes with the binding it explains. `/*` is deliberately not accepted —
a block comment can span lines and this only ever sees one.

Updating the same binding had a quieter defect: the node span runs to the start
of trailing trivia, so replacing it verbatim glued the rendered node onto the
comment (`reason="Changed."// why`). The separator is now preserved. The output
still parsed, so this was cosmetic rather than corrupting.

Reported by Codex review on #361 (P2). The remove case is the one that hard-failed;
the update case was found while confirming the scope.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* docs(vrs): reconcile with the merged Resource Profile foundation

#351 landed first and rewrote R20: a binding's URI scheme is now the exact
lookup key for an optional, catalog-declared Resource Profile, scheme meaning
stays downstream-owned, and st2 ships no built-in profiles.

Drop R35. It existed only because R20 then read "st2 does not register schemes",
which a scheme st2 resolves would have contradicted; it granted an exemption from
a clause R20 no longer contains, so rebasing it would have carried a
self-contradiction into requirements.md. `working-state` needs no exemption under
the merged R20 — st2 writes the carrier through `st2 context`, and resolving the
scheme is a catalog's choice. requirements.md is now identical to main: this
branch makes no constitutional edit.

Decision 0012 records the withdrawal as an amendment rather than rewriting its
history. Nothing about the carrier, its scheme name, or its realization changes.

Renumber this branch's decisions 0008/0009/0010 to 0011/0012/0013; #351 and #345
took 0008 and 0009 on main.

Correct three places that asserted the pre-#351 contract: the ontology's Resource
and working-state entries, and 07-resource/spec.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* docs(vrs): scope the resource boundaries to this document, not to st2

"No Resource registry, no generic resolution" described st2 before #351 landed
one. Resolution is out of scope for 07-resource, not absent from st2: the
scheme-keyed profile registry lives in 07-resource-profile. The two are
orthogonal — a binding names a Resource whether or not a profile resolves its
scheme, and `st2 resource` neither reads nor requires one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

* test: align resource validation coverage with the widened #345 envelope

#345 admits a catalog-relative carrier path as a binding uri, not only an
absolute URI, and reserves the binding name `declaration` for resync. Three
assertions written against the older contract went stale on the rebase:

- `agent_author` refused `./issue/1` and `example.com/issue/1`; both are valid
  relative carriers now. The refusals worth pinning are the ones that escape the
  catalog (`/etc/passwd`, `../outside`), so those replace them, and a positive
  case pins that `carriers/goal.md` is admitted.
- `agent_resource` asserted a non-absolute uri is refused; it now asserts the
  escaping paths are refused and that the resync-reserved name is refused.
- `agent_publish` drove the retired link-plane `resource add <url>` as a
  state-plane write under an incomplete-apply marker. A binding write is a
  declaration write, so it probes `context append` for the state plane and now
  additionally asserts the marker fences `resource add` — the behaviour that
  moving planes makes correct.

Verified against a same-toolchain `git archive origin/main` baseline: the
branch's failing-target set is a strict subset of main's own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agent-identity: unknown
agent-persona: generalist
agent-supervisor: unavailable
agent-tool: Claude Code
agent-tool-version: 2.1.237
agent-runtime: Claude Code 2.1.237
tooling-profile: dotfiles@11eaf2d-dirty

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

2 participants