Append-only architectural and product decisions for ytstack. Never rewrite past entries. If a decision is reversed, add a new entry that supersedes.
Format for each entry:
Context: what forced the decision Options considered: A, B, C Chose: selected option Reason: why Supersedes: link to earlier entry if this reverses a prior decision
Context: GSD v2 is a substantial TypeScript application (CLI, SQLite, IPC, TUI). Rebuilding all of it inside ytstack is months of work.
Options considered:
- A) Full rebuild as ytstack-runtime
- B) Vendor GSD as optional external tool alongside ytstack
- C) Replicate GSD's artifact-discipline + context-management via skills + hooks, skip the runtime
Chose: C
Reason: Claude Code's native Agent Teams feature already provides fresh 200k-context-per-task and shared task list with file-locking -- the core value GSD's runtime adds. Combined with hooks (SessionStart/TeammateIdle/TaskCompleted) and skill-managed .ytstack/ artifacts, we replicate ~95% of GSD's value without a runtime app. User explicitly ruled out B ("zusaetzlich installieren, das wird zu viel").
Context: External AI-first methodology material (German-language, proprietary/copyrighted) contains compelling concepts (three-tier context model, component breakdown, skill-playbook structure, skill catalog) relevant to ytstack's design.
Options considered:
- A) Vendor the source material with attribution
- B) Only adapt concepts, re-implement in own prose, no source naming
- C) Skip external influence entirely
Chose: B
Reason: User explicit: "bitte nichts [...] 1:1 kopieren (keine binaries)" and later "namentliche Referenz [...] entfernen". Concepts aren't copyrightable, the source's text/images/graphics are. We take the structural ideas (tier-based context, artifact-as-memory, playbook-based skill creation), write our own prose, credit generically via NOTICE as "inspired by external AI-first methodology work" without naming the source.
Context: ytstack could be (1) a Claude Code plugin with skills/hooks/commands, or (2) an Agent SDK-based library that replaces parts of Claude Code.
Options considered:
- A) Skills-based plugin (current approach)
- B) Agent SDK library with custom orchestrator
- C) Hybrid (plugin for skills, optional SDK for headless use)
Chose: A
Reason: Lower barrier to adoption (one-line install vs SDK integration). Claude Code plugins are the native extension point; fight the platform less. Keeps ytstack shareable via marketplace. Option C can be added later if demand appears.
Context: superpowers and gstack each have their own UX patterns that feel consistent, but neither validates them mechanically. Drift happens.
Options considered:
- A) Codify UX rules in docs only, rely on contributor discipline
- B) Codify + write a
ytstack-skill-checkCI tool that validates every skill - C) Generator-based approach (skill templates with substitution, like gstack's SKILL.md.tmpl)
Chose: B
Reason: gstack uses C and it works, but adds a build step and mental model complexity. B is simpler: write skills as plain markdown, validator enforces contract on commit. Can upgrade to C later if skill count grows past ~30.
Context: Where should project memory artifacts live? In the repo (git-tracked) or in user-home (private)?
Options considered:
- A) Project-only (./.ytstack/ in repo, committed)
- B) User-only (~/.ytstack/projects//)
- C) User chooses per project (init-project asks)
Chose: C with A as the recommended default.
Reason: Different projects have different needs (open-source shareable vs solo secret project). Asking costs ~10 seconds, making the wrong default costs data loss or accidental public commits. Project-level recommended because most builders want team-shared context and survival-against-hardware-failure.
Context: Claude Code's Agent Teams feature is the core mechanism we rely on for fresh-context-per-task (replacing what GSD's TypeScript runtime does). It is marked experimental, requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, and needs v2.1.32+. Documented limitations: no session resume with in-process teammates, task-status-lag, no nested teams, no session transfer of lead role.
Options considered:
- A) Wait for stable release before building M006
- B) Build on experimental API, pin version, document risk
- C) Build without Agent Teams, use regular subagents (reduced parallelism)
Chose: B
Reason: Waiting for stable could be months; the feature is core to our value prop. Pinning Claude Code version in our plugin manifest + a prominent README warning + graceful fallback to subagents (M006) if teams are unavailable mitigates most risk. Experimental APIs moving under us is the price of being early.
How to apply: M006 skills MUST detect CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS and Claude Code version in preamble, emit clear guidance if missing, fall back to subagent-based workflow if teams unavailable.
Context: superpowers and gstack each offer many skills. Vendoring all of them bloats the plugin, conflicts with other plugins, and increases maintenance. We need a triage.
Options considered:
- A) Vendor everything, disable via config
- B) Vendor only the skills with documented production-value evidence
- C) Vendor nothing, write all skills from scratch
Chose: B
Reason: Production evidence from independent comparison articles flagged specific skills as highest-value: plan-ceo-review (gstack) as "most valuable" for forcing requirement clarification; test-driven-development (superpowers) for measurable regression reduction; systematic-debugging and verification-before-completion as execution foundations. Start with these 5-6, add more only when a real use case appears.
How to apply: M004 and M005 wrapper skills target only the proven set. Other skills from upstream stay vendored (for git-subtree sync simplicity) but ytstack does not surface them until there's a case.
Context: An independent comparison article reported that superpowers' interactive prompts (within its brainstorming/writing-plans flow) can block Claude Code's input during builds. We haven't reproduced it in a controlled test. This is documented as a known risk, not a settled bug.
Options considered:
- A) Reproduce the bug before building anything with superpowers skills
- B) Build wrappers that sidestep the interactive pattern (our wrappers invoke superpowers in non-interactive mode where possible)
- C) Skip superpowers execution skills entirely
Chose: B, with a verification task scheduled in M005.
Reason: The skills are too valuable to skip, but we must not inherit the bug. Our wrappers will pass YTSTACK_NON_INTERACTIVE=1 or equivalent to downstream superpowers invocations when running inside an Agent-Teams teammate or inside an automated workflow. M005 includes an explicit verification task to reproduce and characterize the original bug before we ship.
How to apply: Every superpowers wrapper MUST explicitly set non-interactive mode for orchestrated contexts. Verification task in M005 reproduces + documents the original failure mode.
2026-04-24: Add using-ytstack skill + SessionStart-hook-injected directive for agent-driven skill selection
Context: Initial M002 SessionStart hook injected only project state (milestone / slice / task position + recent summaries). It did NOT tell the agent it should auto-invoke ytstack skills. Result: ytstack behaved as a slash-command menu the user had to drive manually. superpowers' magic is that their SessionStart hook reads the full using-superpowers/SKILL.md content and injects it as a forceful directive -- the agent then proactively reaches for skills based on natural-language user intent.
Options considered:
- A) Rely on Claude Code's native skill-description matching. Every ytstack skill already has a
descriptionfield -- Claude should pick up on them based on context. - B) Add a
using-ytstackskill with a trigger map (phrase → skill), Red Flags anti-rationalization table, and EXTREMELY-IMPORTANT directive. Update SessionStart hook to inject its content alongside project state. - C) Add slash-command aliases for common phrases (e.g. auto-map "where were we" to
/ytstack:resume-sessionvia a preprocessor).
Chose: B.
Reason: M001 T05 smoke test already revealed Claude Code's native skill-description matching does NOT reliably trigger in headless mode (Skill-tool-invocation errored as "not registered in the harness"). superpowers' pattern proves the injected-directive approach works -- their ~40 skills reliably auto-fire because using-superpowers primes the agent with compliance pressure ("1% chance a skill applies → invoke"). Option A alone is not strong enough. Option C would surface user-level string-matching that conflicts with Claude Code's own slash-command-parser. Option B follows the proven superpowers pattern.
How to apply:
skills/using-ytstack/SKILL.mdcontains the trigger map (natural-language phrase → ytstack skill), EXTREMELY-IMPORTANT directive, and anti-rationalization Red Flags.hooks/session-startreads and injects its full content asadditionalContextJSON, wrapped in an<EXTREMELY_IMPORTANT>envelope, BEFORE the project state block.- Total injected context per session start is ~8.5KB -- non-trivial but within Claude Code's context budget.
- The README reframes ytstack as agent-driven: user talks naturally, agent auto-fires skills; slash-commands are the steering override for non-happy-path cases.
Context: /check-consistency 2026-04-24 flagged that README.md contradicts itself and disk on the skill count. README:131 said "15 skills", README:145 said "14 skill packages", disk has 16 directories under skills/. docs/concept.md §9 had pre-flagged this drift. Root cause: using-ytstack lives as a SKILL.md under skills/ but carries kind: directive and description "Not meant for direct user invocation", which opens a semantic question whether it should count as "a skill ytstack ships".
Options considered:
- A) Flat count -- every SKILL.md under
skills/counts as one skill. README says "16 skills". - B) Role-split count -- README says "15 user-invocable skills + 1 internal directive".
- C) Restructure -- move
using-ytstackout ofskills/intohooks/so it is not a skill at all. Breaks 1:1 parallelity with superpowers'using-superpowers.
Chose: A.
Reason: User-facing README numbers answer "how big is this thing"; one number is the useful answer. The directive-vs-action distinction lives in the skill's own frontmatter (kind: directive) and description, which is sufficient for internal tooling. Option C was rejected after checking vendor/superpowers/hooks/session-start (2026-04-24): superpowers uses the exact same shape we do (SKILL.md read via cat, injected as additionalContext JSON), and the pattern rides on an officially documented Claude Code API (SessionStart hook + additionalContext). Deviating from the proven pattern to paper over a Claude-Code-side feature gap (no manifest flag for invocable: false) would force a reverse migration the moment Anthropic ships such a flag. Option B surfaces internal semantics in user-facing prose for no reader benefit.
How to apply:
- Every directory under
skills/that contains aSKILL.mdcounts as one skill in user-facing docs. - README.md currently at "16 skills" (§Status) and "16 skill packages" (§Repo layout);
/check-consistencyenforces this on every run. docs/concept.md§9 drift bullet removed; the live/check-consistencyrun is now the enforcement surface.- If Claude Code later ships
invocable: falseor equivalent in the plugin manifest, revisit this decision via a superseding entry and consider surfacing the directive/invokable split in README prose.
Context: Original M008 planning assumed a two-repo design: Yesterday-AI/ytstack (plugin source) plus a separate Yesterday-AI/ytstack-marketplace (marketplace manifest pointer). Research against Claude Code's official plugin-marketplace docs (2026-04-24) confirmed .claude-plugin/marketplace.json inside the plugin repo is natively supported as a "self-marketplace" -- no separate marketplace repo required. Keeping two repos would be duplicate maintenance for zero user benefit.
Options considered:
- A) Two-repo design:
Yesterday-AI/ytstack+Yesterday-AI/ytstack-marketplace. Marketplace.json only in the marketplace repo. - B) Self-marketplace: one repo (
Yesterday-AI/ytstack) holds bothplugin.jsonandmarketplace.jsonunder.claude-plugin/, with the marketplace entry'ssourcepointing at the same repo ("./.").
Chose: B.
Reason: Officially documented in Claude Code's plugin-marketplaces reference; removes one repo's worth of maintenance + sync overhead; collapses install to a single reference (/plugin marketplace add Yesterday-AI/ytstack && /plugin install ytstack@ytstack-marketplace). No downside surfaced during research. Applied in-session: README, QUICKSTART, docs/references.md, .ytstack/ROADMAP.md, RUNTIME.md, REVIEW-NOTES.md, STATE.md all reconciled; marketplace.json rewritten with source: "./." and stale $note removed.
How to apply:
- Install command everywhere:
/plugin marketplace add Yesterday-AI/ytstack(plugin repo itself, not a separate marketplace repo), followed by/plugin install ytstack@ytstack-marketplace(marketplace name from the json). .claude-plugin/marketplace.jsonuses"source": "./."for self-reference.- If a future decision adds community-maintained marketplaces (e.g. a Yesterday-wide or Anthropic-official marketplace listing), they can also reference ytstack -- this decision only commits that the primary, vendor-owned marketplace lives in-repo.
Context: Six plugin skills (test-driven-development, systematic-debugging, office-hours, plan-eng-review, verification-before-completion, plan-ceo-review) and one project-meta skill (check-consistency) used ${CLAUDE_PLUGIN_ROOT:-/Users/alex/Sync/home/alex/Code/WebDev/projects/yesterday-ai/ytstack} as the vendor-skill path resolution pattern. The fallback was Alex's machine-local absolute path, which would break on any other machine if the env var was unset.
Options considered:
- A) Leave hardcoded absolute fallback.
- B) Fail-fast with
${CLAUDE_PLUGIN_ROOT:?msg}for plugin skills; portable fallback${CLAUDE_PROJECT_DIR:-$PWD}for project-meta skills. - C) Derive plugin root from the running script via
$0/BASH_SOURCE.
Chose: B.
Reason: Plugin skills only ever run inside Claude Code, where CLAUDE_PLUGIN_ROOT is set by the harness. If it's ever missing, the right behavior is an explicit error, not silent wrong-path execution against someone else's filesystem. Option C is brittle because SKILL.md bash blocks don't have a reliable $0 from Claude Code's execution context. Project-meta skills (only check-consistency so far) use CLAUDE_PROJECT_DIR with $PWD fallback, matching the existing pattern in other ytstack preambles.
How to apply:
- Plugin skills:
"${CLAUDE_PLUGIN_ROOT:?CLAUDE_PLUGIN_ROOT not set}/vendor/..." - Project-meta skills (under
.claude/skills/):"${CLAUDE_PROJECT_DIR:-$PWD}" - Never reintroduce absolute user-machine paths as fallbacks.
Context: Follow-up to earlier 2026-04-24 "ytstack self-marketplaces". The marketplace.json "name" field was still "ytstack-marketplace", a leftover from the original two-repo plan. Produced the confusing install command /plugin install ytstack@ytstack-marketplace -- readers asked where the -marketplace suffix came from when there is no separate marketplace repo.
Research: Checked upstream convention via vendor/superpowers/.claude-plugin/marketplace.json -- superpowers uses "superpowers-dev" (plugin name + channel suffix, not -marketplace). Official Claude Code docs do not mandate a naming convention; reserved names exist but third-party marketplaces pick freely.
Options considered:
- A)
"ytstack"-- match the plugin name. Install:ytstack@ytstack. - B) Keep
"ytstack-marketplace". Misleading now that no separate marketplace repo exists. - C)
"ytstack-dev"-- mimic superpowers, implies a dev channel. We do not have stable-vs-dev channels.
Chose: A.
Reason: Simplest, no implied channel distinction, no stale -marketplace suffix. The install command reads as "plugin ytstack from marketplace ytstack" -- redundant but unambiguous. Matches the self-marketplace spirit: one repo, one marketplace, minimum naming overhead.
Supersedes: the "How to apply" bullet in "2026-04-24: ytstack self-marketplaces, no separate -marketplace repo" that specified ytstack@ytstack-marketplace. New install: /plugin install ytstack@ytstack.
Context: The initial using-ytstack/SKILL.md included a trigger-map table mapping explicit user phrasings ("baue mir X", "build me X", "new project", ...) to ytstack skills, plus a "Greenfield flow" section encoding a specific 6-step chain. Both were added by the agent during the using-ytstack authoring pass on 2026-04-24 (documented in the "Add using-ytstack skill" decision earlier today) as a defensive workaround for headless-mode skill-tool registration failures observed in M001 T05.
Review 2026-04-24 revealed two problems:
- Phrase-matching is an antipattern. Every new user phrasing (German, paraphrase, domain-specific word) needs an explicit table entry; otherwise the agent silently misses. Fragile by construction. User feedback: "ich glaube den workflow an phrasen zu matchen ist ein unkontrollierbares antipattern."
- Other Claude-Code plugins (superpowers, claude-md-management, code-review, security-guidance) do NOT use trigger-map tables. They rely on the
description:field in each skill's frontmatter, which Claude Code's model matches semantically against user intent. Docs confirm: "description - What the skill does and when to use it. Claude uses this to decide when to apply the skill." - The M001 T05 headless-mode evidence that motivated the trigger-map did not apply to interactive-mode: superpowers has ~40 skills in production use without a trigger-map. The workaround was applied to the wrong layer.
Options considered:
- A) Keep trigger-map (status quo). Pro: explicit routing for known phrasings. Con: brittle, requires maintenance per new phrasing, fragile-by-construction.
- B) Remove trigger-map entirely, rely on skill
description:semantic matching + using-ytstack directive pressure (the superpowers pattern). Pro: matches documented Claude Code mechanism, handles unknown phrasings naturally, less maintenance. Con: requires each skill to have a rich, context-rich description; testing is LLM-judgment-based rather than deterministic. - C) Hybrid: keep trigger-map as "fast path", fall back to description-matching. Pro: both paths available. Con: two codepaths to maintain; the trigger-map path becomes an authoritative-seeming-but-incomplete list that drifts out of sync with descriptions.
Chose: B.
Reason: The trigger-map is the mechanism that superpowers deliberately does not have. Description-matching is how Claude Code skills are meant to be selected per documentation. Adding a phrase-list on top is scope-creep (same pattern as the banned-words list that was rolled back in the previous DECISIONS entry). It also reduces context-budget pressure on every session start because the directive becomes smaller.
How to apply:
skills/using-ytstack/SKILL.mdrewritten without the trigger-map table and greenfield-flow section. Kept: SUBAGENT-STOP, EXTREMELY-IMPORTANT, instruction priority, the rule (now description-first), skill priority (process / structural / execution), Red Flags table, process flow DOT. Version bumped to 0.2.0.- Five skill
description:fields sharpened to encode greenfield-vs-milestone context + ordering (office-hours, init-project, plan-ceo-review, plan-eng-review, plan-milestone). Each description now states situational when-to-use that semantically covers expected user phrasings without listing keywords. - Added
using-ytstack/SKILL.mdversion bump (0.1.0 -> 0.2.0) reflecting the structural change. - Added a design principle to README.md: "Skill selection via semantic descriptions, not keyword matching."
Supersedes: the trigger-map and greenfield-flow portions of "2026-04-24: Add using-ytstack skill + SessionStart-hook-injected directive for agent-driven skill selection". The SessionStart-hook-injection pattern and 1%-rule directive stand. The trigger-map portion is retired.
2026-04-24: Greenfield-flow reorder -- office-hours → plan-ceo-review → [plan-eng-review] → init-project → plan-milestone
Context: Smoke test 2026-04-24 in a greenfield dir with marketplace-installed plugin exposed the greenfield-flow first-skill miss concretely. Prompt "baue mir eine cli die csv-files liest und in postgres laedt" auto-invoked ytstack:plan-milestone (semantically matched "plan a milestone" / "what's next" rows) instead of any project-validation or infra-setup entry. Four combined root problems:
- Trigger map: init-project triggers only on literal "init ytstack" / "new project" phrasings, missing natural build-intent like "baue mir X" / "build me X".
- Skill ordering: plan-ceo-review + plan-eng-review are milestone-scoped (read
M###-CONTEXT.md+M###-ROADMAP.md), cannot validate a project concept before any milestone exists. - init-project mixes infra-setup (scope decision, skeleton files) with PM content (name, one-liner) -- forces the user to invent pitch cold during infra-setup.
- Even the currently-documented greenfield flow (QUICKSTART: init-project → plan-milestone → [optional plan-ceo-review]) is not reliably matched by agent behavior after the marketplace install.
REVIEW-NOTES 2026-04-24 "Greenfield-flow first-skill is wrong" and docs/concept.md §5.1 "Open design point" already proposed the target flow:
office-hours (concept validation)
→ plan-ceo-review (premise + scope challenge)
→ [optional] plan-eng-review (architecture review)
→ init-project (infra-only: scope decision + skeleton files)
→ plan-milestone
Options considered:
- A) Surface-fix only: extend init-project trigger-map for build-intent phrasings ("baue mir X", "build me X"). Fastest. Leaves ordering, init-project split, and plan-ceo-review concept-mode unresolved.
- B) Partial: add dual-mode (concept + milestone) to plan-ceo-review + plan-eng-review; update trigger map for greenfield entry; leave init-project unchanged. Half-measure.
- C) Full: (1) dual-mode plan-ceo-review + plan-eng-review (concept + milestone), (2) init-project refactor -- PM questions removed, PROJECT.md name/one-liner populated from office-hours output instead, (3) office-hours' Terminal State points at plan-ceo-review, which points at plan-eng-review (optional) or init-project, which points at plan-milestone, (4) using-ytstack trigger map rewritten for greenfield routing, (5) README.md / QUICKSTART.md / docs/concept.md §5.1 updated for the new flow.
Chose: C.
Reason: Workflow orchestration is the foundational value of ytstack, not a deferrable polish. Shipping A or B would lock in the wrong default: every greenfield user lands in plan-milestone (or init-project with cold PM questions) before the project premise is validated -- contradicting ytstack's own methodology. The REVIEW-NOTES proposal is already the target; we simply haven't paid the implementation cost yet. User explicit 2026-04-24: "die orchestrierung des workflows ist die GRUNDLAGE fuer dieses plugin, das KOENNEN WIR NICHT AUFSCHIEBEN".
How to apply:
- New milestone M010 "Greenfield Flow Reorder" planned next via
ytstack:plan-milestone, sliced viaytstack:slice-milestone. - Artifacts in scope:
skills/office-hours/,skills/plan-ceo-review/,skills/plan-eng-review/,skills/init-project/,skills/using-ytstack/(trigger map),README.md,QUICKSTART.md,docs/concept.md§5.1. - Out of scope: no changes to downstream lifecycle skills (plan-milestone, slice-milestone, plan-task, summarize-task, reassess-roadmap, handoff-session, resume-session).
- Exit criterion: re-run same greenfield smoke test ("baue mir eine cli...") routes to
office-hoursfirst (not plan-milestone), and the documented flow in QUICKSTART matches observed agent behavior. - Supersedes: the implicit ordering in QUICKSTART.md §1-6 and docs/concept.md §5.1. Those files are updated as part of M010 execution.
Context: Audit 2026-04-24 revealed that existing ytstack "wrappers" (plan-ceo-review, plan-eng-review, office-hours, test-driven-development, systematic-debugging, verification-before-completion) mix prose indirection ("read vendor file and follow it") with partial adaptation / fork of vendor content. Worst case: verification-before-completion is 97% the size of the vendored original -- quasi-copy, not wrapper. Three of the six are thin (5-7% of vendor size), but even those use prose indirection rather than any CC-native mechanism. Both approaches violate the vendor-as-single-source-of-truth rule and will drift on upstream updates.
Options considered:
- A) Shell-exec content injection: wrapper body ends with
`!`cat ${CLAUDE_PLUGIN_ROOT}/vendor/.../SKILL.md`to inline the vendor procedure verbatim at render time, with ytstack-specific context prepended. Documented CC feature. - B) plugin.json
"skills"as array pointing at vendor/ dirs. Unverified whether the schema supports arrays, and no context-injection possible before vendor-procedure runs. - C) Stop wrapping -- ytstack ships only Project-OS skills, users install superpowers + gstack separately as their own plugins. Contradicts the README "one install" claim; loses ytstack-context-aware invocation.
- D) Symlinks in
skills/pointing tovendor/<name>/. Works but offers no context-injection hook. - paperclip
metadata.sources[]+usage: referenced: spec-level wrap-vocabulary (agentcompanies.io/specification#external-references-and-pinning) but Claude Code has no native runtime that fetches / inlines / merges these references. Would require custom build tooling.
Chose: A.
Reason: Only A enforces vendor-as-SSOT (automatic flow-through of ./sync-upstream.sh updates) while preserving the ytstack-context-injection we need (milestone file paths, STATE.md values, post-process instructions like "log scope decisions to DECISIONS.md"). D solves SSOT but loses context hook. B+C are either unverified or contradict positioning. metadata.sources is attractive conceptually but requires building our own runtime, which is out of scope for the current milestone. User explicit 2026-04-24: "shell inject kommt dem am naechsten, dann koennen wir anders als bei symlinks noch ein paar meta directives geben".
How to apply:
- Every wrapper SKILL.md becomes a thin file containing: minimal frontmatter (name + description + allowed-tools), a preamble
```!block that loads ytstack context (_YT_DIR,_CURRENT_MILESTONE, relevant artifact paths), a prose "ytstack invocation notes" section with milestone-specific context + post-process instructions, ending with```! \ncat "${CLAUDE_PLUGIN_ROOT:?}/vendor/<src>/SKILL.md"\n```to inline the vendored procedure. - Wrappers rewritten in scope of this change:
plan-ceo-review,plan-eng-review,office-hours(vendor/gstack),test-driven-development,systematic-debugging,verification-before-completion(vendor/superpowers/skills). - Cross-ref check: extend
bin/ytstack-checkwith a new validator that parses each vendored SKILL.md referenced by a wrapper, finds skill-name mentions inside the vendor text, and flags any reference that resolves to neither a ytstack-shipped skill nor a wrapped vendor skill. Output: REVIEW-NOTES drop-in with exact file:line of the dangling reference, so the human can decide (ship-additional-wrapper / rewrite-vendor-ref / accept-gap). - Scope impact on M010: merged into the greenfield-flow-reorder milestone as "Part 1: Wrapper refactor (6 skills thin-wrapped + cross-ref check added)", "Part 2: Greenfield-flow reorder (office-hours first, dual-mode ceo/eng, init-project split)".
2026-04-25: Tagline -- "An opinionated OS for AI coding agents. Plan like a PM, execute like a senior eng."
Context: Original tagline ("Working memory for AI coding agents.") captured only the GSD-inspired persistence layer and undersold the other two pillars (curation of gstack + superpowers; execution discipline via TDD / systematic-debugging / verification gates). User flagged: "ist 'Working memory for AI coding agents.' wirklich die beste tagline?"
Options considered:
- A) Keep "Working memory for AI coding agents." -- punchy, memorable, but lopsided.
- B) "An opinionated OS for AI coding agents." -- covers all three pillars, generic on its own.
- C) "Plan like a PM, execute like a senior eng -- with AI agents." -- plakativ, already in the README body as a claim.
- D) Compound: "An opinionated OS for AI coding agents. Plan like a PM, execute like a senior eng." -- positioning + framing in two sentences.
Chose: D.
Reason: The compound form is the only one that lands all three positioning angles (curation, memory, discipline) without dropping the punch. Two short sentences read fine in the README header <em> slot and in docs/concept.md §1.1. Em-dash dropped per writing-style.md (the original -- candidate Plan like a PM, execute like a senior eng -- with AI agents had a dangling redundancy with the leading "AI coding agents" phrase, which D eliminates).
How to apply:
- README.md header
<p><em>...</em></p>updated. docs/concept.md§1.1 (What) updated to keep concept paper in sync.- Future external surfaces (GitHub repo description, marketplace.json description, social previews) should mirror this exact phrasing or its short form ("An opinionated OS for AI coding agents.").
marketplace.jsondescription currently reads "Yesterday Technologies Stack -- opinionated software-development OS for AI agents." which is consistent in spirit; do not edit defensively, but next time it touches, align fully.
2026-04-25: README workflow diagrams use collapsible <details> blocks; greenfield expanded by default
Context: The three workflow infographics (greenfield, brownfield, debugging) are large PNGs. Embedding all three inline pushes the README's body content (Install / Compared-to / How-it-works) below the fold and overwhelms first-time readers.
Options considered:
- A) Inline all three PNGs (status quo before this decision).
- B) Collapse all three behind
<details>-- cleanest, but hides the primary visual on first load. - C) Expand greenfield (the canonical "what is this?" diagram) by default, collapse brownfield + debugging.
Chose: C.
Reason: Greenfield is the on-ramp for first-time readers and matches the README's sequencing (Why -> What it does -> Workflows -> Install). Brownfield + debugging are reference material for users who already know ytstack's shape, so collapsing them keeps the page scannable without burying the primary diagram. User explicit: "kannst du das greenfield standard ausgeklappt machen?"
How to apply:
<details open>for the greenfield section, summary text reads "click to collapse".- Plain
<details>for brownfield + debugging, summary text reads "click to expand". - All three
summarylines use<strong>for visibility. - This convention applies to README only. QUICKSTART.md and concept.md don't embed images.
Context: docs/concept.md §3.7 defines three "should this be a skill?" gates (distinct artifact, distinct from siblings, semantic description). Two unresolved questions remain after the 2026-04-25 architecture discussion: (a) ytstack covers planning -> execution -> close but stops before ship / post-deploy; (b) where do tool-style skills (browser wrappers, diagram tools) belong if ytstack adds them. Both resolve to "where does this skill live -- ytstack core, sibling plugin, or out-of-scope?"
Options considered:
- A) Add a 4th gate to §3.7 ("which dev-loop phase?"); ytstack core covers all phases.
- B) Hard-cap ytstack at "plan-to-close" (no ship, no post-deploy); rely on user to pair gstack standalone.
- C) Lifecycle-phase as pre-classification heuristic: skill needing
.ytstack/-state -> ytstack core; generic tool-wrapper -> sibling plugin under ystacks; Yesterday-internal -> separate org-internal plugin.
Chose: C.
Reason: ytstack is committed to full dev-loop coverage (user explicit 2026-04-25: "ytstack full loop ja/nein ist keine frage -> definitiv ja, das war schon immer der plan"). The earlier "tool-skills brueche scope" framing was wrong -- skills like browse, qa, ship are methods with embedded tooling, not raw tools. The real sort is "does it need ytstack-state?", which subsumes the ship-gap question (ship reads STATE/SUMMARY/DECISIONS -> ytstack) and the future excalidraw-style question (no ytstack-state needed -> sibling plugin under ystacks).
How to apply:
- Update §3.7's first gate to ask "does this skill require
.ytstack/artifacts to make sense?" before the existing artifact / distinctness / description gates. - Add §3.7.1 "Where to ship": skills needing ytstack-state ship in ytstack core; generic tools ship in a sibling plugin listed under
ystacks; Yesterday-internal tools ship in a separate plugin not listed publicly. - Adding ship as ytstack core skill is now unambiguous (lifecycle gap, requires ytstack-state).
- Adding excalidraw-style tool-skills to ytstack is now disallowed (no ytstack-state); they belong in a sibling plugin.
Supersedes: none. Extends §3.7 (Curation principle) without retracting it.
Context: 2026-04-24 "ytstack self-marketplaces, no separate -marketplace repo" (concept §3.5) was scoped to single-plugin state with the explicit "consolidate when sibling emerges" caveat. 2026-04-25 architecture session locked: full-loop ytstack (ship + post-deploy land here), four planned sibling plugins (ydstack daily-work, ycstack consulting separate-track, yastack autonomous-agent core, yastack-internal yesterday-bundle), and Yesterday-internal service-repos managing their own skills + plugin manifests.
Options considered:
- A) Self-marketplace per plugin -- N marketplaces for N plugins.
- B) Single shared catalog repo (
Yesterday-AI/ystacks) listing each plugin via github source only. Plugins keep own repos. - C) Pure monorepo: collapse ytstack + all future plugins into one repo with
metadata.pluginRoot. Loses independent versioning + history for plugins that need it. - D) Hybrid monorepo + catalog: ystacks contains BOTH the marketplace catalog AND some plugins as
plugins/<name>/subdirs, while listing additional plugins from external repos via github source. Per-plugin decision: own repo (when independent visibility / release / lifecycle needed) or subdir (when shared lifecycle is fine). - E) Two marketplaces: public ystacks, private yistacks for infra-bound tools.
Chose: D with Yesterday-AI/ystacks as the single private monorepo + catalog.
Reason: E abandoned -- "internal" tools are functionally infra-bound, not secret; mixed-visibility marketplace listing leaks plugin names + descriptions while users cannot install. D beats B because: less repo proliferation (1 monorepo vs N plugin-repos), atomic cross-plugin commits, shared issue/PR/CI surface for plugins maintained by Yesterday-team. Plugins that need separate visibility or lifecycle (yastack public; ytstack pre-existing with own .ytstack/ self-tracking; service-repos that exist for the service itself) live in own repos and are listed via github source. The -internal suffix (today: yastack-internal) is the convention for yesterday-bundle plugins -- a thin manifest with dependencies array, no skills of its own.
How to apply:
Yesterday-AI/ystacks(private monorepo + catalog) scaffolded 2026-04-25. Contains:.claude-plugin/marketplace.json,plugins/ydstack/,plugins/yastack-internal/. README documents the visibility / source matrix.ytstackstays in own repoYesterday-AI/ytstack(private vorerst, public-tauglich -- decision deferred), listed in ystacks via github source.ydstack,ycstack(later),yastack-internal-- live asplugins/<name>/subdirs in the ystacks monorepo.yastack-- own public repoYesterday-AI/yastack(planned), 15 generic agent skills, no Yesterday-infra deps. Listed in ystacks via github source. External users can install via direct github source without ystacks auth.- Yesterday service-repos (clawrag, llm-gateway, paperclip-companies, agent-services, openclaw, ...) -- bekommen
.claude-plugin/plugin.json+skills/<name>/SKILL.mdwenn ready, listed in ystacks via github source. Service-team owned skills. agentic-foundationrepo serves as source pool for ydstack + yastack skill migration; its post-migration purpose is undecided (archive vs repurpose).-internalsuffix is the convention for yesterday-bundle plugins. May graduate to a separateystacks-internalmarketplace if the count grows past ~3.- ytstack's own
.claude-plugin/marketplace.jsonremains functional (legacy install path) until a future DECISIONS entry deprecates it. - Cross-marketplace deps (
allowCrossMarketplaceDependenciesOn) NOT needed -- everything in one ystacks marketplace. - docs/concept.md §3.5 update is deferred to a separate change so this DECISIONS entry lands first as the source-of-truth.
Supersedes: "ytstack self-marketplaces, no separate -marketplace repo" (2026-04-24, concept §3.5). The "pragmatic, not permanent" caveat there is now invoked.
Context: Wrapper mechanism (2026-04-24 "shell-exec inject + cross-ref check") inlines vendored SKILL.md verbatim via cat ${CLAUDE_PLUGIN_ROOT}/vendor/<src>/SKILL.md. Vendored gstack skills carry deep gstack-specific preambles that source ~/.claude/skills/gstack/bin/gstack-update-check, gstack-config, gstack-repo-mode, gstack-telemetry-log. When ytstack inlines these, the binaries don't exist (gstack isn't standalone-installed in the ytstack-only case), every call fails silently via || true, and the inlined skill loses its gstack-side context (REPO_MODE detection, proactive flag, telemetry, explain-level). Identified during 2026-04-25 evaluation of ship as a candidate wrapped skill.
Options considered:
- A) Accept silent failure. ytstack injects its own context (project state, milestone paths) via the wrapper preamble; the gstack-side config is orthogonal to ytstack workflow.
- B) Patch vendored preamble. Direct violation of "Never modify vendored content" hard rule (CLAUDE.md). Drifts on every
sync-upstream.sh. - C) sed-strip the gstack preamble at
cat-time. Pseudo-modification via runtime filter; preserves vendor-as-SSOT but obscures the deviation. - D) Bundle ytstack-side stubs at
~/.claude/skills/gstack/bin/that no-op or return ytstack-equivalent values. Hijacks gstack's install path; breaks if user has standalone gstack installed.
Chose: A.
Reason: Lost gstack-side features (telemetry, REPO_MODE, proactive flag, explain-level) are gstack-internal UX, not behavior ytstack relies on. ytstack's own context-injection (_YT_DIR, _CURRENT_MILESTONE, artifact paths via wrapper preamble) is the relevant context. Preserves vendor-as-SSOT (no modification, no runtime filter), no install collision, and matches what already happens for the six existing wrapped skills (their gstack/superpowers preambles partially fail today; nothing breaks).
How to apply:
- Document in CLAUDE.md and docs/ux/skill-structure.md that wrapped skills with deep upstream-specific preambles WILL emit silent failures when their preambles run; this is by design.
bin/ytstack-checkadds a soft-warning when a wrapped vendor skill references upstream-specific binary paths (e.g.~/.claude/skills/gstack/bin/). Surfaced to maintainer, not blocking.- Wrappers MAY add their own preamble values that emulate critical upstream signals if a downstream procedure depends on them (case-by-case during per-wrapper authoring; not a blanket policy).
- Future ship wrapper is the first concrete test of this stance; concrete fall-out goes to REVIEW-NOTES.
Supersedes: none. Locks an aspect of wrapper mechanism (2026-04-24) that was implicit until now.
Context: Initial M011 framing (per the 2026-04-25 lifecycle-heuristic decision) was "ship + 1 post-ship skill (canary or document-release)". User requested a re-read of the two comparison articles cited in docs/concept.md (dev.to, medium) before locking the milestone scope. The articles clarified: gstack is the only framework with a complete release pipeline (ship / land-and-deploy / canary / document-release / qa); superpowers covers pre-merge closure (verification-before-completion + finishing-a-development-branch + requesting-/receiving-code-review); GSD has no shipping equivalent ("stabilizer, not builder").
Options considered:
- A) ship + (canary OR document-release) -- gstack-only, original scope, 2 skills
- B) ship + finishing-a-development-branch + requesting-code-review + receiving-code-review + document-release -- mixed cherry-pick, 5 skills (3 superpowers + 2 gstack), balances ship-mechanics with PR-review discipline
- C) ship + land-and-deploy + canary + document-release -- gstack-only complete pipeline, 4 skills, full deployment + monitoring coverage
Chose: B.
Reason: Matches ytstack's "non-overlapping best of both" pattern (concept §3). gstack owns ship-mechanics (VERSION / CHANGELOG / PR-creation / docs sync); superpowers owns PR-review-cycle discipline -- neither framework has the other's strength in the post-summarize arc. Option A leaves a gap (no PR-review skills between summarize and ship). Option C piles up gstack-preamble-drift exposure on 4 wrappers instead of 2 and pulls in deployment + monitoring -- a different skill-class (infra / observability) that belongs in a separate future milestone if demand emerges. B keeps land-and-deploy, canary, setup-deploy, qa explicitly out of M011 scope.
How to apply:
- M011 wraps 5 skills total: 2 gstack (
ship,document-release), 3 superpowers (finishing-a-development-branch,requesting-code-review,receiving-code-review). - Concept §3.6 "Future candidates (deferred)" no longer applies to
requesting-code-review/receiving-code-review-- they move into M011 active scope. Update §3.6 accordingly when M011 lands. - Order in normal flow:
summarize-task->finishing-a-development-branch->requesting-code-review->receiving-code-review(loop until approved) ->ship->document-release. - ROADMAP M011 entry updated with the 5-skill scope + cross-reference to this DECISIONS entry.
- All 5 wrappers fall under the "Vendored-preamble drift accepted" decision (2026-04-25); gstack-side preamble calls fail silently on
shipanddocument-release, ytstack injects own context via wrapper preamble.
Supersedes: none. Refines (not retracts) the M011 scope hint that lived inside the 2026-04-25 "Lifecycle-phase as the curation heuristic" entry; that entry only justified ship in core, not the specific cherry-pick.
2026-04-25: Marketplace architecture split into ystacks (public) + ystacks-internal (private), per-plugin own repos for plugins with architectural surface
Context: Earlier 2026-04-25 "Marketplace consolidates on Yesterday-AI/ystacks (monorepo + catalog hybrid)" established a single private ystacks repo as monorepo + catalog. User reviewed this later same day and identified two issues:
- Mixed-visibility marketplace listing leaks plugin names + descriptions of internal plugins to externals if catalog goes public; staying private locks externals out of public-tauglich plugins entirely.
- Per-plugin DECISIONS history is load-bearing -- bundle plugins (deps-only) and skill collections (no methodology) can live as subdirs, but plugins with real architectural surface (yastack, yopstack) deserve own repos so their DECISIONS can evolve independently.
Plus: ops-layer split during the same session (yopstack created as own public repo to host gstack ops-skills + opentofu migrated from yastack) created a new public plugin that needs a clean home.
Options considered:
- A) Stay with hybrid private monorepo + catalog -- accept either visibility-leak (going public) or no external discovery (staying private).
- B) Two-marketplace split with each architectural-surface plugin as own repo: rename existing
ystackstoystacks-internal(private), scaffold newystacks(public). Bundle plugins (yastack-internal, yopstack-internal) and skill collections (ydstack) live as subdirs in their respective marketplace repo. Plugins with own methodology/skills (yastack, yopstack, ytstack) live in own repos and are listed via github source. Cross-marketplace deps viaallowCrossMarketplaceDependenciesOnenable bundle plugins to dep on public plugins.
Chose: B (two-marketplace split + per-plugin own repos for architectural surface).
Reason: Cleanest visibility-split (no leak, full external discovery on public side). Per-plugin DECISIONS preserved where it matters (architectural surface). Wrapper bundles + skill collections stay as subdirs because their DECISIONS surface is captured at the catalog level. ytstack (this repo) stays own repo with its own .ytstack/, cross-listed in private ystacks-internal while it's still private; will move to public ystacks when it flips public.
How to apply (executed 2026-04-25 afternoon):
gh repo rename Yesterday-AI/ystacks ystacks-internal(done).- New public
Yesterday-AI/ystacksscaffolded (catalog + ydstack subdir transplant) -- ready to push. - New public
Yesterday-AI/yopstackscaffolded (ops-layer plugin, opentofu migrated from yastack + 3 gstack ops-skills planned) -- ready to push. Yesterday-AI/ystacks-internal(renamed) -- marketplace.json scope-flipped to private-only,allowCrossMarketplaceDependenciesOn: ["ystacks"]added, plugin entries: ytstack (cross-listed while private), yastack-internal (subdir), yopstack-internal (subdir, NEW). ydstack subdir transplanted out.yastack-internal/plugin.jsonupdated: yastack dep declared as cross-mp{ name: "yastack", marketplace: "ystacks" }.- New
yopstack-internal/subdir scaffolded in ystacks-internal: deps cross-mp on yopstack@ystacks + intra-mp on cloud (when ready). - yastack repo updated: opentofu out of skill list (moved to yopstack), companion section now references
ystacks-internal(notystacks). - All affected repos got their
.ytstack/DECISIONS.mdentries documenting their part of the split.
Supersedes: "Marketplace consolidates on Yesterday-AI/ystacks (monorepo + catalog hybrid)" (2026-04-25, earlier today). The "monorepo + catalog hybrid" choice held for ~6 hours before the visibility-split realisation; superseded by this two-marketplace split. The earlier "Lifecycle-phase as the curation heuristic" entry remains valid (the heuristic still applies; only the marketplace topology changed).
Context: issue #21 -- journal/sessions.jsonl records slice:"none"/task:"none" even mid-slice, because nothing writes active_slice back to STATE.md frontmatter. Code inspection: plan-task already COMPUTES the active slice (preamble fallback to the first open slice in the roadmap, then uses it for the task-plan frontmatter + status body line) but never persists it to the active_slice: frontmatter field. Separately, spawn-milestone-team runs native Agent Teams in a SHARED working tree, and teammates invoke plan-task/summarize-task per task -- so a naive single-active_slice write races across parallel teammates.
Options considered:
- A) slice-milestone sets active_slice at slice-plan creation. Rejected: slice-milestone creates ALL slices at once, has no notion of which slice is "entered".
- B) plan-task persists the already-computed active_slice (1 line next to the existing active_task write); summarize-task clears it in the existing all-tasks-
[x]branch. No guard. - C) Option B PLUS a
CLAUDE_AGENT_TEAM_MEMBERguard so teammates in a swarm never mutate STATE.md slice/task frontmatter.
Chose: C.
Reason: plan-task is the only lifecycle skill that already knows the active slice, so it owns the write -- minimal change, no new derivation logic, slice-milestone stays untouched. The single active_slice scalar is a SEQUENTIAL-mode concept; in parallel swarm execution there is no one active slice, and 4 teammates writing the same frontmatter field in a shared tree would race. Guarding the write on CLAUDE_AGENT_TEAM_MEMBER keeps issue #21's sequential journaling correct while preserving the parallel-execution model M012 itself depends on.
How to apply: M012-S02. plan-task: add active_slice: <old> -> active_slice: {ACTIVE_SLICE} to the STATE.md edit step, wrapped in an if [ -z "$CLAUDE_AGENT_TEAM_MEMBER" ] guard (same env var that drives _NON_INTERACTIVE elsewhere). summarize-task: in the existing "slice fully complete (all tasks [x])" branch, also clear active_slice -> none, same guard.
Context: spawn-milestone-team uses in-process Agent Teams sharing ONE working tree (no worktree isolation). summarize-task optionally stages git. Running M012's four slices in parallel would race on the shared git index, and a git add -A would sweep in unrelated dirty files (the repo currently has uncommitted hooks/pre-tool-use-edit from the #19 WT fix and an unrelated .claude-plugin/plugin.json change).
Options considered:
- A) Each teammate commits its own slice files path-scoped. Race-prone in a shared index; only safe with worktree isolation, which spawn-milestone-team does not use.
- B) Teammates implement + test + summarize but do NOT commit; the lead commits each slice path-scoped (
git commit -- <slice-files>) after that slice's verification passes.
Chose: B.
Reason: Serializes commits through a single actor (the lead), preserves the atomic-commit-per-logical-change rule, and prevents git add -A from capturing unrelated dirty files or half-finished teammate work in the shared tree. Aligns with the repo's hard git-safety rules (never blind git add -A, always path-scoped).
How to apply: M012 execution. spawn-milestone-team teammates: implement + test + summarize, no git commit/git add. Lead: after each teammate reports a verified slice, git commit -- <that slice's files> with the slice's atomic message. Never git add -A. Leave the pre-existing dirty plugin.json out of all M012 commits (out of scope).
Context: spawn-milestone-team currently dispatches parallel teammates into ONE shared working tree (native Agent Teams). For file-disjoint, independent slices this forces commit-race-avoidance by discipline (see 2026-05-31 "Swarm commit discipline") rather than by structure, and leaves the shared index exposed to git add -A accidents. User call: ytstack should ENCOURAGE worktrees at usage time -- when a user runs a swarm, each parallel teammate should get its own worktree by default. Isolation should be structural, not a rule the operator has to remember.
Options considered:
- A) Keep shared-tree default; only recommend worktrees in docs.
- B) Conditional: skill detects file-disjoint slices and recommends worktrees, shared-tree for coordination-heavy slices.
- C) Default-on: for >1 parallel slice, dispatch each teammate into its own worktree + merge step at the end, with an explicit opt-out flag for the shared-tree model.
Chose: C (default-on for parallel slices, opt-out to shared-tree).
Reason: Strongest default hygiene -- the common parallel case (independent slices) gets real isolation with zero operator effort; commit-races and cross-contamination become structurally impossible. The shared-tree model stays reachable via opt-out for the minority of milestones whose slices need a common live state. Conditional detection (B) was rejected as too clever -- "is this slice-set truly disjoint?" is hard to detect reliably, and a wrong guess silently picks the riskier model.
How to apply: New ytstack work item (ROADMAP M013) -- give spawn-milestone-team a default worktree-per-teammate dispatch (one worktree per parallel slice off a shared milestone branch), a conflict-free merge/integration step at milestone close (file-disjoint slices merge fast-forward), and an explicit shared-tree opt-out flag. Until M013 ships, M012 itself runs on the current shared-tree model under the 2026-05-31 "Swarm commit discipline" entry (lead commits path-scoped, teammates do not).
Relationship: Sets the go-forward default. Does NOT supersede "Swarm commit discipline" (2026-05-31) -- that entry governs the shared-tree fallback, which the opt-out flag keeps alive.
Context: Doc-consistency audit 2026-05-31 found the README install block still described the 2026-04-25 ystacks / ystacks-internal two-marketplace split (install ytstack@ystacks-internal, cross-mp deps on skill-creator + web-design from ystacks). The live manifests had already moved on: ytstack's own plugin.json declares a single dependency web-design from marketplace yesterday-public-plugins (commit 14f7c19), and Yesterday-AI/skills/marketplace.json (name yesterday-public-plugins, Yesterday's PUBLIC catalog) lists ytstack via github source Yesterday-AI/ytstack. The README could not be followed as written.
Options considered:
- A) Treat the README (ystacks/ystacks-internal) as truth, revert the manifests.
- B) Treat the manifests (yesterday-public-plugins via Yesterday-AI/skills) as truth, reconcile the README to them.
Chose: B.
Reason: Maintainer confirmed 2026-05-31 that ytstack is a standalone plugin imported and bundled by the yesterday-public-plugins catalog in Yesterday-AI/skills. The manifests are the newer, intended state; the README simply lagged. The ystacks/ystacks-internal topology from the 2026-04-25 split is superseded for ytstack's listing.
How to apply:
- Primary install:
/plugin marketplace add Yesterday-AI/skillsthen/plugin install ytstack@yesterday-public-plugins. - ytstack's own
.claude-plugin/marketplace.jsonself-marketplace (nameytstack, source./,allowCrossMarketplaceDependenciesOn: ["yesterday-public-plugins"]) stays as a secondary/pin-direct path. - Single cross-mp dependency:
web-design. - README §Install + §Status reconciled in the same change; version aligned to 0.1.5 across
plugin.json,marketplace.json, README badge.
Open follow-up (not blocking, maintainer-owned, not a ytstack-repo edit): ytstack's plugin.json points the web-design dep at marketplace yesterday-public-plugins -- the correct public catalog name (repo Yesterday-AI/skills) -- but that catalog currently lists only ytstack. web-design previously lived in the now-deleted ystacks catalog. The live marketplaces are yesterday-public-plugins (Yesterday-AI/skills) and yesterday-private-plugins (Yesterday-AI/yesterday-skills); web-design still needs migrating into the public one for the dep to resolve on a clean install. The dep target name in ytstack's manifests is already right, so no ytstack-repo change is needed here.
Supersedes: the ytstack-listing portions of "2026-04-25: Marketplace architecture split into ystacks (public) + ystacks-internal (private)" and the install commands in "2026-04-24: Marketplace name equals plugin name". The self-marketplace mechanism itself remains valid. Note 2026-05-31: the ystacks / ystacks-internal catalogs are now deleted; the live marketplaces are yesterday-public-plugins (Yesterday-AI/skills) and yesterday-private-plugins (Yesterday-AI/yesterday-skills).
Context: bin/ytstack-check failed 10 errors across 5 native skills migrated from agentic-foundation (atomic-design, deutschland-stack-api, european-alternatives-api, oss-project, software-craftsmanship): each missing ## Checklist and ## Terminal State. These are methodology / lookup references, not numbered procedures, so the procedure-skill contract did not fit them. The contract previously said "every skill, no exception", which contradicted the shipped skills. using-ytstack already had an analogous exemption via kind: directive (the check skips structural sections for it).
Options considered:
- A) Add
## Checklist+## Terminal Stateto all 5, forcing them into the procedure template. - B) Introduce a
kind: referenceskill class that, likekind: directive, is exempt from the structural-section checks (Checklist / Terminal State / Preamble / Procedure) but still gets em-dash hygiene. Document it inskill-structure.md. - C) Leave the failures, document as a known deviation.
Chose: B.
Reason: These skills genuinely are not procedures; option A would distort reference content into a checklist shape it does not have. kind: directive already established the precedent that non-procedural skill classes skip the structural contract. A parallel kind: reference class matches what the skills are and removes the contradiction without weakening the contract for real procedure skills.
How to apply:
bin/ytstack-check:is_reference = fm.get("kind") == "reference"; the early-return that skips structural sections now fires foris_directive or is_reference.- The 5 skills above get
kind: referencein frontmatter. docs/ux/skill-structure.mddocuments the two non-procedural classes (directive,reference) and warns against usingreferenceto dodge writing a checklist on a real procedure skill.bin/ytstack-checknow passes with no failures (warnings only: pre-existing vendor sibling-skill references, accepted per 2026-04-25 "Vendored-preamble drift accepted").
Supersedes: none. Extends the structural contract with a class distinction the contract did not previously name.
Context: The agent-facing contributor guide lived in CLAUDE.md. AGENTS.md is the emerging cross-tool convention for agent instructions, while Claude Code still loads CLAUDE.md natively. Sibling repo clawrag/ already uses an AGENTS.md + CLAUDE.md-symlink layout.
Options considered:
- A) Keep
CLAUDE.mdas the only file. - B) Rename to
AGENTS.mdand makeCLAUDE.mda tracked symlink to it. - C) Maintain two real files (
AGENTS.md+CLAUDE.md) with duplicated content.
Chose: B (git mv CLAUDE.md AGENTS.md && ln -s AGENTS.md CLAUDE.md).
Reason: One source of truth, named by the cross-tool convention, with zero behavior change for Claude Code (the symlink, git mode 120000, resolves transparently for native load, [ -f CLAUDE.md ], and Read/Edit/Write). Matches the workspace precedent set by clawrag/. Option C drifts; option A keeps the non-conventional name.
How to apply:
- Prose docs name
AGENTS.mdas the contributor guide;CLAUDE.mdappears only as "symlinks to it" mentions. - Leave generic Claude-Code-convention references (e.g.
skills/using-ytstack/SKILL.md"User's explicit instructions (CLAUDE.md, ...)") and vendor paths (vendor/**/CLAUDE.md) untouched -- the skill ships to projects where the user's file genuinely isCLAUDE.md. - No
.ytstack/artifact sweep for the rename (append-only / hook-managed); the symlink keeps any historical references valid.
Supersedes: none.
2026-05-31: post-tool-use-bash is stub-once; summarize-task owns commit-to-task linking (M015 / issue #22)
Context: The post-tool-use-bash hook appended a ## Commits so far line to the active task's T##-SUMMARY.md on every command matching the glob *git*commit*. That glob fired on echoes, git log | grep commit, and doc strings; it appended the current HEAD with no new-commit check and no dedup (N duplicate lines, same SHA); and because it rewrote a tracked file after each commit and never staged it, the tree never settled (committing the draft re-fired the hook) and the churning draft was invisible to a worktree branched from main, breaking spawn-milestone-team dispatch. Separately, summarize-task overwrites the SUMMARY from a template with no commit section, so the hook's appends were discarded at task close anyway -- pure churn for zero durable value.
Options considered:
- A) Harden the append in place (tighten matcher + dedup by SHA). Still leaves the tree dirty per commit and self-perpetuates on the summary commit; does not fix the worktree/dispatch gap.
- C) Stub-once: the hook creates the draft stub once and never re-writes it.
- C+) Stub-once PLUS move commit-to-task linking into
summarize-task(grepgit logfor theM###-S##-T##:commit ref into a committed## Commitssection).
Chose: C+.
Reason: The hook can never settle a tracked file it writes after the commit it describes, so per-commit append is structurally incompatible with worktree dispatch. Commit-linking belongs in the deliberate skill that runs at task close, where it is accurate and committed. Stub-once makes the hook idempotent (settles after one commit), and the M###-S##-T##: commit convention gives summarize-task a reliable, durable commit list. The matcher is also tightened to require git commit after a command separator (start / ; / && / || / | / (), rejecting echoes and pipelines; the residual lexical ambiguity (e.g. sudo git commit not auto-stubbed) is harmless because summarize-task still writes the real summary.
How to apply:
- Hook: match a real
git commitonly; if the SUMMARY already exists, exit 0 (never append); otherwise create the draft stub once. summarize-task: gathergit log --grep "^M###-S##-T##"in the preamble and write a committed## Commitssection.- Regression test:
tests/post-tool-use-bash.test.sh(10 cases) is the contract guard.
Supersedes: none.