Commit e9fcd6b
feat(spec)!: duration-shaped number keys carry their unit in the key name — no-baseline gate + seven ADR-0087 renames (timeoutMs, ttlSeconds/ttlMs, *TimeoutSeconds) (#15626)
* wip(spec): duration-unit-keys gate draft (#14478)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the seven duration keys, conversions, readers, pins (#14478)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): regenerate artifacts, i18n bundles, remaining fixtures (#14478)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): strip issue ids from tombstones, declare the gate population, regen docs (#14478)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* fix(driver-turso): the DriverOptions door pin writes timeoutMs (#14478)
`turso-driver-options-door.test.ts` builds a `Parameters<TursoDriver['update']>[3]`
literal — that argument IS `DriverOptions`, so the `timeout` key it wrote is the
one renamed to `timeoutMs` here, not a driver-local key. Same magnitude
(milliseconds), no value conversion. `TursoDriverConfig.timeout` in
`turso-driver.ts` and the `timeout` in `src/spec/turso.zod.ts` are a different
key on the driver's own connection schema and stay as they are.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate liveness state counts on the merged tree (#14478)
`packages/spec/liveness/state-counts.md` is routed to `merge=os-regen`, so the
merge of origin/main took it without a text merge and left it owing a
regeneration. Regenerated with `pnpm --filter @objectstack/spec
gen:liveness-counts` from the merged tree, which is the union of both sides:
main's `field` row (89 live / 3 planned -> 90 live / 2 planned) lands on top of
this branch's `hook` and `job` rows, and the totals follow.
No hand edits: the file is generator output.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(spec)!: declare the duration rule's two structural exemptions on the schema — a shared `EpochMs` instant and a `.meta({ externalVocabulary })` marker (#15814)
* feat(spec)!: the twelve api/ duration keys carry their unit in the key name (#15677, stack 2/6) (#15837)
* feat(spec)!: the fourteen kernel/ duration keys carry their unit in the key name (#15678, stack 3/6) (#15906)
* feat(spec): declare the two duration-rule exemptions on the schema (#15676)
Ruling B on #14478 exempts two structural classes from the duration-unit rule,
and is explicit that both are declared ON THE SCHEMA, never in a gate ledger.
This commit lands the declaration channels themselves:
- `EpochMs` (`packages/spec/src/shared/epoch.zod.ts`) — the shared
epoch-milliseconds instant. A key whose value IS this schema is an instant,
not a duration, and `check:duration-unit-keys` recognises that structurally.
- `.meta({ externalVocabulary: '<the standard>' })` — the marker a key carries
when it mirrors a name fixed outside this repo. It rides `z.toJSONSchema`
verbatim, the same channel `xRef` / `xExpression` already use.
Neither exemption is a pass on lying: a marked key still fails
`name-unit-contradicts-prose`, and an `EpochMs` key whose describe names a unit
other than milliseconds fails the new `instant-unit-contradicts-schema`. Both
classes stay visible in the census — `--list` marks them and the verdict line
counts them.
The gate also now reads `description` out of `.meta()`. Without it, moving a
describe into `.meta({ description })` would take a key out of the population
silently — an exemption by blindness. Measured: one numeric key declares its
description that way today (`data/Field.precision`), naming no time unit, so
the reading adds no offender.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(spec)!: move the six epoch instants onto EpochMs and mark the external-vocabulary keys (#15676)
The two exemption classes ruling B declares, applied to the keys the gate lists.
Instants (exemption i) — all six now typed `EpochMs`; the four whose name was
bare are renamed to the `*At` instant convention, tombstoned with `retiredKey()`
and registered in `RETIRED_KEYS_BY_MAJOR[18]` plus one D3 semantic entry:
api/WebSocketEvent.timestamp -> occurredAt
api/SimplePresenceState.lastSeen -> lastSeenAt
kernel/KernelContext.startTime -> startedAt (+ TenantRuntimeContext)
kernel/HealthStatus.timestamp -> checkedAt
kernel/ServiceMetadata.registeredAt (already `*At`, schema only)
kernel/ScopeInfo.createdAt (already `*At`, schema only)
`*At` and not `*Ms`, measured rather than chosen: on this package's own
authorable surface all 51 distinct `*Ms` keys are durations and all 51 distinct
`*At` keys are instants, so spelling an instant `*Ms` would move it into the
family the rule exists to separate it from.
Semantic entries rather than D2 conversions because all four are
runtime-emitted — wire payloads, a host-constructed kernel context, an emitted
health report — so no conversion seam ever sees one. That is the disposition
`kernel/KernelContext:previewMode` already carries on one of these defs, and
what ruling B prescribes for a runtime-emitted key.
External-standard mirrors (exemption ii) — eleven keys marked, not thirteen.
Two of the thirteen the card attributed do not survive verification against
their own schema and are left for their directory cards; the PR body records the
evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(spec): publish the externalVocabulary standard on the reference page (#15676)
The published half of exemption (ii). A marked key keeps its bare name BECAUSE
an external standard fixes it, and that argument only reaches the reference-page
reader if the page names the standard — so the description cell now carries
"(unit per <the standard>)". Without it the exemption would leave exactly the
reader `check:duration-unit-keys` was filed for where the gate found them.
Also: `EpochMs` gains its type alias (the docs import-surface ratchet demands
one for every documented schema) and its ADR-0122 isomorphism pin.
Regenerated: json-schema.manifest/, authorable-surface/, api-surface/,
export-origins/, declaration-map/, content/docs/references/**.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the two duration-rule exemptions (#15676)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the derived artifacts and fix the consumers the tombstones caught (#15676)
- `src/contracts/startup-orchestrator.test.ts` built a `HealthStatus` with the
old `timestamp` key. The `retiredKey()` tombstone refused it at compile time
(`Type 'number' is not assignable to type 'undefined'`, 7 errors) — the
audible-removal property the tombstone exists for, working on the first
consumer it met.
- `type-alias-convention.pin.test.ts`: the pin count assertion follows the new
`Iso868`.
- Regenerated: export-origins/, declaration-map/, api-surface/ and the
`objectstack-api` skill reference index (one generated line, naming the new
`shared/epoch.zod.ts` module).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 12 api/ duration keys, tombstones on the old spellings (#15677)
The schema half of stack card 2/6. Gate reads 48 -> 36 with src/api/ at 0.
Readers, registry entries and regenerated artifacts follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers, ADR-0087 registrations, ledger row for the api/ renames (#15677)
12 retired-key entries, one D2 conversion
(api-endpoint-cache-ttl-to-cache-ttl-seconds — apis: is a stack collection) and
five semantic entries for the eleven runtime-emitted / construction-argument
keys. Readers moved in runtime, metadata, rest-adjacent tests, dogfood fixtures
and the showcase example; liveness/api.json carries the renamed row plus the
dead tombstone row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): tombstone refusal tests, alias retarget, regenerated artifacts (#15677)
Per-key refusal tests assert the prescription (code + rename text), not a bare
throw. Two readers the key-name grep missed and tsc/the tombstones caught: the
ApiEndpoint alias table (cacheTTL/ttl/cache retargeted onto cacheTtlSeconds —
an alias must point at a key the schema accepts) and the showcase endpoint
fixture in metadata-type-api-registration.test.ts.
Regenerated: authorable surface + defaults, reference docs, liveness
state-counts. skills/objectstack-api/SKILL.md carries the rename (governed;
net 0 lines, file and package both).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the twelve api/ duration renames (#15677)
@objectstack/spec minor with the BREAKING banner naming every renamed key, the
six adr-0087 ids registered, the retryAfter wire note, and the disposition
split (one D2 conversion, five semantic entries).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* fix(docs-audit): declare the conversion-replay exclusion kind (b) relied on incidentally (#15677)
An ADR-0087 conversion fixture copies a routable metadata kind verbatim, so an
`apis:` fixture carries `method:` beside `path:` — because that is what an
ApiEndpoint IS. Ruling A named conversions/registry.ts as the guard's target but
enforced it with requireMethodSignal, a content proxy that held only while no
conversion fixture carried a verb. This card's apis: conversion is the first that
does, and the live pin red exactly as designed.
The fixture is correct and stays. The exclusion moves to CONVERSION_REPLAY_FILE_RE,
which states the structural fact instead of testing a symptom, and three cases pin
the new guard as load-bearing rather than incidental in its turn.
NOT restricting kind (b) to packages/spec/src/api/**: that is the invariant the
live pin asserts, and enforcing it in the walk would make that pin true by
construction — a check that cannot fail.
Measured tail-neutral: the scan census is byte-identical to the base (17 route
sources, 12 call sites, 5 contract declarations, 78 tails, 61 reachable).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs: move the hand-written pages onto the renamed keys, and strip the issue ids (#15677)
Lap 1 regenerated content/docs/references/** but left the HAND-WRITTEN pages
teaching the old spellings. Three of them carried `os:check` blocks authoring
`cacheTtl`, so check:skill-examples was RED and lap 1 never ran it — it sits in
check:generated's "not run here" list and I did not run it separately.
cacheTtl -> cacheTtlSeconds: 14 occurrences on 13 lines, all the ApiEndpoint key.
retryAfter -> retryAfterSeconds: 14 occurrences, the ADR-0112 envelope field only.
Deliberately NOT swept, each verified rather than assumed:
- the HTTP `Retry-After` response header (6 locals over 4 sites) — RFC 9110,
a separate surface, and the thing the tombstone prose exists to protect;
- `retry_after` as a RetryStrategy ENUM VALUE (errors.zod.ts z.enum);
- `details.retry_after` on the wire, and the pre-existing
`details.retryAfterSeconds` the runtime really emits (endpoint-policy.ts).
Also strips `(#14478 ruling B)` from the twelve tombstone prescriptions THIS card
wrote: check:doc-authoring forbids an internal issue id in customer-facing spec
text (maintainer ruling 2026-08-12), and the campaign's own earlier tombstones
already comply. The version and the FROM -> TO mapping stay — those are the
durable references AGENTS.md requires. Measured: the gate read 4 findings on the
base and 16 on my head; it now reads the base's 4 again, so this PR adds none.
Those 4 are card 1/6's (PR #15814) and are not mine to touch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(spec): drop the internal issue id from the four epoch-instant tombstone prescriptions (#15676)
`check:doc-authoring` rule 3 forbids an internal issue id in customer-facing
spec text (maintainer ruling 2026-08-12). The four `retiredKey()` prescriptions
this stack card added each opened with a `(#14478 ruling B)` parenthetical — a
citation-shaped token that resolves to nothing for the author who meets it the
moment their key is refused, and which the generated reference pages publish
verbatim.
The parenthetical goes; the sentence keeps everything actionable it carried —
the FROM to TO rename, that the value is unchanged, and the
`SimplePresenceState.lastSeen` neighbour caveat — matching the shape the
campaign's already-compliant tombstones use (`hook.timeout`, `job.timeout`,
`DriverOptions.timeout`). The internal anchor is untouched in the adjacent `//`
and `/** */` comments, which are not customer-facing and were never findings.
`content/docs/references/**` regenerated with `pnpm --filter @objectstack/spec
gen:docs` — no generated artifact was hand-edited.
check:doc-authoring: 4 findings before, exit 0 after.
check:duration-unit-keys: unmoved — 48 offender(s) among 215 duration-shaped
numeric key(s), (6 declared `EpochMs` instant(s), 11 declared
`externalVocabulary` mirror(s)).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the reference page the merge deferred (#15677)
The merge with card 1/6's advanced tip (9605808) touched
api/websocket.zod.ts on both sides. The schema source auto-merged; the
generated content/docs/references/api/websocket.mdx is routed to merge=os-regen,
so the driver deferred it and the merge kept OUR side — silently dropping card
1/6's half. Regenerating from the merged tree is what repairs it, and it carries
both sides: their two stripped prescriptions land (issue-id occurrences 2 -> 0)
while my four renamed keys stay (6 -> 6).
Not hand-edited and not resolved by taking a side: the bytes come from
`pnpm --filter @objectstack/spec check:generated --fix` on the merged tree, and
the staged diff was read before committing (`git diff` reads clean over this
trap; only the staged diff shows it).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 14 kernel/ duration keys, tombstones on the old spellings (#15678)
The schema half of stack card 3/6. Gate reads 36 -> 22 with src/kernel/ at 0.
Readers, registrations and regenerated artifacts follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers and ADR-0087 registrations for the kernel/ renames (#15678)
14 retired-key entries and five semantic entries. No D2 conversion on this
card: none of the twelve defs is a stack collection member or a stored
sys_metadata row (stack.zod.ts declares no eventBus / startup / plugin-security
root), so the conversion chain has no seam that would see one. Readers moved in
core's health monitor, the kernel and contracts test suites, and the
hand-written lifecycle protocol page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): tombstone refusal tests and regenerated reference pages (#15678)
Per-key refusal tests assert the prescription (issue code + rename text), not a
bare throw, plus acceptance pins at the same magnitudes and defaults. Two
deliberate NEGATIVE controls: RuntimeConfig.resourceLimits.timeout names no unit
so it stays bare, and EventSourcingConfig.snapshotRetention is a count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the fourteen kernel/ duration renames (#15678)
@objectstack/spec minor + @objectstack/core patch, BREAKING banner naming every
renamed key, adr-0087: registered with the five semantic ids. Documents the two
unit collisions the rename removes (responseTime hours vs ms; uptime ms vs the
seconds-valued GET /health) and the three keys deliberately left bare.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): correct why `RuntimeConfig.resourceLimits.timeout` is left bare (#15678)
The changeset stated the key "names no unit anywhere in its prose". That is
false and it would have published into the CHANGELOG: the JSDoc directly above
the key (plugin-security-advanced.zod.ts:291-293) says "Execution timeout in
milliseconds". Its `.describe('Maximum execution time')` names none.
The accurate reason is stronger: `check:duration-unit-keys` reads `.describe()`
and `.meta({ description })` and does NOT read JSDoc, so this key's unit sits in
a channel the gate cannot see. The gate therefore lists it among the
duration-shaped keys but deliberately does not judge it — neither an offender
nor an exemption. That channel gap is tracked in #15939.
The same false clause appeared in three more places on this branch and is
corrected in all of them: the ADR-0087 semantic migration entry (its `reason`
publishes into the generated upgrade guide), its generated mirror in
registry.ts (via gen:migration-registry, never hand-edited), and the comment
above the pin test.
Prose only. No key renamed, no schema, `.describe()`, JSDoc, tombstone or pin
assertion touched; the key stays bare and stays out of the rename.
`check:duration-unit-keys` still reads 22 offender(s) among 215 duration-shaped
numeric key(s) in 805 source file(s), `kernel/` at 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the os-regen artifacts on the merged tree
Discharges the deferral the merge commit recorded. `check:generated` proved
exactly one artifact set stale (content/docs/references/**) after the spec
build re-derived authorable-surface/kernel.json, so only that one was
regenerated. Both files re-derive this card's [RETIRED] rows on top of the
trunk's side; the trunk's own renames (checkedAt, startedAt) survive in both.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(spec)!: the fifteen system/ duration keys carry their unit in the key name [#14478 stack 4/6] (#15938)
* feat(spec): widen check:duration-unit-keys to every workspace package's zod schemas, and convert the one turso offender it finds (#16022)
* feat(spec): declare the two duration-rule exemptions on the schema (#15676)
Ruling B on #14478 exempts two structural classes from the duration-unit rule,
and is explicit that both are declared ON THE SCHEMA, never in a gate ledger.
This commit lands the declaration channels themselves:
- `EpochMs` (`packages/spec/src/shared/epoch.zod.ts`) — the shared
epoch-milliseconds instant. A key whose value IS this schema is an instant,
not a duration, and `check:duration-unit-keys` recognises that structurally.
- `.meta({ externalVocabulary: '<the standard>' })` — the marker a key carries
when it mirrors a name fixed outside this repo. It rides `z.toJSONSchema`
verbatim, the same channel `xRef` / `xExpression` already use.
Neither exemption is a pass on lying: a marked key still fails
`name-unit-contradicts-prose`, and an `EpochMs` key whose describe names a unit
other than milliseconds fails the new `instant-unit-contradicts-schema`. Both
classes stay visible in the census — `--list` marks them and the verdict line
counts them.
The gate also now reads `description` out of `.meta()`. Without it, moving a
describe into `.meta({ description })` would take a key out of the population
silently — an exemption by blindness. Measured: one numeric key declares its
description that way today (`data/Field.precision`), naming no time unit, so
the reading adds no offender.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(spec)!: move the six epoch instants onto EpochMs and mark the external-vocabulary keys (#15676)
The two exemption classes ruling B declares, applied to the keys the gate lists.
Instants (exemption i) — all six now typed `EpochMs`; the four whose name was
bare are renamed to the `*At` instant convention, tombstoned with `retiredKey()`
and registered in `RETIRED_KEYS_BY_MAJOR[18]` plus one D3 semantic entry:
api/WebSocketEvent.timestamp -> occurredAt
api/SimplePresenceState.lastSeen -> lastSeenAt
kernel/KernelContext.startTime -> startedAt (+ TenantRuntimeContext)
kernel/HealthStatus.timestamp -> checkedAt
kernel/ServiceMetadata.registeredAt (already `*At`, schema only)
kernel/ScopeInfo.createdAt (already `*At`, schema only)
`*At` and not `*Ms`, measured rather than chosen: on this package's own
authorable surface all 51 distinct `*Ms` keys are durations and all 51 distinct
`*At` keys are instants, so spelling an instant `*Ms` would move it into the
family the rule exists to separate it from.
Semantic entries rather than D2 conversions because all four are
runtime-emitted — wire payloads, a host-constructed kernel context, an emitted
health report — so no conversion seam ever sees one. That is the disposition
`kernel/KernelContext:previewMode` already carries on one of these defs, and
what ruling B prescribes for a runtime-emitted key.
External-standard mirrors (exemption ii) — eleven keys marked, not thirteen.
Two of the thirteen the card attributed do not survive verification against
their own schema and are left for their directory cards; the PR body records the
evidence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(spec): publish the externalVocabulary standard on the reference page (#15676)
The published half of exemption (ii). A marked key keeps its bare name BECAUSE
an external standard fixes it, and that argument only reaches the reference-page
reader if the page names the standard — so the description cell now carries
"(unit per <the standard>)". Without it the exemption would leave exactly the
reader `check:duration-unit-keys` was filed for where the gate found them.
Also: `EpochMs` gains its type alias (the docs import-surface ratchet demands
one for every documented schema) and its ADR-0122 isomorphism pin.
Regenerated: json-schema.manifest/, authorable-surface/, api-surface/,
export-origins/, declaration-map/, content/docs/references/**.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the two duration-rule exemptions (#15676)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the derived artifacts and fix the consumers the tombstones caught (#15676)
- `src/contracts/startup-orchestrator.test.ts` built a `HealthStatus` with the
old `timestamp` key. The `retiredKey()` tombstone refused it at compile time
(`Type 'number' is not assignable to type 'undefined'`, 7 errors) — the
audible-removal property the tombstone exists for, working on the first
consumer it met.
- `type-alias-convention.pin.test.ts`: the pin count assertion follows the new
`Iso868`.
- Regenerated: export-origins/, declaration-map/, api-surface/ and the
`objectstack-api` skill reference index (one generated line, naming the new
`shared/epoch.zod.ts` module).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 12 api/ duration keys, tombstones on the old spellings (#15677)
The schema half of stack card 2/6. Gate reads 48 -> 36 with src/api/ at 0.
Readers, registry entries and regenerated artifacts follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers, ADR-0087 registrations, ledger row for the api/ renames (#15677)
12 retired-key entries, one D2 conversion
(api-endpoint-cache-ttl-to-cache-ttl-seconds — apis: is a stack collection) and
five semantic entries for the eleven runtime-emitted / construction-argument
keys. Readers moved in runtime, metadata, rest-adjacent tests, dogfood fixtures
and the showcase example; liveness/api.json carries the renamed row plus the
dead tombstone row.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): tombstone refusal tests, alias retarget, regenerated artifacts (#15677)
Per-key refusal tests assert the prescription (code + rename text), not a bare
throw. Two readers the key-name grep missed and tsc/the tombstones caught: the
ApiEndpoint alias table (cacheTTL/ttl/cache retargeted onto cacheTtlSeconds —
an alias must point at a key the schema accepts) and the showcase endpoint
fixture in metadata-type-api-registration.test.ts.
Regenerated: authorable surface + defaults, reference docs, liveness
state-counts. skills/objectstack-api/SKILL.md carries the rename (governed;
net 0 lines, file and package both).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the twelve api/ duration renames (#15677)
@objectstack/spec minor with the BREAKING banner naming every renamed key, the
six adr-0087 ids registered, the retryAfter wire note, and the disposition
split (one D2 conversion, five semantic entries).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* fix(docs-audit): declare the conversion-replay exclusion kind (b) relied on incidentally (#15677)
An ADR-0087 conversion fixture copies a routable metadata kind verbatim, so an
`apis:` fixture carries `method:` beside `path:` — because that is what an
ApiEndpoint IS. Ruling A named conversions/registry.ts as the guard's target but
enforced it with requireMethodSignal, a content proxy that held only while no
conversion fixture carried a verb. This card's apis: conversion is the first that
does, and the live pin red exactly as designed.
The fixture is correct and stays. The exclusion moves to CONVERSION_REPLAY_FILE_RE,
which states the structural fact instead of testing a symptom, and three cases pin
the new guard as load-bearing rather than incidental in its turn.
NOT restricting kind (b) to packages/spec/src/api/**: that is the invariant the
live pin asserts, and enforcing it in the walk would make that pin true by
construction — a check that cannot fail.
Measured tail-neutral: the scan census is byte-identical to the base (17 route
sources, 12 call sites, 5 contract declarations, 78 tails, 61 reachable).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs: move the hand-written pages onto the renamed keys, and strip the issue ids (#15677)
Lap 1 regenerated content/docs/references/** but left the HAND-WRITTEN pages
teaching the old spellings. Three of them carried `os:check` blocks authoring
`cacheTtl`, so check:skill-examples was RED and lap 1 never ran it — it sits in
check:generated's "not run here" list and I did not run it separately.
cacheTtl -> cacheTtlSeconds: 14 occurrences on 13 lines, all the ApiEndpoint key.
retryAfter -> retryAfterSeconds: 14 occurrences, the ADR-0112 envelope field only.
Deliberately NOT swept, each verified rather than assumed:
- the HTTP `Retry-After` response header (6 locals over 4 sites) — RFC 9110,
a separate surface, and the thing the tombstone prose exists to protect;
- `retry_after` as a RetryStrategy ENUM VALUE (errors.zod.ts z.enum);
- `details.retry_after` on the wire, and the pre-existing
`details.retryAfterSeconds` the runtime really emits (endpoint-policy.ts).
Also strips `(#14478 ruling B)` from the twelve tombstone prescriptions THIS card
wrote: check:doc-authoring forbids an internal issue id in customer-facing spec
text (maintainer ruling 2026-08-12), and the campaign's own earlier tombstones
already comply. The version and the FROM -> TO mapping stay — those are the
durable references AGENTS.md requires. Measured: the gate read 4 findings on the
base and 16 on my head; it now reads the base's 4 again, so this PR adds none.
Those 4 are card 1/6's (PR #15814) and are not mine to touch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(spec): drop the internal issue id from the four epoch-instant tombstone prescriptions (#15676)
`check:doc-authoring` rule 3 forbids an internal issue id in customer-facing
spec text (maintainer ruling 2026-08-12). The four `retiredKey()` prescriptions
this stack card added each opened with a `(#14478 ruling B)` parenthetical — a
citation-shaped token that resolves to nothing for the author who meets it the
moment their key is refused, and which the generated reference pages publish
verbatim.
The parenthetical goes; the sentence keeps everything actionable it carried —
the FROM to TO rename, that the value is unchanged, and the
`SimplePresenceState.lastSeen` neighbour caveat — matching the shape the
campaign's already-compliant tombstones use (`hook.timeout`, `job.timeout`,
`DriverOptions.timeout`). The internal anchor is untouched in the adjacent `//`
and `/** */` comments, which are not customer-facing and were never findings.
`content/docs/references/**` regenerated with `pnpm --filter @objectstack/spec
gen:docs` — no generated artifact was hand-edited.
check:doc-authoring: 4 findings before, exit 0 after.
check:duration-unit-keys: unmoved — 48 offender(s) among 215 duration-shaped
numeric key(s), (6 declared `EpochMs` instant(s), 11 declared
`externalVocabulary` mirror(s)).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the reference page the merge deferred (#15677)
The merge with card 1/6's advanced tip (9605808) touched
api/websocket.zod.ts on both sides. The schema source auto-merged; the
generated content/docs/references/api/websocket.mdx is routed to merge=os-regen,
so the driver deferred it and the merge kept OUR side — silently dropping card
1/6's half. Regenerating from the merged tree is what repairs it, and it carries
both sides: their two stripped prescriptions land (issue-id occurrences 2 -> 0)
while my four renamed keys stay (6 -> 6).
Not hand-edited and not resolved by taking a side: the bytes come from
`pnpm --filter @objectstack/spec check:generated --fix` on the merged tree, and
the staged diff was read before committing (`git diff` reads clean over this
trap; only the staged diff shows it).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 14 kernel/ duration keys, tombstones on the old spellings (#15678)
The schema half of stack card 3/6. Gate reads 36 -> 22 with src/kernel/ at 0.
Readers, registrations and regenerated artifacts follow.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers and ADR-0087 registrations for the kernel/ renames (#15678)
14 retired-key entries and five semantic entries. No D2 conversion on this
card: none of the twelve defs is a stack collection member or a stored
sys_metadata row (stack.zod.ts declares no eventBus / startup / plugin-security
root), so the conversion chain has no seam that would see one. Readers moved in
core's health monitor, the kernel and contracts test suites, and the
hand-written lifecycle protocol page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): tombstone refusal tests and regenerated reference pages (#15678)
Per-key refusal tests assert the prescription (issue code + rename text), not a
bare throw, plus acceptance pins at the same magnitudes and defaults. Two
deliberate NEGATIVE controls: RuntimeConfig.resourceLimits.timeout names no unit
so it stays bare, and EventSourcingConfig.snapshotRetention is a count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the fourteen kernel/ duration renames (#15678)
@objectstack/spec minor + @objectstack/core patch, BREAKING banner naming every
renamed key, adr-0087: registered with the five semantic ids. Documents the two
unit collisions the rename removes (responseTime hours vs ms; uptime ms vs the
seconds-valued GET /health) and the three keys deliberately left bare.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 15 system/ duration keys, tombstones on the old spellings (#15679)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers, tombstone refusal tests and ADR-0087 registrations for the system/ renames (#15679)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): regenerated artifacts and reference pages for the system/ renames (#15679)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* test(spec): fix the three new metrics pins — required label, and assert the prescription explains the non-mechanical name (#15679)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the fifteen system/ duration renames (#15679)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): rename the 7 data/ ui/ ai/ integration/ duration keys, tombstones on the old spellings (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): readers, ADR-0087 registrations and regenerated artifacts for the 7 renames (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* test(spec): tombstone refusal pins for the 7 renames; narrow credential derivation so a retirement tombstone is not read as a secret (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(i18n): regenerate the metadata-form bundles for the dashboard key rename, translations carried over (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): the seven data/ ui/ ai/ integration/ duration renames, plus the auto-arm sibling (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(changeset): declare the ADR-0087 disposition on the driver-memory rename (#15680)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* wip(spec): widen check:duration-unit-keys to every workspace package's src (#15682)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* feat(driver-turso)!: rename the published config timeout to timeoutMs, tombstone on the old spelling (#15682)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* fix(scripts): declare the three workspace-enumerator exports the widened gate imports (#15682)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* fix(service-datasource): read the canonical turso `config.timeoutMs` at the shared libSQL seam
`buildTursoDriverConfig` still consulted `config.timeout` after that authored
key was renamed to `timeoutMs` and tombstoned, so a datasource authored the
canonical way had its timeout silently dropped. `TursoConfigSource.config` is an
untyped string-keyed bag, so tsc could not see the rename through it, and the
covering test authored the retired spelling at all three of its sites and stayed
green over the defect.
The reader now reads `config.timeoutMs`; the driver key it lands on stays
`timeout` (published-but-inert, must not be ratified by a rename). No fallback
arm for the retired spelling — the sqlite `filename` and mongo `url` arms in
`default-datasource-driver-factory.ts` set that precedent, and both authoring
and stored-row rehydration deliver the canonical key already.
The covering test moves to the canonical spelling and gains contract-derived
cases that read the schema's own tombstones, so they hold for the next rename
without being edited. The two sibling pins that author the same spec move with
it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* docs(spec): correct the exclusion pin's stale comment to the fixture it actually builds (#15682)
The behavioural exclusion pin in `check-duration-unit-keys.ts` was described
against an earlier, smaller fixture: "asserts the walk finds ONE file. Seven
offenders on disk, one in the verdict." The code below it writes 8 excluded
shapes plus 2 real source files and asserts `walked.files === 2 &&
walked.findings.length === 2` — three numbers the prose got wrong.
The code is right and the comment was stale, so only the comment moves. The
assertion, the `excluded` list and the second `expect` ("an excluded file is
not merely unjudged, it is never read") are untouched; the wider fixture is the
point of the pin and is deliberately kept.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): the turso ADR-0087 disposition reads `registered` against main (#14478)
Card 6/6's `already-registered` was honest at its own base, where card 5/6 had
already registered `turso-config-timeout-to-timeout-ms`. Measured against `main`,
this branch's combined diff is what ADDS that registration, so the honest
disposition at this level is `registered` — which is what
check-adr-0087-registration.mjs --base origin/main names.
The explanation moves to a second comment on its own line: every
whitespace-separated token after the id on the marker line itself is parsed as
another id.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
* chore(spec): regenerate the deferred artifacts on the merged tree (#14478)
Discharges the os-regen deferral the merge commit recorded. Regenerated in the
order the generators require — `gen:migration-registry` writes a source file, so
the spec is rebuilt before `gen:api-surface` reads `dist/*.d.ts`:
gen:migration-registry, build, gen:api-surface, gen:export-origins,
gen:declaration-map, gen:docs
`check:generated` then reports all 15 generated artifacts up to date.
The ledger keeps every row from both sides and invents none: 356 entry files at
the merge base, +27 from main, +80 from this branch, 463 in the result — set
equality, not just a matching total. `check:migration-registry` reads the same
population back as 186 semantic, 161 retired-key, 116 retired-def.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
---------
Co-authored-by: os-dev <sales@objectstack.ai>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>1 parent 932acc3 commit e9fcd6b
325 files changed
Lines changed: 9109 additions & 776 deletions
File tree
- .changeset
- .github/workflows
- content/docs
- api
- automation
- getting-started
- protocol/kernel
- references
- ai
- api
- data
- integration
- kernel
- system
- ui
- ui
- docs/qa/platform-checklist/areas
- examples/app-showcase/src
- automation/jobs
- system/apis
- packages
- cli/src
- commands
- utils
- core/src
- drivers
- driver-memory/src
- persistence
- driver-turso/src
- spec
- metadata
- src
- loaders
- objectql/src
- platform-objects/src/apps/translations
- qa/dogfood/test
- fixtures
- runtime/src
- services
- service-datasource/src
- __tests__
- service-job/src
- spec
- api-surface
- authorable-defaults
- authorable-surface
- declaration-map
- export-origins
- json-schema.manifest
- liveness
- scripts
- lib
- src
- ai
- api
- contracts
- conversions
- data
- driver
- integration
- kernel
- events
- migrations
- entries
- retired-keys
- semantic
- system
- ui
- scripts
- docs-audit
- skills
- objectstack-api
- references
- objectstack-data/references
- objectstack-ui/rules
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 138 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
0 commit comments