Repository navigation
Releases: NousVigil/archstone
Release list
v0.34.0
Added
booleansemantic type (Experimental). A field can now be declaredtype: booleanorlist: boolean, on inputs and outputs and inside resources. It means a JSONtrueorfalseand nothing else, and lowers to{"type": "boolean"}ininputSchemaandoutputSchema. Nothing is coerced on either side: an input of"true",1ornull(for a required field) is refused asinput_invalidnaming the field before the provider is called, and a provider value that is not a JSON boolean (0/1,"true","t") is the wrong shape for its field, which is a contract violation when the field is required and an omitted, degraded field when it is optional.falsecounts as present. A Postgresbooleancolumn reaches abooleanfield as a JSON boolean, and@archstone/agentcarries the type through unchanged.booleanis the first plain scalar whose output value the response mapper type-checks (quantityandtextstill accept any primitive; see #157). Packages:@archstone/compiler,@archstone/schema,@archstone/emitter-support.
Changed
archstone initandarchstone adoptnow emitboolean. An OpenAPItype: booleanand a Postgresbooleancolumn (previously skipped withcolumn-type-not-expressible) map toboolean, andadoptoffers an observed JSONtrue/false(and an array of them aslist: boolean) instead of refusing it withno-boolean-type. A union such as[boolean, string]still resolves tostring. For an author who had declared a flag asstringand moves it toboolean,archstone diffreports the change as breaking (input-retyped/output-retyped), because the contract really does change. Packages:@archstone/init,@archstone/runtime.
Fixed
-
The README's Claude Code key instructions led to a failing command. It named the
--header "Authorization: Bearer <key>"flag without saying where it goes, and placed before the server name it fails witherror: missing required argument 'name', because--headertakes every argument that follows it. The README now gives the whole command, with the header after the name and URL, as the showcase README already does. -
Showcase prompts that only worked as a follow-up. Pasted alone into a fresh conversation against the hosted demo, the suggested prompts of S-02, S-06 to S-09, S-11 and S-12 ("Book that quote…", "Pay for the booking…", "Tell me more about Casa Alfama") made a model ask for the missing context instead of calling a tool. Each now names the stay, the dates, the guests and any booking number it needs, so it works on its own (checked with a model, one prompt per conversation). Keyed rows (S-06 to S-09) gain an optional
ownAinote inscenarios.jsonsaying what an AI app connected without a key will see, and the showcase README shows how to send a demo key from a client that lets you set anAuthorizationheader (Claude Code does). -
An IR that names a semantic type the runtime does not know is now refused at load, not lowered as
string.@archstone/emitter-support'sRegistryconstructor, which@archstone/runtime(archstone serve) and@archstone/agent(fromIR) both build, now throwsUnknownSemanticTypeErrornaming the type and the field path (for exampletools[shop.get].input.flag), for input, output, list-element and resource fields alike. The JSON Schema lowering, the input validator and the response mapper throw the same error rather than defaulting to a string, so a runtime older than the compiler no longer advertises a newer type asstringand rejects valid values with "expected string". This protects runtimes from 0.34.0 onward: a runtime older than 0.34.0 still falls back tostringfor an unknown type, so keep the compiler, the runtime and@archstone/agenton the same version.UnknownSemanticTypeErroris exported from@archstone/emitter-support,@archstone/runtimeand@archstone/agent.
v0.33.0
Upgrading: inputs are now enforced, and permissive manifests may see new refusals. From this
release a capability's declaredinput:is checked before the provider is called, and the
advertisedinputSchemais closed (additionalProperties: false). A call whose arguments do not
match the declared contract, including one that carries an undeclared key, is refused with
isError: trueand_meta["dev.archstone/input_invalid"]
({ error: "input_invalid", capability, problems: [{ path, expected }], truncated? }); the
embeddedexecute()returnsdenial.reason: "input_invalid". If your agents or manifests relied
on permissive inputs (extra keys, numbers where strings are declared, free-text dates, negative
partycounts), either fix the caller to send what the capability declares, or widen the
capability'sinput:so it declares what you actually accept, then re-runarchstone apply.
The details are the first entry under Changed below.
Changed
- BEHAVIOUR CHANGE: a capability's declared
input:is now enforced before the provider is called. Until nowinputSchemawas advertised but never checked, so arguments of the wrong shape (an object where alocationstring is declared, a free-text date, a negativepartycount, an undeclared key) were forwarded to the backend, and only the backend's own validation stopped them.callTool(MCP, stdio and HTTP) andexecute()(embedded) now validate the arguments with one sharedvalidateInputin@archstone/emitter-support, after the lifecycle and policy gates and before rate limiting and any connector work. A mismatch returnsisError: truewith_meta["dev.archstone/input_invalid"]({ error, capability, problems: [{ path, expected }], truncated? }, at most 16 problems), orExecuteResult.denial = { reason: "input_invalid", capability, problems }, and the audit record isphase: "denied",denialReason: "input_invalid". The refusal names declared field paths and fixed expectations only; it never contains a value or an undeclared key name the caller sent. Nothing is coerced: a wrong type, a missing required field, a value outside anenum, a malformeddate/date-range/date-time, a non-integer or negativepartycount, a malformedmoney, and any undeclared key (top level, or insidemoney,party,date-range) are all refused;nullon an optional field still counts as absent. The advertisedinputSchemais now closed to match (additionalProperties: falseat every object level,minimum: 0onpartycounts), so a client that sent extra keys, numbers for strings or free-text dates and was accepted before will now be refused.execution.schema.json'sdenialReasonenum gainsinput_invalid.
Fixed
-
A required field that arrived in the wrong shape was reported as missing (#196). A backend that sent a string for a
quantity/moneyfield producedcontract violation … missing required field(s): pricePerNight, which sent the reader looking for a field that was there. The violation now keeps the two apart, in the text and in_meta["dev.archstone/contract_violation"]:missinglists only fields the provider did not send, and a newinvalidlists fields it sent in the wrong shape as{ field, expected }, whereexpectedis the declared type.invalidis present only when non-empty, so a violation with nothing malformed is byte-identical to before;missingkeeps its key and shape, but a malformed field is no longer in it. An optional field in the wrong shape is likewise named in a separate degraded note and ininvalid(it also stays indegraded, which still lists every optional field left out of the result).ExecuteResultandContractRecordingcarry the sameinvalid. The provider's value is never echoed. -
The Showcase's searches invented stays and ignored
budgetandpreferences.wanderlust_searchreturned made-up "Generated Stay" rows for any city but Lisbon, whose ids resolved nowhere, and the deprecatedtourism_searchhashed the destination and ignoredbudget. Both now read one catalogue (Lisbon, Porto, Barcelona, Nice, Bucharest) through one destination resolver that tolerates case, diacritics, aliases (Lisboa,Bucuresti) andCity, Country; an unknown place is an honest empty result and every returned id resolves on details, photos, page, quote and availability.budgetis a per-night EUR ceiling on both searches (another currency, or dates that are not an ISO range, are a 400). Both declarepreferences(pets,breakfast,family; synonyms such aspet-friendly,cat,dogfold in, other tags are ignored), andwanderlust.Stayandwanderlust.StayDetailsdeclarepetPolicy. Adds a deterministic conversation suite andpnpm showcase:conversations, a local script that drives a live endpoint with a real model. -
Models passed a stay's name where the showcase expects its id. Against the hosted demo a model called
wanderlust_stay-photoswith{"stayId":"Casa Alfama"}and got a bare 404, or asked the user for a city instead of searching by name. Every stay-id input in the showcase manifest now says it is theidfield returned bywanderlust_search(likews-1001), never a name, and that a stay the user names is found by callingwanderlust_searchwith that name asdestination; thedestinationdescription says a stay's own name works. Declared descriptions only: the runtime and the ids are unchanged. -
Showcase v2: a believable synthetic agency, and honest scenario cards (#201). Examples only; no Archstone guarantee changes. The synthetic agency now issues quotes and payment quotes that expire 15 minutes after the request (not at a fixed boundary), and
bookandpayrefuse an expired one;wanderlust.quotetakes an optional declaredpetscount and itemises the stay's pet fee inpetFeeand in thetotalthe booking honours; a stay's exact name (diacritics and case folded) works as a destination on both searches; room-status isagency-busy/ wrong-typed only on one property and date each; cancelling an id the agency never issued is404 booking_not_found, and the seeded booking refunds its total. The blocked demo key is now denied by policy onpayandcancelas well asbook. The scenario table gains S-23 (a wrong-shaped argument is refused before the agency is called), S-13 now also makes the wrong-format-price call, S-12 returns the tool-description excerpt it relies on, S-06/S-07 say which side enforces what, and the live/runbody carriesbackendCalls. -
The note that says a value was withheld now says exactly where, and that the rest is fine (#201, round 2). The origin-withholding note named only field names (
url,photos[3]), so a model could read "the official page's link is withheld" when only a partner row's link was, and an original array index looked like a position in the shorter list it received. It now names each place in the result by the output field names the model sees, with the item's position in the provider's list as the index,pages[1].url (field omitted), and a removed list item readsgallery.photos[3] (removed; the list now has 4 items); the note ends "Every other link and image returned passed the origin check." (only links and images are origin-checked, so it claims no more)applyResponseMappinggainswithheldAt(located paths) beside the unchangedwithheldnames, andwithheldNotetakes those locations. Nothing that was withheld before is now returned: the guarantee is unchanged. Showcase only:wanderlust.stay-pagesays a partner listing's link is withheld;wanderlust.cancelandwanderlust.paysay they need the agency key and an allowed principal; the booking carriespetsandpetFee(0 when none); and a stay's nightly rate is one figure across search, quote, room-status and availability. -
The Showcase agency's two availability endpoints now agree (#201, round 3).
wanderlust.room-status(per room type, free or taken) andwanderlust.availability(rooms free) were derived independently, so a date could read "both room types taken" in one and "4 rooms free" in the other, and a model could only call the date unconfirmed. Both now come from one deterministic count: 0 rooms free means every room-type row is taken, 1 means exactly one type is free, 2 or more means both are. Prices are unchanged. The two deliberate faults (the busy row onws-1002, the wrong-typed price onws-1003, both on 2027-05-12) stay, and the rows beside them agree with availability too.wanderlust.availability's description now says the call limit is Archstone's policy, 3 calls per minute per caller, rather than "Rate limited."
v0.32.0
0.30.0 and 0.31.0 were tagged but never published to npm. Their release gate failed on the
sql-reportingexample's verify step (fixed below), so npm kept serving 0.29.0. Everything in the
[0.30.0]and[0.31.0]sections below therefore ships for the first time in 0.32.0, together
with the entries listed here.
Added
-
The Showcase has an executable negative-scenario suite.
examples/showcase/test/now asserts, through the real runtime and with a request spy around the synthetic API, what Archstone withholds and refuses: no margin, passport, phone or raw HTML reaches a model; an off-origin image or page link is withheld by field path and never fetched; each of the seven denial reasons is reached and a test fails if one is not; no tool exists for deletion and noDELETEis ever issued; and a hand-written passthrough double proves the absence checks cannot pass by accident. Examples and tests only; nothing in the published packages changes. -
Recorded Showcase scenarios,
examples/showcaseS-15 to S-21. A recorder (pnpm showcase:record,
pnpm showcase:record:check) runs the workspace CLI and the embedded SDK against the synthetic agency
and writes one stable JSON transcript per scenario toexamples/showcase/transcripts/: a SQL report from
a local Postgres that the live manifest does not serve,apply --exposure,verifyandadopton a
backend that gains a field,diffwith the backend stopped,auditanddoctorwith no outbound
connection (enforced by a preload that makes any attempt fail),initfrom the OpenAPI document, and the
@archstone/agentSDK in three vendor shapes. Each scenario asserts its expected outcome and its
negative and exits non-zero otherwise;--checkfails CI on any byte that differs from a fresh
recording. Examples and tests only: no published package changes. -
A synthetic travel-agency example,
examples/showcase. An invented agency whose backend
deliberately over-exposes (guest passports and phones at several nesting levels, a private margin,
raw HTML, images and links on undeclared hosts, a DELETE endpoint), and the CDL manifests that
contain it: all three effects, every lifecycle state,authenticatedwith${caller.accessToken},
a principal allow/deny rule, a rate limit,onErrorrows, nested projection, and theimageand
web-pagetypes with declared origins. It ships a mis-declared payment variant thatarchstone apply
warns about without blocking, a scenario table (scenarios.json), two public demo keys, and tests
that run the real pipeline against the API in-process. Examples and tests only: no published package
changes.
Changed
- Behaviour change: a backend that returns a bare number or string for a
moneyfield is no longer passed through. The value is now withheld (optional field, resultdegraded) or acontract_violation(required field). Declare such a fieldquantityif the backend really sends a plain number, or have the backend send{amount, currency}.
Fixed
-
archstone adoptcould edit the wrong namespace's resource file. When two namespaces declared a resource with the same bare name (tourism.Stayandwanderlust.Stay), the lookup returned whichever document it met first, so a field adopted intowanderlust.searchwas written intotourism.Stay's file and the edit then failed to compile. An exact name now wins; the bare-name fallback is used only when exactly one resource matches, and an ambiguous one is reported as a problem naming every candidate. -
archstone adoptresolved a namespace-qualified resource name into another namespace. Whenwanderlust.Foohad no exact match but exactly one resource elsewhere shared the bare name (tourism.Foo), the bare-name fallback picked that one and edited its file. A qualified name now never crosses namespaces: with no exact match, adopt reportsno resource '<ns>.<Name>', lists same-bare-name resources as a hint, and writes nothing. The bare-name fallback still applies to unqualified names. -
apply --exposurelisted a retired capability as exposed. Each capability now carries astate(exposed,unlistedfor an experimental one callable by id, ornot_exposedwith areasonofretired,unevaluatableorunbound), in the human report and in--json, which also gains atotalsobject.totals.exposedequals the number of toolstools/listadvertises: both read the newRegistry.listedTools(). (#173) -
Extraction accepted any string as a
moneycurrency (#182).extractor(resource, format).validate()passed{amount: 10, currency: "euro"}(and"","eur") as valid money, although the response path has required a finite numericamountand a three-uppercase-lettercurrencysince #176. Both paths now call one shared check, so they cannot disagree. In extraction a present but malformedmoneyvalue is aninvalidentry and the document aviolation, as for every other malformed value (extraction never repairs, ADR-0011). -
A
moneyoutput field accepted a plain string as valid (#176). A value such as"price": "129.00"was passed on as if it were money. The mapper now requires an object with a finite numericamountand a three-letter uppercasecurrency, and treats anything else as a shape mismatch: the field is withheld and the resultdegradedwhen it is optional, and acontract_violationwhen it is required, the same rule the other checked types follow. -
The release gate now verifies the
sql-reportingexample instead of failing on it. It ran
archstone verifyon every example with only a mock URL in the environment, so the 0.31.0
release stopped atisolation not verified: negative identity did not resolve to any claims.
The gate now passes--identity-mapto any example that ships anidentity-map.json. For an
example with asqlbinding, it builds a database from the example'sfixture.sqlin the
job's Postgres and connects as the fixture's runtime role, through the variable the binding's
dsnnames. If no database is configured, that example's verify fails rather than being
skipped. Pull requests now run the same per-example build and verify against the workspace
build, so an example that needs input the gate does not supply fails on its PR, not at
release time (#162).
v0.29.0
Added
web-page, an Experimental output-only semantic type, checked against origins the binding
declares. A link field typedstringcan carry any URL, so a provider's data — or text a
listing's owner typed in — could have an assistant show a person a link to anywhere. A field
typedweb-pagemeans "the page where a person sees this resource on the provider's own
site", and a binding declares where such pages live with a neworigins: { pages: [...] }
key (a sibling ofresponse:/extract:; each entry a barehttpsorigin). The shared
response mapper in@archstone/emitter-support(applyResponseMapping) now checks every
web-pagevalue — top-level, inside nested resources, in every collection row, inonError
rows and inextract:fields — and fails closed: a value must be an absolutehttpsURL with
no userinfo whose normalised origin equals a declared one. A passing value is emitted as its
normalised href; a failing one is withheld (an optional field is omitted and the result is
degraded; a required field is a contract violation carried in_meta, never in
structuredContent).MappingResultand@archstone/agent'sExecuteResultgain an
optionalwithheld: string[]of field names — never values, and never added todegraded.
archstone verifyreports any withheld value red (value outside declared origins in: <fields>), and recording a contract keeps nothing when one is withheld. MCP lowers the type
to{ "type": "string", "format": "uri" }; thetools()envelopes carry input schemas only
and are unchanged; extraction refuses it;archstone initnever infers it.applyadds six
named rules:web-page-in-input,web-page-no-origins,web-page-needs-mappingand
origins-malformed(errors),origins-unusedandweb-page-required-in-collection
(warnings). Additive: an IR withoutoriginsis byte-identical andIR.versionstays"0".
Only typed fields are checked (a URL inside atextfield is untouched), relative links are
withheld rather than resolved, and nothing fetches the page. See the CDL specification §4.7.
Changed
-
Breaking: what a manifest that under-declares a nested resource now returns. A mapped
value contains only what its declared type names:- a nested resource value holds only the resource's declared fields, read by name, at every
level and in everycollection:row; other keys are dropped; money,partyanddate-rangeobjects keep only their declared keys. A primitive in their
place (price: 120) passes as before;- a
ref:slot takes a primitive id only. An object or array there is treated as absent,
never reduced to an id; - an object or array where a scalar is declared is treated as absent, top level included;
- nested values are capped at 32 resource levels; anything deeper is absent.
Absent values follow the required/optional rules at each level. When a nested value is missing
a required field, the nearest optional field above it is dropped and named indegraded
(host, orhost.agencydeeper down). With no optional field on the way up, the response is
a contract violation naming the deepest field (host.name; collection rows share a path, with
no index). Acollection:inside a resource is dropped as a whole when any row fails, never
shortened. This also applies to a requiredweb-pagewithheld inside a nested value. Until
this release that failed the whole response even under an optional parent.archstone verifyprints, under each capability,
nested keys not declared (dropped): host.phone, …, with names only and never values. The
line is informational and does not change the result.--jsoncarries the same list as
undeclaredNested.applyResponseMappingtakes an optional fourth argument,
{ collectUndeclared: true }, which addsundeclaredNestedto its result.Upgrading:
- Run
archstone verify, declare on the nested resource each key it lists that you need, and
re-run. - If a binding uses
@archstone/provider-sqland reads adateortimestampcolumn,
re-record its fixture: those columns' contract fingerprints change fromobjectto
string(see below). Fingerprints of every other binding are unaffected. - If you pass your own
pgPoolFactoryto@archstone/provider-sql, install its exported
jsonSafeTypeParseron that pool (types: { getTypeParser: jsonSafeTypeParser }).
Without it,datecolumns arrive as local-midnightDates and shift by the host's
time zone.
- a nested resource value holds only the resource's declared fields, read by name, at every
-
@archstone/provider-sqlreturns JSON-safe rows.pgreturneddateandtimestamp
columns as JSDateobjects. The stricter mapper treats a non-JSON object as absent, so a
required date field would fail the response. The default pool now parses adateas
Postgres's ownYYYY-MM-DDtext, which also removes the one-day shift pg's local-midnight
Datecaused east or west of UTC. Atimestamp(no time zone) is read as UTC and sent as an
ISO instant (2026-10-03T12:34:56.789Z); atimestamptzis an ISO instant in UTC. Their array
types are parsed the same way, andinfinityor a BC value stays as Postgres prints it. Any
value left with no JSON form isnull, which the mapper treats as absent:bytea,
interval, and other class instances. A required field read from such a column therefore
reports missing. Abigintbecomes its decimal string and a non-finite number becomesnull.
numericandint8already arrived as strings and are unchanged. Ajsonbkey named
__proto__is kept as ordinary data. The parser is exported asjsonSafeTypeParser. -
archstone adoptrecords the replayed response against the manifest's own resources. It
used to map that response with an empty resource registry, so every mapped field counted as
required and no field type was known. It now uses the manifest's resource definitions: an
absent optional field degrades (yellow) instead of failing the adoption, and aweb-page
value outside the declared origins stops it (red, field names only). -
A shape error on an enumerated value now names the value it refused.
@archstone/schema's
loader appends(got "<value>")(JSON-quoted, truncated to 80 characters) to anenum
failure in an authored manifest — e.g.
/capability/output/links/list must be equal to one of the allowed values (got "web-page").
Machine-emitted execution records are reported as before. -
contractViolationMessage(@archstone/emitter-support) takes an optional third argument,
the withheld field names. Without it, or with an empty list, the text is unchanged.
Security
@archstone/emitter-support: undeclared keys inside a nested value no longer reach the model
(#146). The response mapper projected only the top level of each row. A field whose type is a
resource (host: Host) was copied as the provider sent it, so every key inside it reached
structuredContent, the model-facing text, the embeddedexecute()result andverify's
replay, whetherHostdeclared it or not. The same was true of acollection:inside a
resource, of an object sent in aref:slot, of extra keys inside amoney,partyor
date-rangeobject, and of an object or array sent where a plain scalar (text,quantity, …)
is declared. Every value is now projected against its declared type at every level, so
ADR-0008's guarantee, that an undeclared provider field never reaches a model, holds at any
depth. Every earlier release is affected for an output resource that nests atype:resource,
acollection:field or a composite semantic type.
v0.28.0
Changed
- Breaking for a programmatic caller of
ensureConnectionorConnectionEntryin
@archstone/provider-sql.ensureConnectionno longer runs the D-9 check. It is now
synchronous and returns the pool with the DSN's cached refusal, if any ({ pool, entry, refusal? }, or{ pool: undefined, error }when no pool could be created). The eager check
thatserve,serve --httpandverifyrun at startup is the newcheckConnectionPrivileges.
ConnectionEntry.checkis replaced byrefusaland a per-server-and-databaseownershipmap.
invokeSqlno longer checks out a separate connection for the check on its first call.
Fixed
archstone serve,serve --httpandverifyno longer call a sql connection over-privileged
when the D-9 check could not reach a verdict. An unreachable database, a failed catalog query
or apg_postmaster_start_time()the runtime role cannot execute is now reported under
sql connection privilege check(s) could not complete:, apart from a genuine refusal, which
keepsover-privileged sql connection(s):. Both still exit 1.verify --jsonaddsrefused
andincompletearrays beside the existingerrors(@archstone/cli). A CI script that
matchesverify --json'serror === "sql_over_privileged"now sees
sql_privilege_check_incompletewhen no check reached a verdict and none refused; the exit code
is unchanged, anderrorsstill lists every finding, now refusals first. In
@archstone/provider-sql,checkConnectionPrivilegesmarks a result that reached no verdict
withincomplete: true(additive), and its no-verdict error now reads
connection privilege check failed (…)instead ofover-privileged connection check failed (…).
Security
- A
sqlDSN re-pointed at another server kept the over-privileged verdict of the first one.
@archstone/provider-sqlran the ADR-0012 D-9 role and ownership checks once per DSN and
cached the result for the life of the process, so a long-livedserve --httpor embedded
execute()kept serving after a logical-replication failover, a blue/green cut-over or a proxy
re-pointed behind the same DSN, onto a server where the role could be a superuser, hold
BYPASSRLSor own what it reads. Now every transaction reads the role'srolsuperand
rolbypassrls, the server'spg_postmaster_start_time()and the database oid before any claim
is set. The ownership check runs again the first time a transaction lands on a server and
database it has not judged. A refusal reached mid-life refuses every later call on that DSN
until restart, as one at startup does. A read that fails, for example because the runtime role
cannot executepg_postmaster_start_time(), fails the call withquery failed (…)and caches
nothing; atserveandverifystartup it stops the process. Not covered: an
ALTER … OWNER TOor a newGRANTon the same server and database while the process runs
(ADR-0012 R-8); restart long-lived processes after one.
v0.27.2
Fixed
- A connection pool that could not be created no longer prints its raw error at
serve/verify
startup. The ADR-0012 D-9 startup check echoed the pool factory's (or thepgPool
constructor's) own message unscrubbed, and that message can carry the DSN and its password.
ensureConnectionin@archstone/provider-sqlno longer throws: a pool that cannot be created
is a failed check, reported through the same path as every other driver failure — the error
code only, with the DSN and its password scrubbed from the one stderr line — and is not cached,
so the next call retries. The string for this case is nowpool creation failed (…)at startup
and at invocation; invocation previously saidpool checkout failed (…).
Terminal hygiene: the unscrubbed message never reached the model.
Security
@archstone/provider-sqlcould echo a literal DSN to the model. When a binding'sdsnwas
not a${VAR}reference,invokeSqlused the DSN itself, password included, where the env var
name belongs, and the over-privileged connection refusals (rolsuper,rolbypassrls, an owned
relation) returned it to the caller.applyalready refuses that shape, so this was reachable
only past it. NowinvokeSqlrefuses a non-${VAR}dsn before any connection is used, with an
error that names no part of it, and the refusals from the exportedensureConnectionname a
DSN that is not an env var name as(unnamed dsn).
v0.27.1
Fixed
- Published packages pointed
repository.urlat the old GitHub owner. The repository now lives at
NousVigil/archstone, and npm rejects a provenance-signed publish whosepackage.json
repository.urldisagrees with the repository that built it (E422), so 0.27.0 could not be
published. All nine packages now namehttps://github.com/NousVigil/archstone.git. - Links to the repository in the package READMEs,
server.json,SECURITY.md,SUPPORT.md,
CONTRIBUTING.mdand the issue-template config now point atNousVigil/archstone. - The MCP Registry name moves to
io.github.NousVigil/archstone. The registry verifies a
namespace against the GitHub owner publishing it, which is nowNousVigil.mcpNamein
@archstone/cliandnameinserver.jsonchange together. Clients that pinned the old
io.github.Archstone-Romania/archstoneentry (last published at 0.26.0) must switch to the new name.
v0.26.0
Added
-
Two CI checks on the changelog, for contributors (
.github/workflows/ci.yml). Every pull
request now either adds achangelog.d/fragment (or a line toCHANGELOG.md) or carries a
Changelog: none — <reason>line in a commit message or its description
(changelog entry or waiver,scripts/check-changelog-entry.mjs); and no pull request may
change a released## [x.y.z]section unless it declares
Changelog-correction: <x.y.z> — <reason>(released changelog sections are unchanged,
scripts/check-changelog-history.mjs). The second catches a silent failure: a branch that wrote
under[Unreleased]before a release, rebased after it, has its entries reattached under the
released heading by git with no conflict, so a shipped version appears to contain later work
and the next release's notes lack it. SeeCONTRIBUTING.md→ "The changelog". No change to
any published package. -
archstone init: an OpenAPI list ofoneOf[success, error]rows now maps onto
response.onError(@archstone/init, ADD-12 §8.1 / §8.4 item 1). When a list's items are a
oneOfof two object branches and exactly one branch declares exactly one property with a
scalarconst, that branch is the error row: the binding gets
onError: { errorResource: <Success>Error, when: { path: $.<property>, equals: <const> } },
plus amap:only for a field whose source is not$.code/$.message, and a
<Success>Errorresource with the fieldscodeandmessageis emitted beside the success
resource.codereads from the error branch'scode, else from the discriminator itself;
messagefrom itsmessage, else from its one remaining plain string property. Such an
operation used to be skipped asunsupported-composition. Every other two-branchoneOfis
still refused, now under a reason code naming what is missing —oneof-too-many-branches,
oneof-no-discriminator,oneof-non-object-branch,oneof-error-fields-unresolved— and the
accepted form anywhere but the items of the collection being mapped (the response root, a
single-object property, a nested list, or when the root locus is chosen) is refused as
oneof-outside-collectionrather than flattened into its success shape.anyOf, a
discriminatorkeyword, and the nullability idiom (oneOf: [X, {type: 'null'}]) behave
exactly as before. -
Release preparecomputes the version by default (bump: auto,
scripts/classify-bump.mjs). It classifies the commits since the lastv*tag by Conventional
Commits — breaking, thenfeat, then everything else, counting every commit listed in a
squash body — and names the commits that decided the bump in the run summary. Before 1.0 a
breaking change bumps the minor, so a computed default never produces 1.0.0;patch/minor/
majorand an explicitversionstill override it. Maintainer tooling only.
v0.25.0
Fixed
-
@archstone/provider-sqlwas left out of the release pipeline.providers/sqlis
private: false, butrelease.yml's hardcoded stamp-assert and publish loops did not list
it. So the 0.24.0 stamp left it at 0.23.0, and CI never published it (its 0.23.0 and 0.24.0
on npm were published by hand). It is now stamped in lockstep with the other packages.
release.ymlpublishes it after the packages it depends on (@archstone/compiler,
@archstone/emitter-support) and before its dependents (@archstone/runtime,
@archstone/cli). The package counts inrelease.yml,release-prepare.yml,
release-tag.ymlandCONTRIBUTING.mdnow read nine. -
@archstone/agent's"openai"format mixed two different OpenAI APIs' shapes
(archstone#89).tools("openai")
emitted the Chat Completions tool shape ({type, function:{name, description, parameters}}),
whileextractor(...,"openai").structuredOutputemitted the Responses API'stext.format
shape ({type, name, schema, strict}) — oneExtractorhanded a caller halves from two
APIs that do not accept each other's shapes.ToolFormatnow has two explicit values:
"openai-chat"(Chat Completions: nested tool shape,response_format.json_schemafor
structured output) and"openai-responses"(Responses API: flat tool shape with
strict: false,text.formatfor structured output).strict: falseis unchanged on both
— seeextract.ts's header for why."openai"remains a legal,@deprecatedToolFormat
value and forwards to"openai-chat"on both axes.
Changed
-
Breaking, on the deprecated
"openai"alias's structured-output axis only
(archstone#89):
extractor(resource, "openai").structuredOutputnow returns the Chat Completions
response_format.json_schemashape ({type, json_schema:{name, schema, strict}}) instead of
the Responses API's flattext.formatshape it returned before.tools("openai")is
unchanged — byte-identical totools("openai-chat"), as it was to pre-#89tools("openai").
A caller relying onextractor(..., "openai").structuredOutput's previous (Responses) shape
must switch to"openai-responses"explicitly; a caller who only usedtools("openai")is
unaffected.OpenAIToolDefandOpenAIStructuredOutputare kept as@deprecatedtype
aliases of the renamedOpenAIChatToolDefandOpenAIResponsesStructuredOutputso existing
type-level imports do not break. -
Changelog entries now go in
changelog.d/, one file per change, instead of in
CHANGELOG.md. Before, every PR added its entry at the top of the same[Unreleased]
list, so almost every merge put the other open PRs in conflict.Release preparenow folds
changelog.d/<slug>.<category>.mdfiles into the version section and deletes them, and
Release tagrefuses a fragment merged after that step. CI also runs the release-script
tests (node --test scripts/*.test.mjs) on every PR. Before, they ran only inside the
release workflows, which is why theproviders/sqlomission surfaced at release time.
Seechangelog.d/README.md.
v0.24.0
Added
- Row-level errors in a
response:collection (@archstone/schema,@archstone/compiler,
@archstone/emitter-support, ADD-12 §8.1, #81) — aresponse.onErrorblock names a second,
error-shaped resource and awhendiscriminator (path+equals/exists) that classifies
each collection item before the success mapping runs. A matching row is mapped against
errorResourceand tagged$row: "error"; every other row is mapped againstresource
exactly as before, tagged$row: "ok", required fields enforced in full — a successful row is
never loosened because another row in the same call failed. A row matching neither shape and
missing a required field is a per-row violation, named and dropped from the returned array
without implicating any other row. The whole-responsecontract_violationnow fires only when
a non-empty collection has zero usable rows.outputSchemafor the mapped field advertises
items: { oneOf: [<success, tagged>, <error, tagged>] }whenonErroris declared. - Scalar array fields outside the collection, via
extract:(@archstone/emitter-support,
ADD-12 §8.2, #82) — an output field declared as a List (list: <semantic type>) may now be
populated byextract:: every JSONPath match becomes an array item instead of only the first,
and an empty match set is OK, not DEGRADED.archstone adopt'splanAdoptionnow offers an
observed root-level (or otherwise outside-collection) array of one scalar type as adoptable;
an observed array of objects is refused with a new, distinct reason
(array-of-objects-unresolved), naming that it needs a declared row/resource shape core #49
has not ratified yet. - Data-dependent array shapes no longer flap in
archstone verify's drift report
(@archstone/compiler, ADD-12 §8.3, #83) —diffShapenow treats an array's element-level
shape as observed-so-far: an array that goes from populated to empty no longer reports its
element fields asremoved, and one that goes from empty to populated no longer reports them
asadded. A genuinely new element variant (e.g. a declaredonErrorerror-row shape
appearing for the first time) still reports asadded, never folded intoretyped; a field
genuinely dropped while the array stays populated is still reported asremoved.