Skip to content

feat(spec)!: retire the connector triggers array — the ConnectorTrigger shape nothing registered, polled or received (#20287) - #20587

Merged
os-justin merged 14 commits into
mainfrom
claude/issue-20287-connector-triggers-retired
Sep 29, 2026
Merged

os-justin merged 14 commits into
mainfrom
claude/issue-20287-connector-triggers-retired

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Part of #20287

Clause-②: no (narrowing). The tombstone refuses triggers, which parses today. @objectstack/spec minor, with the BREAKING banner and the ADR-0087 disposition the ruling sets.

Retires connector.triggers (the ConnectorTrigger array) under ADR-0049, per the director's ruling on the card (letter B; ADR-0041 unchanged, no new ADR).

  • triggers is a retiredKey() tombstone on ConnectorBaseSchema. Its prescription names what works today: an api flow for an external event and a schedule flow for a scheduled pull, each calling the connector's action in a connector_action node.
  • The provider-bound refusal on triggers is deleted: the tombstone refuses every value first, so that rule could no longer be reached.
  • ConnectorTrigger leaves whole. The D2 connector-triggers-removed (step 18) carries retiredAfter 17.5.0, the label main carries after the 17.5.0 cut. The D3 is connector-triggers-retired.
  • The earlier triggers[].interval → intervalSeconds rename is absorbed (spec-property-retirement §0).
  • Ledger: the triggers rows collapse into one dead tombstone row. Generated files are regenerated.
  • metadata-core's per-entry window pins now state the window rule over the live registry, not a snapshot of its retiredAfter stamps. This first post-cut stamp broke the snapshot.

#20287 stays open for its action half.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1

…nnector-triggers-retired

Resolves packages/spec/src/migrations/registry.ts: main's side (step 18's
rationale as key-sorted fragments, conversionIds derived), with this branch's
hand-written rationale carried over as fragment edits — the connector pair
note in duration-keys-unit-in-key, the resilience fragment's tail, and a new
connector-triggers-retired fragment (order 47). Generated regions are main's
here and are regenerated in the next commit.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…ter merging main

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
… pending resilience note's 'rename is unaffected' sentence

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 26 documentable anchor(s). ⚠️ 13 changed file(s) yielded no anchor (packages/spec/api-surface/integration.json, packages/spec/authorable-surface/integration.json, packages/spec/declaration-map/integration.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/connectors.mdx (via billing_api (literal, a string literal in fixture))
  • content/docs/kernel/cluster.mdx (via RETIRED_DEFS_BY_MAJOR (symbol, a top-level const object))
  • content/docs/ui/forms.mdx (via new_lead (literal, a string literal in fixture))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via ConnectorTriggerSchema (symbol, a top-level const), DeclarativeConnectorEntrySchema (symbol, a top-level const))
  • content/docs/releases/v17/17-4.mdx (via intervalSeconds (literal, a string literal in apply; a string literal in summary))
  • content/docs/releases/v17/17-5.mdx (via retiredAfter (symbol, a field of const object connectorHealthAndTriggerDurationsUnitInKey; a field of const object connectorTriggersRemoved), retiredFromLoadPath (symbol, a field of const object connectorHealthAndTriggerDurationsUnitInKey; a field of const object connectorTriggersRemoved))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 13 changed file(s) yielded no anchor (packages/spec/api-surface/integration.json, packages/spec/authorable-surface/integration.json, packages/spec/declaration-map/integration.json, …) — pages documenting those are invisible to this run
  • 9 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 649df038ed80ceb4ebdfbad74882d4372ba1bff1 — the merge of head 4f8c62b3975236c86e079f64bff19dc1005b50f6 into base 1322cc72c96f9e80240c1fe0a7d12708f3b269b9, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 649df038ed80ceb4ebdfbad74882d4372ba1bff1 && git checkout 649df038ed80ceb4ebdfbad74882d4372ba1bff1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 4f8c62b3975236c86e079f64bff19dc1005b50f6 && git checkout -B drift-repro 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 && git merge --no-ff 4f8c62b3975236c86e079f64bff19dc1005b50f6

node scripts/docs-audit/affected-docs.mjs --json 1322cc72c96f9e80240c1fe0a7d12708f3b269b9

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8da27aa8a644105ecff2e9353031e3ac0d483943
Local-runs: none

Director seat (objectstack#12708, session_01AsCNgFBs8HCjwhyHQsFbx3), on the maintainer's ask 「契约复审」. Inputs: card #20287 (the stage-1 report 5880862266, the seat's 裁定与请示 5880969529, the director ruling 5881831013 — letter B on the maintainer's 「同意」 — the stage-2 claim 5881984829, the dev report 5884248207 at 298e735bdf, and the seat's ruling on its two questions 5884277442), the PR body, the 30-file net diff against the merge base c876a7426d (+1163 / −413), the check-runs on the head, and read-only git show / git grep at the head and on main. Nothing built, run or re-run; the dev's suite, gate and ablation figures are read, not this record's own. The head is the reported head plus a merge of origin/main c876a7426d (per the seat's ruling), the regenerated registry regions, and one changeset sentence; the PR-own diff is unchanged in substance.

① Derived judgments

  1. The ruling is delivered as ruled — right. Letter B (5881831013): retire the connector triggers family now, ADR-0041 untouched, no new ADR, the action half of the card left to its own stage. The diff touches no ADR and no governed path; ConnectorTrigger and its carrier key leave under ADR-0049 with the ADR-0087 kit; the card stays open (Part of #20287, the seat's question 1 → A) for actions.description / actions.outputSchema going live after objectui#11028 and the pin bump.
  2. The tombstone, on both carriers — right. triggers: retiredKey(TRIGGERS_RETIRED) sits on the private ConnectorBaseSchema that ConnectorSchema and DeclarativeConnectorEntrySchema both wrap, so defineConnector, stack.connectors[], the /meta door and registerConnector all meet it — in tsc (input type never) and at parse (invalid_type at path triggers, the prescription as the message). The schema is not .strict(), so a bare deletion would have been a silent strip (ADR-0104); the tombstone is the right instrument, as for the six sibling tombstones in the file. The prescription is in the house form (opens as the siblings do, removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove); closes with the os migrate meta --from 17 sentence the shared pin holds), names what actually starts work today — an api flow for an external event, a schedule flow for a scheduled pull, each calling the connector's action in a connector_action node — and covers an author still holding the pre-rename interval spelling. The key had no default, so no retired-default residue is owed. Pinned: the refusal with the prescription; every value refused, both interval spellings and [] included; the second carrier, a provider-bound instance, the /meta door and stack.connectors[], with controls; a well-formed connector grows no triggers property; the walked shape keeps the key so the ledger and authorable-surface rows stay reachable; tsc never at the authoring site.
  3. The provider-bound refusal deleted, not re-reasoned — right. ADR-0097 §5's DeclarativeConnectorEntrySchema rule refused triggers on a provider-bound instance with the reason that the provider derives them at boot; no provider ever derived a trigger. With the tombstone refusing every value on every carrier, the rule could only repeat the verdict with an untrue reason, so it left; connector-provider.test.ts now pins that a provider-bound instance meets the retirement prescription and that no issue anywhere says derives them from the upstream. The actions half of that rule stays as it was. ADR-0097's table row (a provider-bound entry must not author triggers) still holds in effect — refused by the tombstone rather than the §5 rule — and the ADR is untouched, as the health / webhooks retirement (feat(spec)!: retire the connector resilience family — health (probe + breaker), status and nested webhooks, sixteen keys nothing read (#20273) #20350) left it.
  4. The def leaves whole, and every public entry agrees — right. integration/ConnectorTrigger in RETIRED_DEFS_BY_MAJOR[18]; integration/Connector:triggers and integration/DeclarativeConnectorEntry:triggers in RETIRED_KEYS_BY_MAJOR[18]; the integration/ConnectorTrigger:interval row kept as the record of the bare spelling's retirement (the entry files match). api-surface, export-origins and declaration-map lose the ConnectorTrigger / ConnectorTriggerSchema pair, the JSON-schema manifest loses the def, the authorable surface loses the six ConnectorTrigger:* rows and marks the two carrier rows [RETIRED]; the reference page drops the ConnectorTrigger section and both nested tables and shows the carrier key as never [REMOVED]; the references index (1522 → 1521), the strictness ledger (5 → 4) and the type-alias pin count (780 → 779, with its receipt) move with it. Pinned: zero holders of any retired name on any public entry while the carriers survive; the integration barrel resolves without the schema. On main, git grep finds no import or authoring of ConnectorTrigger / connector.triggers outside packages/spec (the other triggers hits are webhook subscriptions and the engine's flow-trigger map); objectui at the pin dd3f7e1b holds one comment-only mention (clientValidation.ts:609), no import.
  5. D2 connector-triggers-removed — right. Step 18, retiredFromLoadPath: true, retiredAfter: '17.4.0' (the last published label, as conversions/types.ts requires), stripKeys(c, ['triggers'], …) over connectors[], one attributed notice per connector, idempotent and copy-on-write; stored connector rows through the rehydration seam. It strips and never writes a flow: a flow that runs would start work that never happened before, and the D3 entry connector-triggers-retired carries those three judgments (which triggers should now exist as flows; the cadence in seconds, since interval: 60000 once asked for sixteen hours; whether the external sender can sign the calls a signed api flow requires — the trigger-api arms a flow's inbound hook without a secret and accepts unsigned posts; ADR-0041's trigger-api acceptance criteria name a per-flow secret and HMAC verification #20529 rule the branch's earlier main merge brought). Pinned: a stored row converted losslessly; the strip with one notice, idempotent; the absorbed chain (a pre-rename interval trigger ends with the whole array gone, one notice, no rename); the D2 wired into the step-18 chain as retired, stamped, lossless; one D3 entry naming its D2, the chain and the two working shapes.
  6. The absorption — right, and the published id is accounted for. connector-health-and-trigger-durations-unit-in-key had lost its breaker half to the health removal (feat(spec)!: retire the connector resilience family — health (probe + breaker), status and nested webhooks, sixteen keys nothing read (#20273) #20350); with its trigger half absorbed here it had nothing left, so it leaves the table and step 18 under spec-property-retirement §0 (a rename followed by a strip of its container is unobservable; the disjoint-fixture contract cannot hold both), its never-released D3 connector-resilience-durations-unit-in-key deleted with it. The ABSORBED note states that the id was published in 17.4.0 (the tarball's retired-after.census.json, the changelog), that nothing outside the package named it, and that the chain replays only the ids a step lists — so a reader who greps it finds the note and the two removals, and a row holding either old spelling meets the removal that deletes its container. connector-resilience-keys-retirement.test.ts moves its control to a live sibling key and pins the rename's absence from the chain instead of an ordering against an absent id; connector.test.ts drops the two [#14478 stack 5/6] data/ · ui/ · ai/ · integration/: the 7 remaining duration keys carry their unit in the key name — ADR-0087 conversions with readers #15680 trigger-spelling tests with a pointer.
  7. The ledger and the checklist — right. The six triggers child rows collapse into one dead tombstone leaf (check:liveness refuses children under a non-container, the health precedent): connector dead 30 → 25, classified 60 → 55, regenerated; the README partition corrected. The platform-checklist negative item now says both webhooks and triggers are retired tombstones refused at parse, which is what the parse does.
  8. The seat's second ruling honoured. The pending .changeset/20273-connector-resilience-keys-retired.md ("the rename is unaffected") is not edited — that is the finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712 deliberate-correction class, a person's confirmation — and this PR's own changeset states the correction instead (the head's last commit). The maintainer may still prefer the edit itself; that needs their written confirmation on this PR and is not a blocker.
  9. Coverage. Read from the report, not re-run: spec local suite 574 files / 16877 passed + 1 todo, repo project 41 / 732 (+16 here), typecheck 0, 108 / 109 derived gates green with check:platform-checklist red on the base too (identity-auth.json twoFactor, neither file in this diff); four ablations (the tombstone line, the D2 strip, the api-flow shape in the prescription, the Connector:triggers registration) each red exactly the pins that name them, restores proven by blob; ESLint 0 / 0 on the 15 changed lintable files.

② Semver level

@objectstack/spec: minor with the BREAKING banner, a FROM → TO table (polling → schedule flow at the cadence in seconds; webhook → an api flow the sender can sign; the schema pair → no replacement), the one-line fix, Clause-②: no (narrowing) — right: the key parses today and is refused after, the def leaves the barrel, and the changeset says runtime behaviour is unchanged because nothing ever read the key. The ADR-0087 marker registers connector-triggers-removed and connector-triggers-retired, both new here; Check Changeset and check:adr-0087-registration are success on the head. The consumer population outside the repo is declared unmeasured, as the house form asks. Same level as the two earlier connector retirements in this step.

③ Boundary flags

  • CI on the head: 35 check runs, 33 success, 2 skipped (Console Pin Gate — no pin moved; Packed-tarball smoke, opt-in), 0 failure.
  • Merge: a driver-free git merge-tree --write-tree against main 7a1faf1a5d is clean (the head merged c876a7426d; the seat's dirty reading at 298e735bdf is answered).
  • Governed: none of the 30 paths; not Tier H. The retirement skill was followed, not edited.
  • State: draft, assignee os-justin (domain:spec seat 5, session_01Sfe5YjBLwB9J3y8fvm2xq1); the landing is the seat's, on this record. The card stays open for its action half.
  • Residue, noted by the dev and not this PR's: the index.ts module docblock of integration/ still lists webhooks and rate limiting; objectui's comment at clientValidation.ts:609 names two retired schemas; the 20273 changeset sentence (item 8 above).

Implemented-by: claude/issue-20287-connector-triggers-retired
Reviewed-by: session_01AsCNgFBs8HCjwhyHQsFbx3

VERDICT: PASS

…nnector-triggers-retired

Resolves packages/spec/liveness/README.md: main's realtime_subscription and
analytics_cube rows, this branch's connector row (the only row either side
changed in that hunk). step 18's rationale auto-merged beside main's
cube-member-inner-name-retired fragment (order 47); this branch's
connector-triggers-retired fragment moves to order 48, one past the highest
now present. Generated regions are regenerated in a following commit.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…0 label, and name the released resilience note the changeset corrects

main now carries the 17.5.0 label (the version-packages merge), and 17.5.0
still accepts `triggers`, so it is the last release the retirement follows.
The 20273 note was consumed into the 17.5.0 changelog entry; the correction
sentence names it by that release and its D2 id.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Director note · record superseded by the head move · 2026-09-29T07:54Z

Director seat (objectstack#12708, session_01AsCNgFBs8HCjwhyHQsFbx3). This seat's PASS 5884968904 is at 8da27aa8a6; the head is now dd7be6486e (a merge of main and the re-stamp of connector-triggers-removed to retiredAfter: '17.5.0', right after the 17.5.0 cut — the label main now carries, as conversions/types.ts requires). That record no longer covers the head.

Test Core (3/6) is red on dd7be6486e, and it is this PR's: packages/metadata-core/src/artifact-forward-conversion.test.ts fails three #20390 per-entry-window pins (lines 100, 496, 511). They are pinned against the LIVE registry — their own comment says "every retirement it carries is stamped retiredAfter 17.4.0 or earlier, so a 17.5.0 floor on a 17.5.0 runtime predates none of them" — and this entry is the first stamped 17.5.0, so a ^17.5.0 floor now opens its window (floor <= retiredAfter, the rule those pins state) and replayedRetirements carries 17.5.0 beside 17.4.0. The rule holds; the three pins need re-pinning to the registry this PR ships, in this PR.

Before the landing: the fix pushed, CI green, and a delta record at that head (the delta from 8da27aa8a6 is the two-file re-stamp plus the pin update). The owning seat may write it, or this seat will at the next check-in if the head is green by then.

…e live registry, not a snapshot of its stamps

Three pins hard-coded which retirements a floor opens — 'every retirement is
stamped 17.4.0 or earlier' — and went red when connector-triggers-removed,
landing after the 17.5.0 version pass, was stamped 17.5.0 as the census rule
requires. The door behaves as its rule says; the pins now derive the opened
set from ALL_CONVERSIONS, keep every refusal assertion, and keep the
'authored-current' verdict at full strength on a floor past every stamp.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7d9c9aa268b9474a86a5927f67dc2ce247dd2767
Local-runs: none

Director seat (objectstack#12708, session_01AsCNgFBs8HCjwhyHQsFbx3) — the delta record this seat's note 5886051680 said it would write. Inputs: this seat's PASS 5884968904 at 8da27aa8a6 (adopted for everything it covered), the three commits since it (885d9093b5 merge of main, dd7be6486e re-stamp + changeset sentence, 7d9c9aa268 pin update) read commit by commit, the seat's surface extension 5886460348 and the follow-up dev report 5886407077 on #20287, the check-runs on the head, and read-only git show / git grep at the head. Nothing built, run or re-run. The PR-own diff against the merge base 0f6dcac5e9 is 31 files (+1283 / −422): the reviewed 30 plus packages/metadata-core/src/artifact-forward-conversion.test.ts.

① Derived judgments

  1. The retirement kit is unchanged. Against the reviewed head the 30 files differ in two lines only (below); judgments 1–9 of 5884968904 stand as written.
  2. retiredAfter: '17.5.0' — right. 17.5.0 was cut this morning; main and the head carry that label in packages/spec/package.json, and 17.5.0 still accepts triggers (this PR is unmerged), so it is the last release the retirement follows — the fact-at-landing conversions/types.ts requires, not a guess at the next release. It is the table's only 17.5.0 stamp (one of 96 retired entries): the first retirement to land after the cut. Spec's own census pin is green on the head.
  3. The changeset sentence — right. The 20273 note was consumed into the 17.5.0 CHANGELOG by the version-packages merge, so the correction now names it by that release, its D2 id and its former file; no foreign changeset is edited (the seat's ruling 5884277442 kept) and Check Changeset is success.
  4. The pin update — right, and it is the rule's, not a workaround. Three metadata-core per-entry-window pins (Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390) hard-coded which retirements a floor opens — their own comment said "every retirement is stamped 17.4.0 or earlier" — a snapshot of the registry that the first 17.5.0 stamp falsified, as this seat's note read it. They now derive the opened set from ALL_CONVERSIONS (exported by the conversions barrel), the door's two default flips excluded, with floor <= retiredAfter as the rule the block already states. The "authored at the current spec version — no blanket strip" case uses a floor derived past both the label and every stamp, so its refusal keeps full strength; a new case pins the label floor (^17.5.0 on a 17.5.0 runtime): exactly the entries stamped at or after the floor open — at this head, connector-triggers-removed alone — the 17.1.0-retired permission keys never replay, no notice fires, the definition comes back by reference with its retired keys still present for the strict parse. The two Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 cases assert map equality against the rule plus the boundary (an entry the floor post-dates is never in the replay; the 17.4.0 floor still replays the 17.5.0 cohort). Not vacuous: each branch asserts an exact map, and the fixture's own retired sites are still refused unconverted.
  5. Composition. The seat recorded the surface miss (5886460348) and resumed the dev on it; no other test reads the per-entry window over the live registry (the dev's grep; this seat found the same one file). Read from the report, not re-run: check:generated 15 / 15 current, check:migration-registry current, spec local 575 files / 16915 passed + 1 todo, repo project 43 / 761; the metadata-core suite is CI's (Test Core (3/6) green on the head).

② Semver level

Unchanged: @objectstack/spec: minor with the BREAKING banner, Clause-②: no (narrowing); the metadata-core change is test-only and owes no changeset. Check Changeset and check:adr-0087-registration are success on the head.

③ Boundary flags

Implemented-by: claude/issue-20287-connector-triggers-retired
Reviewed-by: session_01AsCNgFBs8HCjwhyHQsFbx3

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7d9c9aa268b9474a86a5927f67dc2ce247dd2767
Local-runs: none

Inputs read: card #20287 (body and all 11 comments, the ruling 5881831013 letter B and the seat rulings 5880969529 / 5884277442 / 5886460348 included), PR #20587 (body, 31-file list, net diff 0f6dcac5e9..7d9c9aa268), the 35 deduped check-runs on the head, and to test version claims the published @objectstack/spec 17.4.0 and 17.5.0 tarballs (npm, read only). The 17.5.0 tarball was published 2026-09-29T08:09:33Z, before the head's last two commits (dd7be6486e, 7d9c9aa268).

① Derived judgments

Accept-set and public-surface changes the diff implies

  1. connector.triggers refused on both carriers, any value ([] and both interval spellings included), as a retiredKey() tombstone on the shared ConnectorBaseSchema — right, the narrowing ruling B ordered. Registered integration/Connector:triggers and integration/DeclarativeConnectorEntry:triggers under RETIRED_KEYS_BY_MAJOR[18]; no default, so no residue owed — right.
  2. The provider-bound triggers refusal (DeclarativeConnectorEntrySchema superRefine) deleted — right: the base's never issue on triggers fires first, so the entry's check could only repeat the verdict with the untrue "the provider derives them" reason. This is a deletion where the ruling's execution line said "corrected"; named in ③.
  3. ConnectorTriggerSchema / ConnectorTrigger leave @objectstack/spec/integration (RETIRED_DEFS_BY_MAJOR[18]: integration/ConnectorTrigger) — right, whole-def removal, and the ratchets moved as the skill's visibility table demands: api-surface −2, export-origins −2, declaration-map −2, json-schema.manifest −1, authorable-surface −6 rows plus two [RETIRED] flips, references index 1522→1521, type-alias pin 780→779 with its receipt. Pinned sibling at .objectui-sha dd3f7e1be3: one comment (clientValidation.ts:609), no import — the Console Pin Gate check-run is skipped on this head, so that reading is this record's, made with git grep at the pin.
  4. D2 connector-triggers-removed behaviour — right: mapCollection(stack, 'connectors') + stripKeys(c, ['triggers']), lossless, one notice per connector carrying the key, idempotent and copy-on-write by construction, toMajor: 18 equals the step, retiredFromLoadPath: true; fixture 2 notices including the pre-rename interval row; stored sys_metadata connector rows reach it through applyConversionsToStoredItem('connector', row) (singular→plural map has connector: 'connectors').
  5. retiredAfter: '17.5.0' — right on both rules. Stamp rule (conversions/types.ts docblock; census test rule for an UNPUBLISHED entry): the label at the moment it lands — packages/spec/package.json reads 17.5.0 at the merge base 0f6dcac5e9 and on main, after the version-packages merge 8c87d26a5d (06:17Z). Per-entry window rule (metadata-core/src/artifact-forward-conversion.ts: entry replays when floor ≤ retiredAfter): the 17.5.0 tarball still exports ConnectorTriggerSchema and carries no connector-triggers-removed, so an artifact authored at ^17.5.0 may hold the key and the window must open for it; 17.4.0 would refuse that artifact (the Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 class). The census test's tolerance [17.4.0 (last censused), 17.5.0 (label)] admits it. The dev's follow-up-2 verdict "stamp right, pins wrong" is confirmed.
  6. Absorbed interval rename (connector-health-and-trigger-durations-unit-in-key deleted from the table and from step 18) — right by spec-property-retirement §0: its fixture must carry triggers, which this strip deletes, so the table's disjoint-fixture contract cannot hold both; with the breaker half already absorbed by spec(integration): retire the connector health-probe, circuit-breaker, authored status and nested webhooks keys (16), which nothing enforces #20273 nothing remained. integration/ConnectorTrigger:interval stays as the record (gate (b3) whole-def steady state) — right. The ABSORBED note says "the 17.4.0 tarball carries it retired": true but stale — the 17.5.0 tarball carries it too (retiredAfter: '17.3.0', 8 occurrences in dist/index.mjs). This is the first absorption that removes a PUBLISHED id whole rather than a half of it; ADR-0087 :144-147 and :452 read "never deleted … the transform history is permanent". Flagged in ③ for the seat's word, not judged wrong: the composed effect is unobservable (any triggers value ends deleted), the id is named by nothing outside packages/spec at the head, and the chain replays only listed ids.
  7. Ledger rows — right: six triggers children (five key rows plus the interval tombstone) collapse to one dead leaf row in the house tombstone shape (RETIRED date, the tombstone, the D2 id, why the row stays, what to do instead), verifiedAt 2026-09-29; _note corrected (refusal rows now authentication / actions; the Audit: several event/subscription/connector enums are schema-only (declared, no runtime consumer) #3197 docblock quote moved to the past); state-counts/connector.md 29/0/0/25/1/55 (30−5); README partition "seven top-level tombstones", "eight by swap". The ruling's "five trigger rows (:116–:136)" are the five key rows; the sixth child was the rename tombstone the same subtree held — the count is explained on the card and in the row.
  8. metadata-core per-entry window pins — right; every refusal they asserted is kept: (a) the :108 blanket-strip refusal keeps authored-current, notices [], definition by reference, allowPurge present, and adds replayedRetirements [], at a floor DERIVED past the label and every stamp; the ^17.5.0-on-17.5.0 scenario it used to model is kept as a new case with the same no-strip assertions (notices [], by reference, allowPurge and allowRestore present), permission-allow-restore-purge-removed never replayed, every replayed retiredAfter ≥ floor; (b) :507 keeps its per-id assertions and replaces the ['17.4.0'] stamp snapshot with equality to the rule plus ≥-floor and contains-17.4.0; (c) :515 keeps notices [], by reference, issuePaths == RETIRED_SITES and adds that both cohort ids are post-dated, while its authored-current / replayedRetirements [] half moves to a companion at the derived current floor. Two verdict assertions are registry-conditional (the verdict is converted-retired-after when the rule opens at least one entry, authored-current otherwise); acceptable because the companion pins the shut window unconditionally and the rule half is what the dev's R1/R2/R4 ablations turn red. DOOR_DEFAULT_FLIPS mirrors the module's two ids by hand — a third default flip fails the equality loudly, not silently.

Text an author acts on — sentence by sentence

  • Tombstone prescription TRIGGERS_RETIRED — every sentence true and sourced: "removed in @objectstack/spec 17" is the file's convention (six sibling tombstones :506–:747 say 17); AutomationEngine.registerConnector parses with ConnectorSchema.parse(def) and walks parsed.actions only (engine.ts:3752-3753 on main); connector_action is a node type (flow.zod.ts:53); api and schedule are flow trigger kinds (flow-trigger-kind.ts); the closing sentence is the pinned house os migrate meta --from 17 form. Right.
  • D3 entry connector-triggers-retired (surface, replacement, reason, acceptanceCriteria) — sourced throughout: "the platform refuses an api flow with no per-flow secret and verifies a signature on every call" holds on main since 487a7846df (trigger-api arms a flow's inbound hook without a secret and accepts unsigned posts; ADR-0041's trigger-api acceptance criteria name a per-flow secret and HMAC verification #20529 via fix(trigger-api,service-automation): refuse an api flow with no per-flow secret, at arm time and at registration (#20529) #20551); interval: 60000 ≈ sixteen hours is arithmetic; the chain sentence matches the table; "no code imports ConnectorTrigger or ConnectorTriggerSchema" is the upgrader's criterion and true at the head. Right.
  • Changeset .changeset/20287-connector-triggers-retired.md:
    • BREAKING paragraph: both carriers, the four doors, exports leaving ./integration (package.json export), ADR-0041 Tier 3 as quoted (docs/adr/0041:141-153), ADR-0097 §5 out of scope (:80) — right.
    • "Measured before removal" paragraph — matches the dev's m1–m3 and this record's grep (the only triggers: under examples/ is the webhook collection) — right.
    • FROM → TO table, one-line fix, os migrate meta --from 17 — right, the signed-api-flow sentence included.
    • Tombstone bullet, incl. "a bare deletion would be a silent strip, ADR-0104" — right; ADR-0104 is the file's house cite for that fact (seven pre-existing cites at the base, and the 17.5.0 CHANGELOG's 20273 entry).
    • Provider-bound refusal bullet, def bullet, D2 bullet, "No deprecation window", "NOT MEASURED", Clause-②: no (narrowing), the single adr-0087 marker (registered connector-triggers-removed, connector-triggers-retired) — right.
    • Chain bullet, last sentence — WRONG. "This corrects the 17.5.0 note for the connector resilience retirement … whose sentence 'The conversion's triggers[].interval → intervalSeconds rename is unaffected.' no longer holds …" (a) The note is RELEASED: packages/spec/CHANGELOG.md line 7735 on main, in the 17.5.0 entry, shipped in the 17.5.0 tarball. AGENTS.md Documentation Guardrails (packages/*/CHANGELOG.md): "Factual error in a released entry → amend that entry in a dedicated docs-only PR, ⛔ never an erratum in a later entry and never a rider on code changes". This sentence is an erratum in a later entry riding a code PR. (b) It is also mis-framed: the 17.5.0 note was TRUE of 17.5.0 — that tarball carries the rename conversion (retiredAfter: '17.3.0'), so nothing in it is corrected; this release changes the chain, which the bullet's earlier sentences already state. The seat ruling 5884277442 directed a correction-in-own-changeset while the 20273 changeset was PENDING (finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712 class, "both ship in the same release"); that premise lapsed at 06:17Z/08:09Z, the dev re-worded the sentence to name the release (5886407077) and no re-ruling followed. Fix: delete the sentence (the chain is already stated); if the maintainer wants the 17.5.0 entry amended, that is a dedicated docs-only PR.
    • D3 bullet — FALSE. "The absorbed rename's own D3 entry (connector-resilience-durations-unit-in-key, never released) is gone with its conversion." The 17.5.0 tarball ships that entry in step 18's semantic list (dist/index.mjs: id: "connector-resilience-durations-unit-in-key", surface: "connector.triggers[].interval …"); 17.4.0 does not. It was "never released" when the stage-2 report said so (05:27Z) and stopped being so at 08:09Z; the head's text was not re-measured. Consequence beyond the wording: the PR deletes a RELEASED D3 entry, which ADR-0087 ("history is permanent") does not by itself license and which the ruling did not name — the seat must say whether a released D3 entry may leave (the family's judgement now lives in connector-triggers-retired) or must stay as a superseded record. Either way the sentence must change.
  • PR body — every sentence true against the tree and the rulings (Clause-②: no (narrowing), minor with the banner and the ADR-0087 disposition, ruling B / ADR-0041 unchanged, the tombstone and its two shapes, the refusal deleted as unreachable, retiredAfter 17.5.0 "the label main carries after the 17.5.0 cut", the ledger collapse, the pins rewritten, "automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287 stays open for its action half" per 5884277442), except "The changeset says that the released 17.5.0 note's 'rename is unaffected' sentence no longer holds", which restates the flagged changeset sentence and falls with it.

② Semver level

'@objectstack/spec': minor with the BREAKING banner, FROM → TO mapping, one-line fix and exactly one ADR-0087 marker — matches what the diff publishes: an authorable key refused that parsed before, and two exports removed from a published entry, in @objectstack/spec alone. packages/metadata-core changes a test file only and publishes nothing; content/docs, docs/** and the baselines are not packages. minor not major is the skill's rule for the launch window (check-changeset-no-major); the breaking semantics ride the banner. Clause-②: no (narrowing) appears in the changeset body and the PR body, the arm AGENTS.md defines as BREAKING and the one the ruling set — right. Check Changeset and TypeScript Type Check (which runs check:adr-0087-registration, check:spec-changes, check:upgrade-guide, check:api-surface) are green on the head. The two changeset sentences flagged in ① do not move the level; they are the text the level ships with.

③ Boundary flags

Dev flags (stage-2 report 5884248207, follow-ups 5886407077 and 5887087586; neither carries a deviations field):

  1. open_questions[0] — "Part of automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287" vs "Closes": answered by seat ruling 5884277442 (A); the Part-of PR must not also close its card check-run is green.
  2. open_questions[1] — the pending 20273 changeset's "rename is unaffected" sentence: ruled 5884277442 (state it in this PR's own changeset; foreign changeset untouched), but the ruling's premise (a PENDING note, both in one release) lapsed when 17.5.0 was cut and published; the dev adapted the sentence without re-escalation and the seat's next comment ruled only on the pins. Escalated to the seat: the released-entry rule of AGENTS.md now governs (see ① changeset chain bullet). This is the first FAIL reason.
  3. out_of_scope_findings[0] — check:platform-checklist red on the base (identity-auth.json / twoFactor): not this PR's; the Lint & Repo Gates check-run on this head is green, so CI does not carry that redness here. Noted for triage.
  4. out_of_scope_findings[1] — the 20273 changeset's false-once-landed sentence: same as flag 2.
  5. out_of_scope_findings[2] (objectui clientValidation.ts:609 comment naming retired schemas) and [3] (packages/spec/src/integration/index.ts docblock) — comment prose, no import, no carrier: noted, correctly outside this diff.
  6. Follow-up-1 report: "connector-triggers-removed.retiredAfter moved from 17.4.0 to 17.5.0" — answered right (① item 5).
  7. Follow-up-2 report: "The STAMP is right and the PINS were wrong … no fork" — confirmed (① items 5 and 8).
  8. Stage-1 open_questions (four-axis framework): answered by the seat's decision post 5880969529 and ruling 5881831013.

Deviations this record names that the dev did not list as such:

  1. The ruling's execution line reads "the refusal reason at connector.zod.ts:1242 corrected"; the diff DELETES the refusal. Answered: the tombstone makes the rule unreachable, so a corrected reason would be dead text; the PR body, changeset, D3 entry and ledger row all say so. No escalation needed.
  2. The changeset's "(connector-resilience-durations-unit-in-key, never released)" is false as of 17.5.0, and the deletion of a RELEASED D3 entry is not named by any ruling. Escalated to the seat (① D3 bullet). This is the second FAIL reason.
  3. The rename conversion connector-health-and-trigger-durations-unit-in-key is a PUBLISHED retired id (17.4.0 and 17.5.0) removed whole under skill §0 while ADR-0087 :144-147 says a retired conversion is "never deleted". Judged right by the skill's disjoint-fixture constraint (① item 6); escalated as a question, not a defect: the seat should confirm the §0 route covers a published id, and the ABSORBED note should name 17.5.0 beside 17.4.0.
  4. The dispatch's Closes #20287 was written as Part of — covered by flag 1.
  5. "Beyond the dispatch: I set the PR assignee to the card's" — the os-dev standard clause; no flag.

Check-runs on 7d9c9aa268: 46 runs, 35 names after dedupe by newest started_at; all completed; none running; 31 success, 4 skipped (Auto Label, Check PR Size, Console Pin Gate, Packed-tarball smoke (opt-in)); 0 failures. Test Core (3/6), red at dd7be6486e, is green on this head.

Verdict reasoning: the tree, the accept-set, the stamp, the ledger and the pins are right; the two failing items are two sentences in the changeset — text that ships to consumers as CHANGELOG.md on a deadline the release consumes and cannot be amended afterwards from a code PR — one of which asserts a false fact about a published tarball and the other of which takes the form AGENTS.md forbids for a released entry. Both are one-line edits before landing.

Implemented-by: claude/issue-20287-connector-triggers-retired
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: FAIL

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-29T09:23Z · rendered by the seat's at-tier review subagent on this head; its served tier family was read from the subagent transcript before posting. FAIL accepted. Both reasons are the seat's to answer: its ruling 5884277442 Q2 was made while the 20273 changeset was pending, and that premise lapsed when 17.5.0 was published. The seat's ruling on both reasons and on ③ item 11 follows on #20287. The review label comes off both carriers now and is re-hung on the next delivered head.


Generated by Claude Code

…eleased 17.5.0 entry that is true of 17.5.0

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…op 'never released'

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…me id retired, 17.4.0 and 17.5.0

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Director note · this seat's delta PASS 5887057887 is withdrawn on two points · 2026-09-29T10:11Z

Director seat (objectstack#12708, session_01AsCNgFBs8HCjwhyHQsFbx3). At the same head 7d9c9aa268, the at-tier record 5887388541 (FAIL) found what this seat's delta record missed: the changeset's erratum on the 20273 note — which had become a RELEASED 17.5.0 CHANGELOG entry at 2026-09-29T08:09Z, so the RELEASE-OWNED rule in AGENTS.md applies and a later entry may not amend it — and the false "never released" on the absorbed D3 entry, which the 17.5.0 tarball ships. This seat's item 3 called the erratum sentence right; it was not, and the D3 claim was not checked against the tarball. The FAIL is the record of that head; the seat's ruling 5887435658 on the card accepted it and the dev delivered the three edits as 4f8c62b397.

The rest of 5887057887 (the kit unchanged, the 17.5.0 stamp, the pin update) is confirmed by that FAIL's own items 5 and 8. The at-tier review judges 4f8c62b397 (needs:contract-review re-hung on the card); this seat writes no further record on this PR unless asked.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4f8c62b3975236c86e079f64bff19dc1005b50f6
Local-runs: none

Inputs read: card #20287 (body and all 13 comments — the maintainer's ruling 5881831013 letter B, the seat rulings 5880969529 / 5884277442 / 5886460348 / 5887435658, the four dev reports and the stage-1 report), PR #20587 (body, the 31-file list, the net diff 0f6dcac5e9..4f8c62b397, its six comments including the earlier records 5884968904 / 5887057887 PASS and 5887388541 FAIL on 7d9c9aa268), the 39 check-runs on the head, read-only git show / git grep at the head and on main 1322cc72, objectui at the pin dd3f7e1be3 by git grep only, AGENTS.md (ADR-0087, BREAKING changesets, released CHANGELOG entries), spec-property-retirement, ADR-0041 :141-153, ADR-0087 :144-147 / :452-455, ADR-0104, and — to test the version claims — the published @objectstack/spec 17.4.0 and 17.5.0 tarballs, downloaded from npm and grepped, never executed. Nothing built, run or re-run.

① Derived judgments

Accept-set and public-surface changes the diff implies

  1. connector.triggers refused on both carriers, every value — right. triggers: retiredKey(TRIGGERS_RETIRED) sits on the private ConnectorBaseSchema that ConnectorSchema and DeclarativeConnectorEntrySchema both wrap, so defineConnector, stack.connectors[], the PUT /meta/connector/:name door (the getMetadataTypeSchema('connector') binding) and AutomationEngine.registerConnector (engine.ts:3752 parses with ConnectorSchema.parse(def)) all meet it — in tsc (input type never) and at parse (invalid_type at path triggers, the prescription as the message). retiredKey is z.never({ error }).optional(), so the key stays in the walked shape and the ledger and authorable-surface rows stay reachable (pinned). Registered as integration/Connector:triggers and integration/DeclarativeConnectorEntry:triggers in RETIRED_KEYS_BY_MAJOR[18], exact per-def membership, both entry files present. The key had no default, so no retired-default residue is owed. This is the narrowing ruling B ordered.
  2. The provider-bound triggers refusal deleted rather than re-reasoned — right in effect; a deviation from the ruling's letter, named in ③. The ruling's execution line reads "the refusal reason at connector.zod.ts:1242 corrected". The base's never issue fires on any value on every carrier, so the entry-level superRefine branch could only add a second issue carrying the untrue reason ("the provider derives them from the upstream at boot" — no provider derives a trigger; ADR-0097 :80 leaves triggers out of scope). A corrected reason would have been dead text. connector-provider.test.ts and the new suite pin that a provider-bound instance meets the retirement prescription and that no issue on any door contains derives them from the upstream. The actions half of the ADR-0097 §5 rule is untouched.
  3. ConnectorTriggerSchema / ConnectorTrigger leave @objectstack/spec/integration — right, and the ratchets moved as a whole-def removal must. RETIRED_DEFS_BY_MAJOR[18] gains integration/ConnectorTrigger. Per the skill's visibility table a whole-def removal cannot leave the four ratchets byte-identical, and it did not: api-surface −2, export-origins −2, declaration-map −2, json-schema.manifest −1, authorable-surface −6 ConnectorTrigger:* rows plus the two carrier rows flipped to [RETIRED]; the references index 1522 → 1521 (integration 17 → 16 schemas), the strictness-ledger site count 5 → 4, the type-alias pin 780 → 779 with its receipt. At the head git grep finds no import or use of ConnectorTrigger outside packages/spec (the remaining hits are the retirement's own prose and a release-owned 17.0 note); objectui at the pin dd3f7e1be3 holds one comment (clientValidation.ts:609) and no .triggers read in the metadata-admin consumers, so the skipped Console Pin Gate (no pin moved) hides no break. Pinned: zero holders of either retired name on any public entry while the carriers survive; the integration barrel resolves without the schema; the tree-scoped absence walk over packages / examples / skills / content / scripts with its anti-vacuity cases.
  4. D2 connector-triggers-removed behaviour — right. mapCollection(stack, 'connectors') plus stripKeys(c, ['triggers'], emit, path): a lossless delete, one attributed notice per connector carrying the key, idempotent and copy-on-write by construction (the stripKeys shape §3 names), toMajor: 18 equal to the step it is wired into (the derived conversionIds reads it in), retiredFromLoadPath: true, surface: 'connector.triggers'. The fixture is disjoint (only identity keys beside triggers), carries a polling, a webhook and a pre-rename interval row, and expectedNotices: 2 equals the connectors carrying the key. Stored sys_metadata connector rows reach it through applyConversionsToStoredItem('connector', row) — pinned as lossless (the row minus triggers, key for key) and accepted by the tombstoned door afterwards. It strips and never writes a flow; the ruling asked for exactly "stripping triggers from stored connector rows", and the judgement of which triggers should now be flows is the D3 entry's.
  5. retiredAfter: '17.5.0' — right on both rules, and the tarball confirms it. Stamp rule (conversions/types.ts :262-271; the census test's UNPUBLISHED rule): the package label at the moment the entry lands — packages/spec/package.json reads 17.5.0 at the merge base, on main and at the head. The census JSON's last release is 17.4.0, so today the pending tolerance [17.4.0, 17.5.0] admits it; once the census records the 17.5.0 tarball (which carries no connector-triggers-removed) the rule collapses to exactly the label 17.5.0, so no refresh can move the stamp. Per-entry window rule (artifact-forward-conversion.ts docblock: entry E replays when the floor is below the runtime, or the floor is at or below E.retiredAfter): the published 17.5.0 tarball still exports ConnectorTriggerSchema, still carries the Trigger definitions describe and no tombstone text, so an artifact authored at ^17.5.0 may legitimately carry triggers and the door must open this entry for it; a 17.4.0 stamp would have refused that artifact — the Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 regression class. It is the table's only 17.5.0 stamp and main carries none, so the PR body's "first post-cut stamp" is true. The dev's follow-up verdict "stamp right, pins wrong, no fork" is confirmed.
  6. The absorbed interval rename — right under spec-property-retirement §0, with the residual recorded. connector-health-and-trigger-durations-unit-in-key (toMajor 18) had already lost its breaker half to the health removal (spec(integration): retire the connector health-probe, circuit-breaker, authored status and nested webhooks keys (16), which nothing enforces #20273); its fixture must carry triggers, which this strip deletes, so the table's disjoint-fixture contract cannot hold both, and with neither half left the entry and its semantic connector-resilience-durations-unit-in-key leave the table and step 18. integration/ConnectorTrigger:interval stays as the record (gate b3 whole-def steady state). The residual: the id was PUBLISHED — the 17.4.0 and 17.5.0 tarballs both carry it retiredFromLoadPath: true (17.5.0 with retiredAfter: '17.3.0'), and 17.5.0 also ships the D3 entry — while ADR-0087 :144-147 reads "never deleted … the transform history is permanent". The seat ruled the absorption stands (5887435658 item 3: §0's condition is the unpublished MAJOR, protocol 18 is unpublished; the composed effect is unobservable, any triggers value ends deleted; a stored row or artifact in either spelling meets the strip that deletes its container; spec(integration): retire the connector health-probe, circuit-breaker, authored status and nested webhooks keys (16), which nothing enforces #20273 absorbed the other half of the same id) and named it for the maintainer as the first whole-id absorption. Judged right by that ruling and the skill; the chain pin (a pre-rename interval trigger ends with the whole array gone, one notice, no rename) and the resilience suite's control moved to a live sibling both hold.
  7. The ledger rows — right. The six triggers children (five key rows plus the interval rename tombstone) collapse into one dead leaf, verifiedAt 2026-09-29, in the house tombstone form the health / status / webhooks rows use (RETIRED date, the ADR, the tombstone, the D2 id, why the row stays — the rls.priority precedent — what to do instead, and the collapse explained by the health precedent: the gate refuses children under a non-container). _note corrected (the refusal rows are now authentication / actions; the Audit: several event/subscription/connector enums are schema-only (declared, no runtime consumer) #3197 docblock quote moved to the past tense); state-counts/connector.md 29/0/0/25/1/55 (dead 30 → 25, classified 60 → 55); the README partition reads seven top-level tombstones and eight tombstone rows by swap. The ruling's "five trigger rows (:116–:136)" are the five key rows; the sixth child was the rename's own tombstone in the same subtree — the count is explained on the card and in the row. The platform-checklist negative item now says what the parse does. Spec property liveness is success on the head.
  8. The rewritten metadata-core per-entry window pins keep every refusal they asserted — right. (a) :108 "authored at the current spec version — no blanket strip" keeps authored-current, notices [], definition by reference and allowPurge present, adds replayedRetirements [], at a floor DERIVED past the label and every stamp (currentSurfaceFloor), which the registry cannot move; the ^17.5.0-on-17.5.0 scenario it used to model is kept as a new case with the same no-strip assertions (notices [], by reference, allowPurge and allowRestore present), permission-allow-restore-purge-removed never replayed, every replayed retiredAfter at or above the floor, the replay set equal to openedByRule('17.5.0'). (b) :507 keeps both cohort ids at 17.4.0 and the default-flip exclusion, replaces the ['17.4.0'] stamp snapshot with equality to the rule plus the at-or-above-floor check plus contains 17.4.0. (c) :515 keeps floor 17.5.0 on runtime 17.5.0, notices [], by reference and issuePaths == RETIRED_SITES (the strict parse still refuses all four retired sites), adds that both 17.4.0-cohort ids are never replayed; its authored-current / replayedRetirements [] half moves to a companion at the derived current floor with the same RETIRED_SITES refusal. Two verdict assertions are registry-conditional; acceptable because the companion pins the shut window unconditionally and DOOR_DEFAULT_FLIPS hand-mirrors the module's two ids so a third flip fails the map equality loudly. Test Core (3/6), red at dd7be6486e, is success on this head.
  9. Generated projections that did not move — right. spec-changes.json and the upgrade guide project only through the current protocol major (perMajor 16 → 17); step 18 is not projected until it ships, so no change was owed. check:spec-changes and check:upgrade-guide run inside TypeScript Type Check, success on the head.

Text an author acts on — sentence by sentence

  • Tombstone prescription TRIGGERS_RETIRED — every sentence true and sourced. Fully-qualified key in backticks first; "was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove)" is the file's convention (the six sibling tombstones say 17); "AutomationEngine.registerConnector registers a connector's actions only" — engine.ts:3752-3753; "no polling loop read intervalSeconds (or the interval spelling it was renamed from), and no receiver was driven by a webhook trigger" — the stage-1 measurement, unchallenged on the card, and the ledger row; "Delete the key; the ConnectorTrigger shape leaves with it" — true (item 3); connector_action is a node type (flow.zod.ts:53); api and schedule are flow trigger kinds; the closing sentence is the pinned house os migrate meta --from 17 form (17 = N−1 of toMajor 18). No tracker number in the text (pinned). Right.
  • D3 entry connector-triggers-retired, author-shown fields — right. surface (both carriers, both spellings) true; replacement names the two working shapes; reason: "the platform refuses an api flow with no per-flow secret and verifies a signature on every call" — api-trigger.ts:134-142 throws at arm when config.secret is blank, so a hook is armed only signed (the trigger-api arms a flow's inbound hook without a secret and accepts unsigned posts; ADR-0041's trigger-api acceptance criteria name a per-flow secret and HMAC verification #20529 rule on main); "interval: 60000 … once every sixteen hours or so" is arithmetic; the chain sentence matches the table. acceptanceCriteria — "no code imports ConnectorTrigger or ConnectorTriggerSchema" is the upgrader's criterion and true at the head. The entry is one file, wired into step 18's semantic list (pinned: exactly one entry, naming its D2 and the chain).
  • Changeset .changeset/20287-connector-triggers-retired.md — nothing false, unsourced or over-broad remains. BREAKING paragraph: both carriers, the four doors, the two exports leaving @objectstack/spec/integration, ADR-0041's third tier and promotion rule quoted true against :141-153, "ruled RETIRE on the maintainer's criterion" — right. "Measured before removal" — matches the stage-1 report, and "no connector package, provider or example declared one" holds at the head (the tree-scoped pin). FROM → TO table: polling → a schedule flow with a connector_action node at the cadence in seconds; webhook → an api flow the sender must be able to sign, "refused without a per-flow secret and every call must carry its signature" true; the schema pair → no replacement — right. The one-line fix and os migrate meta --from 17 — right. "Runtime behaviour is deliberately unchanged" — right, nothing read the key. Kit bullets: the tombstone bullet's ADR-0104 cite for "a bare deletion would be a silent strip" is the file's house cite (seven pre-existing cites at the base; ADR-0104 :18 and :72 record the silently-stripped-declaration class); the refusal bullet, the def bullet and the D2 bullet (stored rows through the rehydration seam, one notice per connector, stripped never turned into a flow) — right. "The chain." bullet: the erratum on the released 17.5.0 note that the prior FAIL named is deleted; the bullet now ends on "stays as the record", and every remaining sentence is true of the table. The D3 bullet: ", never released" is deleted — the 17.5.0 tarball ships connector-resilience-durations-unit-in-key, so the old clause was false and the new one ("is gone with its conversion") is true. "No deprecation window" and the NOT MEASURED out-of-repo population — the house form. Clause-②: no (narrowing); exactly one adr-0087 marker, registered connector-triggers-removed, connector-triggers-retired, both ids resolving in the registries. The released 17.5.0 CHANGELOG entry is not edited and no erratum rides here — the AGENTS.md :699 rule holds.
  • PR body — every sentence true against the tree and the rulings. Clause-②: no (narrowing) with minor plus the banner and the disposition the ruling set; ruling B, ADR-0041 unchanged, no new ADR; the tombstone on ConnectorBaseSchema and its two prescribed shapes in a connector_action node; the refusal deleted as unreachable; ConnectorTrigger leaves whole; D2 retiredAfter 17.5.0 "the label main carries after the 17.5.0 cut" (package.json); the D3 id; the absorption per §0; the ledger collapse and regeneration; the pins now state the rule and "this first post-cut stamp broke the snapshot" (the only 17.5.0 stamp, none on main, the three failures at dd7be6486e); "automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287 stays open for its action half" per 5884277442 item 1 and the Part-of PR must not also close its card check. The sentence the prior FAIL flagged in the body is no longer there.
  • ABSORBED note in conversions/registry.ts (a code comment the ruling dictated, not author-shown) — fact true, citation over-broad. "the 17.4.0 and 17.5.0 tarballs carry it retired (retired-after.census.json)": both tarballs do carry the id retiredFromLoadPath: true (read from npm), but the census JSON at the head records releases only through 17.4.0, so the parenthetical sources the 17.4.0 half alone; the 17.5.0 half is sourced by the tarball, not the file. The dev flagged this (③ item 8). Not text an author acts on; rides the next census refresh.

② Semver level

'@objectstack/spec': minor with the BREAKING banner, a FROM → TO mapping, the one-line fix, Clause-②: no (narrowing) and exactly one ADR-0087 marker — matches what the diff publishes: an authorable key that 17.5.0 accepts is refused after (the (narrowing) arm AGENTS.md defines as BREAKING and the arm the ruling set), and two exports leave a published entry, in @objectstack/spec alone. minor, not major, is the skill's rule for the launch window (check-changeset-no-major); the breaking semantics ride the banner. packages/metadata-core changes a test file only and publishes nothing; content/docs, docs/** and the baselines are not packages, so no second changeset is owed and skip-changeset would be wrong. The Clause-② line appears in the changeset body (where check:adr-0087-registration reads the arm) and in the PR body. Check Changeset and TypeScript Type Check (which runs check:adr-0087-registration, check:api-surface, check:spec-changes, check:upgrade-guide) are success on the head. Same level and shape as the two earlier connector retirements in this step.

③ Boundary flags

Dev flags (stage-2 report 5884248207, follow-ups 5886407077, 5887087586, 5887792654; none carries a deviations field):

  1. Stage-2 open_questions[0] — "Part of automation: connector triggers start flows, and a connector action's description / outputSchema reach the flow designer (7 keys) #20287" vs a closing keyword: answered A by 5884277442 item 1; the Part-of PR must not also close its card check-run is success.
  2. Stage-2 open_questions[1] — the 20273 note's "rename is unaffected" sentence: ruled 5884277442 Q2 while the note was pending, then withdrawn and re-ruled 5887435658 item 1 after 17.5.0 was published at 08:09Z (the note is now a released CHANGELOG entry; AGENTS.md :699 forbids an erratum in a later entry; no amendment PR is owed because the note is true of 17.5.0 — the tarball carries the rename conversion). The cut is delivered at this head.
  3. Stage-2 out_of_scope_findings[0] — check:platform-checklist red on the base (identity-auth.json / twoFactor): not this PR's; Lint & Repo Gates is success on the head; triage's.
  4. out_of_scope_findings[1] — the 20273 sentence: same as flag 2, closed.
  5. out_of_scope_findings[2] (objectui clientValidation.ts:609 comment naming retired schemas) and [3] (integration/index.ts docblock): comment prose, no import, no carrier — noted, correctly outside this diff.
  6. Follow-up 1: retiredAfter moved 17.4.0 → 17.5.0 by the census rule after the version-packages merge — answered right (① 5).
  7. Follow-up 2: "the STAMP is right and the PINS were wrong; no fork" — confirmed (① 5 and 8). The seat's surface extension 5886460348 (the metadata-core pin file joins the claim's surface) is honoured; no other test reads the per-entry window over the live registry.
  8. Follow-up 3 edits_read_whole[3]: the ABSORBED note's retired-after.census.json parenthetical sources only the 17.4.0 half — confirmed (① last bullet). A one-line follow-up at the next census refresh; the census trails the label now that 17.5.0 is published, and refreshing it is not this PR's.
  9. Stage-1 open_questions (the four-axis framework): answered by the seat's decision post 5880969529 and the ruling 5881831013.
  10. not_measured items across the reports (cloud repo; check:dual-build-cjs-loads, check:type-check-debt, check:skill-examples locally; the server-side merge): CI covers them — TypeScript Type Check, the four Type Check · jobs and Lint & Repo Gates are success; the PR reads mergeable: true, mergeable_state: clean against main 1322cc72.

Deviations this record names that the dev did not list as such:

  1. The ruling's "refusal reason corrected" was delivered as a deletion of the rule — answered (① 2): the tombstone makes the rule unreachable, the PR body, changeset, D3 entry and ledger row all say so, and the seat's later rulings read the report without objection. No escalation needed.
  2. A whole PUBLISHED conversion id and a released D3 entry leave the registries under §0 — ruled to stand by the seat (5887435658 item 3) and escalated by the seat to the maintainer in its round report as the first whole-id absorption; the maintainer may overrule. Recorded, not a defect on this head (① 6).
  3. The prior FAIL 5887388541 on 7d9c9aa268 gave two reasons; both are fixed at this head, and the delta 7d9c9aa268..4f8c62b397 is exactly the three ruled edits (2 files, +3 / −7: the erratum sentence cut, ", never released" cut, the ABSORBED note naming both tarballs) and nothing else.
  4. Governed surfaces: none of the 31 paths; Governed Surface Queue Guard is success. The retirement skill was followed, not edited.
  5. State: draft; assignee os-justin; needs:contract-review on the PR; the card stays open for its action half (objectui#11028, the pin bump, the two live flips). The landing is the seat's, on this record.

Check-runs on 4f8c62b3975236c86e079f64bff19dc1005b50f6: 39 runs, 35 names after dedupe by newest started_at; all completed; none running or queued; 31 success, 4 skipped (Auto Label, Check PR Size, Console Pin Gate — no pin moved, Packed-tarball smoke (opt-in)); 0 failure. Named: Test Core (3/6) success (the metadata-core pins), Spec property liveness success, Check Changeset success, TypeScript Type Check success, Lint & Repo Gates success, Part-of PR must not also close its card success, Dogfood Regression Gate success, Temporal Conformance (live PG + MySQL) success.

Verdict reasoning: the accept-set change is the one the ruling ordered and is delivered on both carriers with the ADR-0087 kit complete; the stamp is right on both rules and confirmed against the published tarball; the ledger, the baselines and the pins are right; the two changeset sentences that failed the prior head are gone and every sentence an author acts on is now true and sourced; every dev flag is answered or carried by a ruling on the card; the head is green.

Implemented-by: claude/issue-20287-connector-triggers-retired
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-29T10:24Z · rendered by the seat's at-tier review subagent on this head; its served tier family was read from the subagent transcript before posting. It supersedes the FAIL 5887388541 at 7d9c9aa268. The published-id absorption is named for the maintainer in the seat's round report 5888099645; the maintainer may overrule it. The retired-after.census.json parenthetical is left for the next census refresh, as the record says. Landing waits for all checks green; this card stays open for its actions half.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants