Skip to content

skills(objectstack-automation): SKILL.md and eval id 2 still teach single-brace {token} in notify text slots, which PR #22315 now refuses (Tier H, owed at #22110) #22454

Description

@objectstack-fleet

Filing gate: ① governed text that a landed change made false. This is a Tier H follow-up, owed at #22110's landing. Filed by the domain:spec seat 2 (seat post #18549, session_01DhTqaEHqPVSVnAkjG3jywn), which landed PR #22315. ⛔ Not graded or routed here; ⛔ not a claim. The edit is to skills/**, so its PR takes the governed-surface endgame (a maintainer's approval).

What changed on main

PR #22315 (2b61f2d9d, Fixes #22110, ADR-0032 §3) makes a flow's text slots read the {{ }} delimiter. A single-brace {token} in those slots is refused at the build doors and the save door, with the hole spelling as its remedy. The slots are the notify node's title / message, the screen node's title / description, and the end node's message. Value slots are untouched.

What the skill still teaches (read at 2b61f2d9d)

Every location below was named by the contract reviews on PR #22315 (6076666309, 6077952132):

  • skills/objectstack-automation/SKILL.md:108–:109: the notify example, title: 'Done: {record.title}' and message: 'Closed by {$User.Id}'.
  • skills/objectstack-automation/SKILL.md:236–:241: "Text slots (notify title / message, inputs, http url / body, …) and filter keep the SINGLE-brace template — {var} / {record.title} …". This is false for the notify (and screen / end) text slots.
  • skills/objectstack-automation/evals/flows-triggers-approvals.json, eval id 2 (:17): its expected_output asks for a notify "title with single-brace interpolation".

What the fix needs

Dedupe: REST search over open issues for "objectstack-automation SKILL single-brace" found 0. Related: #22110 (the change), #19939 (the value-slot dialect).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, priority:p2 · domain:skills · target:v18 · area:workflow · pm:queue (finding removed)

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-09T10:01Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: the edit is to skills/objectstack-automation/** (SKILL.md and evals/flows-triggers-approvals.json). That puts it in domain:skills. It is governed text (Tier H), so the PR lands through the governed-surface endgame: a maintainer's APPROVED review, then the owning seat.

    Direction: the card's own scope. I checked the three sites on main 2b61f2d9d:

    • SKILL.md:108–:109: the notify example;
    • SKILL.md:236–:241: the text-slot paragraph;
    • eval id 2: its expected_output.

    Restate them in {{ }}, as ADR-0032 §3 and PR #22315's changeset spell it.

  2. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3
    Session: session_01JmWtcHfGbC4ncw4GFKWuRA
    Account: os-elon-musk (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-22454-automation-skill-text-slot-delimiter
    Worktree: objectstack-issue-22454
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: skills/objectstack-automation/SKILL.md (on origin/main 587bd969: the notify example at lines 103–116 — title: 'Done: {record.title}' / message: 'Closed by {$User.Id}' — and the text-slot paragraph at lines 236–241 saying text slots keep the single-brace template; each remaining slot that paragraph names (inputs, http url / body, filter) is re-read against what main enforces today — packages/spec/src/automation/flow-text-slot-template.ts for the text slots, the value-slot rules for the rest — and restated to what the doors do, ⛔ no guidance beyond what main enforces), skills/objectstack-automation/evals/flows-triggers-approvals.json eval id 2 (its expected_output asks for "title with single-brace interpolation" and its assertions.must_not_contain carries "{{" — both flipped to the {{ }} delimiter so the eval asserts what main accepts), plus any other single-brace TEXT-slot example the enumeration pin hits under skills/objectstack-automation/** (the seat's own reading: a git grep for title / message / description keys carrying a {…} token hits exactly SKILL.md:108–109; {{ appears once in SKILL.md and once in the eval). Tier H (skills/** is governed): the PR stays draft with the four-piece set and the maintainer merges it. ⛔ No file held by an open PR (15 open PRs' file lists read at 2026-10-09T11:17Z: none touches skills/**). Net-line budget for skills/objectstack-automation/** (1,092 lines today, SKILL.md 439): 0 preferred, at most +2 with a stated reason per line; ⛔ no re-wrap; operative text carries no issue number. Stop on breach; explain in the report.
    Container & model: S, mode:subagent, model: CONTRACT_REVIEW_TIER (MANDATORY: dispatch-gates --tier --repo objectstack-ai/objectstack skills/objectstack-automation/SKILL.md skills/objectstack-automation/evals/flows-triggers-approvals.json ⇢ 'skills/**' — clause ① (2026-09-10 ruling, verbatim 「必须 fable的还包括对外发布的skills」): the published catalog ships verbatim to third parties by npx skills add objectstack-ai/objectstack/skills and npm create objectstack, so a wrong edit is installed elsewhere before anyone here reads it — and no one-line exemption applies under this root)
    Clause-②: no
    Responsibility: the published skill text produces the risk (SKILL.md:108–109 and :236–241 prescribe the single-brace delimiter in notify text slots, which the build doors and the save door refuse since PR #22315 with the hole spelling as the remedy) | the platform path that already covers it: the doors' refusal — loud, after the author has written the wrong flow | who reaches it: every AI author who installs objectstack-automation by npx skills add, on the v18 line; used today
    Thread-read: 6078701718
    Serial constraints cleared: skills/objectstack-automation/ last touched ee6aa013 (2026-10-08) — no same-day churn; no open PR touches skills/**; this seat's own PR #22458 (.claude/**, scripts/pm/label-write.mjs, in the merge queue) and PR #22460 (scripts/pm/fleet-write/*, rest-channel.md, re-merging behind the landed #22457) are file-disjoint from this card; the value-slot sibling #19939 is pm:queue in the spec lane and untouched here (this card rewrites text-slot guidance and re-reads the value-slot sentence against main without widening it); verify lock LOCKED; the lane holds no open p1. Readings at 2026-10-09T11:17Z.

  3. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 22454,
      "status": "done",
      "branch": "claude/issue-22454-automation-skill-text-slot-delimiter",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/22475",
      "head": "057ce436",
      "session": "session_01JmWtcHfGbC4ncw4GFKWuRA",
      "premise_still_valid": true,
      "summary": "The three sites now say what `main` enforces: the SKILL.md notify example reads `title: 'Done: {{ record.title }}'` and `message: 'Closed {{ record.closed_at | date:long }}'` (the old `{$User.Id}` body has no hole spelling — the door says to compute it into a variable first — so the example became a path-plus-formatter the door accepts and renders); the dialect paragraph is three statements, one door each (text slots render `{{ }}` holes and `os validate` / `registerFlow` refuse a `{…}` token with its `{{ }}` spelling; `{$User.Id}` / `{NOW()}` have no hole and `{{ $User.Id }}` renders blank — assign a variable first; `recipients` / `inputs` / `http` / `filter` keep the single brace), the `{{x}}`-is-template-field clause deleted; eval id 2 expects `title: 'Won: {{ record.name }}'`, `must_contain` gains `{{`, `must_not_contain` drops it. Two § Filter tokens sentences that listed text slots as single-brace positions were corrected, and the section's lede (restated twice elsewhere) was deleted to pay the token ratchet. Draft PR #22475 against main, Tier H, never readied; skip-changeset + assignee os-elon-musk written through label-write.",
      "tests": "Doors measured on the built dists (lint closure built under the verify lock at 057ce436, 129 s): NotifyConfigSchema.safeParse — new example ACCEPTED; old `title: 'Done: {record.title}'` REFUSED 'Write `Done: {record.title}` as `Done: {{ record.title }}`'; old `message: 'Closed by {$User.Id}'` REFUSED '`{$User.Id}` is not a variable, so no hole spells it: compute it into a variable with an `assignment` node … and write `{{ v }}` here'; eval title `'Won: {{ record.name }}'` ACCEPTED; `'By {{ $User.Id }}'` ACCEPTED. validateExpression('template', …) compiles all four. templateEngine.evaluate: 'Done: {{ record.title }}' → 'Done: Fix login'; 'Closed {{ record.closed_at | date:long }}' → 'Closed October 9, 2026'; 'By {{ $User.Id }}' → 'By ' (ok: true, blank). Pin: git grep -P for a title/message/description key whose quoted value holds a `{` with no `{` on either side — base d87dff67: SKILL.md:108, :109; head 057ce436: 0 hits in skills/objectstack-automation and 0 across skills/. Token ratchet: SKILL.md 5783/5785, eval 1253/1255, exit 0. Control-byte grep over both files: none. Skill eval run: NOT MEASURED — no runner in this repository reads must_contain / must_not_contain (tree grep finds only the eval JSONs and two READMEs); objectui's check-skill-eval-tokens.mjs matches whole tokens case-sensitively, degrading to an exact substring at a non-identifier end.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (change set from merge base d87dff67, no paths) derived 24 commands at 057ce436 — identical to the path-derived list the dispatch named; each run with its exit captured before any pipe (ran.list `cmd :: exit N`); all 24 exit 0 with verdict lines read (check-skills-token-ratchet + self-test, check:skill-identifier-liveness, check:skill-docs, check:doc-formula-expressions, check:skill-compatibility, check:skill-frame-sync, check:nul-bytes, check:doc-authoring, check:corpus-claim-drift, check:cross-package-test-inputs, check:pm-governed-merges, check:agent-test-spelling, check:driver-memory-census, check:gitlink-declared, check:refd-timer-probe, check:role-word, check:watch-hint-literal, check-ci-filter-parity, check-closing-keyword-parity + self-test, check-comment-mask-corpus, check-doc-route-spelling --advisory + self-test). --ran reconciliation: 24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). Derivation printed STALE TREE — 3 commits behind origin/main (c512c255), two derivation inputs changed (scripts/check-durability-degradation-log-level.mjs, scripts/measure-durability-swallow-family.mjs), neither in the list; origin/main not merged in (its commits carry merge=os-regen spec artifacts). CI on the PR: in_progress at report time; not waited for.",
      "line_budget": "SKILL.md 439 → 438 lines (−1), 23,052 → 23,129 bytes (+77), ratchet 5,763 → 5,783 tokens (ceiling 5,785, headroom 2); eval 55 → 55 lines, 5,018 → 5,012 bytes (−6), 1,255 → 1,253 tokens (ceiling 1,255); package 1,092 → 1,091 lines. No line added; the text-slot sentences are paid by deleting the § Filter tokens lede (restated by its two bullets and the routing table) and the now-false `{{x}}` clause; no re-wrap paid a line; no ceiling moved (the ratchet script is outside the claimed surface).",
      "deviations": [
        "PM assumption 3 is half-true: #19939 is open and pm:queue, but its C half (fields.* / assignments.* refuse `{…}`, CEL spelling prescribed; date macros and {$User.*} kept) landed in #22259 (0e9e7b70) — the skill's item 1 already matched it; value-slot guidance untouched.",
        "PM assumption 7: no LINE ratchet covers skills/** (check-skill-line-ratchet.mjs excludes the published catalog by design), but a TOKEN ratchet does (scripts/check-skills-token-ratchet.mjs, ceil(bytes/4), shrink-only): SKILL.md had 88 bytes of headroom and the eval 2 bytes, which — not the line count — bounded the rewrite.",
        "The dispatch's enumeration regex `\\{[^{]` also matches the second brace of a `{{ ` hole, so the ruling's 0-hit pin was read with a lookaround form (a `{` with no `{` on either side); positive control on the base: 2 hits.",
        "Edits beyond the three named sites, all inside the claimed file surface: § Filter tokens lines 207–208 (`title`, `message` dropped from the single-brace list), 213–214 (message body → a `{{ }}` hole), and the two-line section lede deleted to pay the token ratchet — flagged in the PR body for the maintainer.",
        "origin/main moved from 587bd969 (dispatch) to d87dff67 (base, fetched at start) and then to c512c255 during the run; the branch is based on d87dff67 and was not re-merged.",
        "Labels on the PR not set by this run: `documentation` and `size/s` (labeler / size-labeler); left alone."
      ],
      "files_changed": [
        "skills/objectstack-automation/SKILL.md",
        "skills/objectstack-automation/evals/flows-triggers-approvals.json"
      ],
      "mcp_calls": "0 — none",
      "api_writes": "3 strokes / 4 REST writes, all through scripts/pm/ as objectstack-fleet[bot]: pr_create → POST /repos/objectstack-ai/objectstack/pulls (fleet-write relay run 37926484670, read back identical, 11468 bytes); label-write → POST /repos/objectstack-ai/objectstack/issues/22475/labels (skip-changeset) + POST .../issues/22475/assignees (os-elon-musk) (relay run 37926571692, read back ok); post-stamped → POST /repos/objectstack-ai/objectstack/issues/22454/comments (this report). git push ×2 (empty-branch probe, then 057ce436) — not REST.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: none (read-only inference) · SKILL.md:154 `The handler reads {NODE_ID.error} (or run-wide {$error})` names no slot; in a text slot it is now `{{ n1.error }}` / `{{ $error.message }}`, in a value-like position still single-brace; the door names the spelling loudly · carrier: 承接者:无 — noted in Acceptance notes, not filed",
        "class: none (polish) · the eval's token ceiling is lowerable 1,255 → 1,253 in scripts/check-skills-token-ratchet.mjs, outside this card's surface · carrier: 承接者:无 — noted, not filed",
        "class: none (read-only inference, no named producer) · `{{ $User.Id }}` in a text slot renders blank with ok: true and passes objectstack validate (validate-flow-template-paths.ts skips an unresolvable root; lint-flow-patterns.ts reads a bare `$` only outside holes); the skill paragraph now warns · dedupe: text slot $User hole blank validate · carrier: 承接者:无 — noted, not filed"
      ]
    }

    Posted 2026-10-09T11:55Z by the dev subagent of session_01JmWtcHfGbC4ncw4GFKWuRA; PR #22475 stays draft (Tier H).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #22475 (head 057ce436), Tier H, the four-piece set posted, landing on the maintainer's APPROVED — skills seat 1, session_01JmWtcHfGbC4ncw4GFKWuRA, 2026-10-09T12:01Z

    Verified on GitHub and on the fetched branch, not on the report: a draft PR against main, first line Fixes #22454 (the only closing keyword in the body), 2 files (+17/−18): the notify example now title: 'Done: {{ record.title }}' / message: 'Closed {{ record.closed_at | date:long }}' (measured on the built dists: accepted by NotifyConfigSchema, rendered by the engine; the old lines refused with the door's remedies); the dialect paragraph three statements, one door each, the false {{x}} clause gone; eval id 2 expects title: 'Won: {{ record.name }}' with {{ moved from must_not_contain to must_contain; the § Filter tokens bullets no longer list text slots as single-brace positions and the section's lede (restated at lines 161–162 and in its own second bullet) deleted to pay the token ratchet. SKILL.md 439 → 438 lines; token ratchet 5,783 / 5,785 and 1,253 / 1,255, shrink-only, exit 0. Pin: a text-slot value holding a lone { — 0 hits across skills/ on the head (2 on the base, the positive control). Gates: the dev's 24-family union pinned to 057ce436 (--ran 24/24, all exit 0); CI on the head at this write: four jobs still in_progress, nothing red — the landing waits for them and for the approval anyway. mcp_calls 0; api_writes 3 relay strokes (pr_create, label-write, the report 6080357455) — skip-changeset and the assignee read back. Commit authored by objectstack-fleet[bot]. Deviations accepted as listed in the review record (two mechanism assumptions corrected by measurement; the edits beyond the three sites are inside the claimed file and paid). NOT MEASURED: the skill eval run (no runner in this repository reads the assertions).

    Contract review PASS in seat at tier: 6080468777. Landing: Tier H (skills/**) — the four-piece set is posted in this act (this ACCEPT, the review record, needs-user-decision + 维护者速读 on the PR, review requested from os-zhuang and hotlong through the relay); on an authorized APPROVED the seat lands unless the approver already did; close-out is the seat's either way. Out-of-scope: the {{ $User.Id }} blank render in a text slot is filed for the spec lane (link follows on this card); the rest stays in Acceptance notes.

  5. objectstack-fleet commented on Oct 9, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #22475 MERGED by the approver, card closed by Fixes — skills seat 1, session_01JmWtcHfGbC4ncw4GFKWuRA, 2026-10-09T13:04Z

    Two readings, taken together: PR #22475 reads merged: true at 2026-10-09T13:02Z (commit 8b713fad783e23d4313fa7d291b88de0bde6efd7, merged through the merge queue after os-zhuang's APPROVED 2026-10-09T12:24Z — review 5469966482 — and their own ready flip, arm (merge) and enqueue at 12:27Z; thirty-five minutes in the queue), and that commit is an ancestor of origin/main as fetched in this act — a single-parent squash commit: the merge queue squashes with its own method whatever the arm said (the approver's arm read merge; shift 1's two-parent commit on PR #22382 was therefore not a queue merge of this kind) — the MERGED proof is by content, never by parent count. The two files are byte-identical to the PR head 057ce436 (git diff 057ce436 <merge commit> -- skills/objectstack-automation/SKILL.md …/evals/flows-triggers-approvals.json is empty); the published skill now teaches the {{ }} delimiter in the flow text slots, the lone-{ text-slot pin reads 0 across skills/.

    Tier H landing by the maintainer's authorized approval on this seat's four-piece set (contract review PASS 6080468777, ACCEPT 6080477431, 维护者速读 6080482877, review requested from both authorized approvers). The card closed completed by Fixes; pm:dispatched and the assignee are stripped in this act, and so is the PR's needs-user-decision. Timings: claim → draft PR 35 min; four-piece set → APPROVED 22 min; APPROVED → MERGED 38 min. The out-of-scope finding is filed as #22477 (spec lane, the {{ $User.Id }} blank render). Nothing else is owed on this card.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itdomain:skillspriority:p2Medium: important, M3target:v18

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions