yadflow 4.0.0-next.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +699 -0
- package/README.md +85 -22
- package/bin/commands.mjs +3 -1
- package/bin/yad.mjs +183 -29
- package/cli/capture.mjs +4 -2
- package/cli/checkpoint.mjs +10 -10
- package/cli/claims.mjs +4 -4
- package/cli/commit.mjs +48 -12
- package/cli/docs.mjs +15 -13
- package/cli/doctor.mjs +557 -141
- package/cli/epic-state.mjs +41 -18
- package/cli/errors.mjs +3 -2
- package/cli/find-product.mjs +197 -0
- package/cli/fold.mjs +4 -4
- package/cli/gate-local.mjs +13 -13
- package/cli/gate.mjs +111 -103
- package/cli/history.mjs +13 -13
- package/cli/hook.mjs +201 -30
- package/cli/index-command.mjs +6 -6
- package/cli/lib.mjs +91 -29
- package/cli/manifest.mjs +171 -104
- package/cli/migrate.mjs +208 -17
- package/cli/mode.mjs +22 -22
- package/cli/next.mjs +6 -6
- package/cli/openpr.mjs +32 -24
- package/cli/people.mjs +22 -10
- package/cli/plan.mjs +718 -45
- package/cli/platform.mjs +13 -7
- package/cli/product-index.mjs +4 -4
- package/cli/{hubcommit.mjs → productcommit.mjs} +11 -7
- package/cli/protection.mjs +19 -9
- package/cli/reconcile.mjs +98 -23
- package/cli/repo-publish.mjs +22 -16
- package/cli/repo.mjs +128 -33
- package/cli/report.mjs +4 -2
- package/cli/review.mjs +26 -19
- package/cli/setup.mjs +105 -36
- package/cli/ship.mjs +1 -1
- package/cli/skip.mjs +4 -4
- package/cli/tidy.mjs +7 -7
- package/cli/update-commit.mjs +22 -19
- package/cli/workspace.mjs +495 -0
- package/package.json +7 -4
- package/skills/sdlc/config.yaml +14 -13
- package/skills/sdlc/module-help.csv +8 -8
- package/skills/yad-analysis/SKILL.md +6 -4
- package/skills/yad-architecture/SKILL.md +4 -3
- package/skills/yad-backfill/SKILL.md +11 -5
- package/skills/yad-backfill/references/backfill.md +33 -3
- package/skills/yad-backfill/templates/checks/backfill-check.sh +200 -17
- package/skills/yad-change/SKILL.md +1 -1
- package/skills/yad-checks/SKILL.md +48 -27
- package/skills/yad-checks/references/check-gates.md +213 -53
- package/skills/yad-checks/templates/checks/commit-message.sh +39 -13
- package/skills/yad-checks/templates/checks/contract-check.sh +472 -23
- package/skills/yad-checks/templates/checks/epic-open.sh +398 -8
- package/skills/yad-checks/templates/checks/ledger-guard.sh +75 -25
- package/skills/yad-checks/templates/checks/lineage-check.sh +391 -7
- package/skills/yad-checks/templates/checks/product-checkout.sh +141 -0
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +399 -8
- package/skills/yad-checks/templates/checks/risk-map-check.sh +35 -3
- package/skills/yad-checks/templates/checks/spec-link.sh +36 -3
- package/skills/yad-checks/templates/checks/verified-commits.sh +26 -2
- package/skills/yad-checks/templates/github/yad-checks.yml +33 -13
- package/skills/yad-checks/templates/github/{yad-hub-checks.yml → yad-product-checks.yml} +14 -10
- package/skills/yad-checks/templates/github/yad-update-guard.yml +1 -1
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +1 -1
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +12 -0
- package/skills/yad-checks/templates/gitlab/{yad-hub-checks.gitlab-ci.yml → yad-product-checks.gitlab-ci.yml} +16 -16
- package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +2 -2
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.mjs +130 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.mjs +117 -0
- package/skills/yad-checks/templates/hooks/yad-capture.mjs +117 -0
- package/skills/yad-commit/SKILL.md +18 -7
- package/skills/yad-connect-design/SKILL.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +1 -1
- package/skills/yad-connect-docs/SKILL.md +16 -11
- package/skills/yad-connect-docs/references/docs-registry.md +8 -8
- package/skills/yad-connect-learning/SKILL.md +1 -1
- package/skills/yad-connect-learning/references/learning-registry.md +1 -1
- package/skills/yad-connect-repos/SKILL.md +31 -8
- package/skills/yad-connect-repos/references/code-context.md +1 -1
- package/skills/yad-connect-repos/references/{hub-config.md → product-config.md} +19 -11
- package/skills/yad-connect-repos/references/repos-registry.md +1 -1
- package/skills/yad-connect-testing/SKILL.md +1 -1
- package/skills/yad-connect-testing/references/testing-registry.md +1 -1
- package/skills/yad-discovery/SKILL.md +2 -2
- package/skills/yad-discovery/references/discovery-schema.md +1 -1
- package/skills/yad-docs/SKILL.md +1 -1
- package/skills/yad-docs/references/data-mapping.md +1 -1
- package/skills/yad-docs/templates/app/package-lock.json +8 -8
- package/skills/yad-docs/templates/app/package.json +1 -1
- package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +2 -2
- package/skills/yad-docs-overview/references/pipeline-model.md +3 -3
- package/skills/yad-engineer-review/SKILL.md +2 -2
- package/skills/yad-engineer-review/references/ship-and-record.md +3 -3
- package/skills/yad-epic/SKILL.md +6 -4
- package/skills/yad-epic/references/state-schema.md +13 -12
- package/skills/yad-open-pr/SKILL.md +1 -1
- package/skills/yad-pair-review/SKILL.md +5 -5
- package/skills/yad-pair-review/references/session-state.md +2 -2
- package/skills/yad-pr-template/SKILL.md +14 -12
- package/skills/yad-pr-template/templates/checks/pr-template.sh +18 -16
- package/skills/yad-pr-template/templates/checks/pr-title.sh +15 -13
- package/skills/yad-pr-template/templates/{hub → product}/github/pull_request_template.md +2 -2
- package/skills/yad-pr-template/templates/{hub → product}/gitlab/merge_request_templates/Default.md +2 -2
- package/skills/{yad-hub-bridge → yad-product-bridge}/SKILL.md +15 -15
- package/skills/{yad-hub-bridge → yad-product-bridge}/references/bridge.md +15 -10
- package/skills/{yad-hub-bridge → yad-product-bridge}/references/login-roster.md +2 -2
- package/skills/{yad-hub-bridge/templates/checks/hub-route.sh → yad-product-bridge/templates/checks/product-route.sh} +2 -2
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/github/yad-gate-sync.yml +29 -16
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/gitlab-ci.include-root.yml +1 -1
- package/skills/{yad-hub-bridge → yad-product-bridge}/templates/gitlab/yad-gate-sync.gitlab-ci.yml +5 -3
- package/skills/yad-report/SKILL.md +1 -1
- package/skills/yad-review-companion/SKILL.md +3 -3
- package/skills/yad-review-gate/SKILL.md +22 -23
- package/skills/yad-review-gate/references/gating.md +13 -13
- package/skills/yad-run/SKILL.md +2 -2
- package/skills/yad-run/references/run-loop.md +2 -2
- package/skills/yad-spec/SKILL.md +2 -1
- package/skills/yad-spec/references/spec-handoff.md +19 -10
- package/skills/yad-stories/SKILL.md +2 -2
- package/skills/yad-sync-repos/SKILL.md +19 -3
- package/skills/yad-test-cases/SKILL.md +2 -2
- package/skills/yad-ui/SKILL.md +2 -2
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +0 -91
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +0 -100
- package/skills/yad-checks/templates/hooks/yad-capture.sh +0 -53
|
@@ -3,13 +3,13 @@ SDLC Workflow,yad-discovery,Foundation,DI,"The Foundation — the Product level,
|
|
|
3
3
|
SDLC Workflow,yad-analysis,Author Analysis,AN,"Optional Shape step: with the analyst pressure-test a feature idea and write the discovery brief into analysis.md. Assigns the EP-<slug> ID and seeds .sdlc state (the chain that puts analysis before epic). If skipped, the epic step does this shaping inline. Never auto-advances.",,{idea: one-line feature idea},1-front,,yad-review-gate,false,epics/EP-<slug>/,analysis.md state.json
|
|
4
4
|
SDLC Workflow,yad-epic,Author Epic,AE,"Shape step 1: shape an idea with analyst then pm into epic.md; assign EP-<slug> ID and seed .sdlc state. Never auto-advances.",,{idea: one-line feature idea},1-front,,yad-review-gate,true,epics/EP-<slug>/,epic.md state.json
|
|
5
5
|
SDLC Workflow,yad-review-gate,Team Review Gate,RG,"Reusable review+approve gate for every Shape review. Shares an artifact for review, records comments and approvals as files, enforces at least 1 approval from someone other than the author (contract/auth/payments add a risk step to the count, capped at the active people less one and advisory), advances state only when approved.",,{artifact: file under the epic} {action: open|comment|approve|advance},1-front,,,true,epics/EP-<slug>/reviews/,reviews/*.md approvals.json state.json
|
|
6
|
-
SDLC Workflow,yad-review-companion,Review Companion,RC,"Fun, easy, transparent review companion for the review gates (Shape gate AND Build code PR). Generates a 60-sec AI trailer of what changed and where the risk is, deals swipe-through review cards, and runs a grounded chat where a reviewer's questions become the review record — then records an engagement signal on the approval (verified vs none) and posts a friendly public @-mention nudge on a bare rubber-stamp. Companion comments carry a noblock marker so they never hold the gate. Soft by default (visible, not impossible); gates only when
|
|
7
|
-
SDLC Workflow,yad-pair-review,Pair Review,PV,"The guided, two-way, teaching pair-review walkthrough — the AI-driven 5th companion face (Shape gate AND Build code PR). The human opens a PR/MR with an AI session and the AI walks them through the change one stop at a time (highest-risk first), gives comprehensive context per change, then asks a Socratic question; the human answers and asks back until BOTH declare satisfied. Doubles as a learning session: demonstrates a transferable review method, scores the engineer, and records review-skill growth in the local-only yad-learn ledger (rolled up by yad
|
|
6
|
+
SDLC Workflow,yad-review-companion,Review Companion,RC,"Fun, easy, transparent review companion for the review gates (Shape gate AND Build code PR). Generates a 60-sec AI trailer of what changed and where the risk is, deals swipe-through review cards, and runs a grounded chat where a reviewer's questions become the review record — then records an engagement signal on the approval (verified vs none) and posts a friendly public @-mention nudge on a bare rubber-stamp. Companion comments carry a noblock marker so they never hold the gate. Soft by default (visible, not impossible); gates only when review.requireEngagement is on in the Product settings. Never auto-advances.",,{epic: EP-<slug>} {artifact} | {repo} {pr} {action: trailer|cards|chat|nudge},1-front,,,false,epics/EP-<slug>/reviews/ | code PR,trailer/cards/chat (platform) approvals.json engagement
|
|
7
|
+
SDLC Workflow,yad-pair-review,Pair Review,PV,"The guided, two-way, teaching pair-review walkthrough — the AI-driven 5th companion face (Shape gate AND Build code PR). The human opens a PR/MR with an AI session and the AI walks them through the change one stop at a time (highest-risk first), gives comprehensive context per change, then asks a Socratic question; the human answers and asks back until BOTH declare satisfied. Doubles as a learning session: demonstrates a transferable review method, scores the engineer, and records review-skill growth in the local-only yad-learn ledger (rolled up by the yad-status skill). Soft and additive — NEVER blocks; rides the engagement signal and surfaces genuine concerns as normal blocking comments. Never auto-advances.",,{epic: EP-<slug>} {artifact} | {repo} {pr} {member} {action: walkthrough|record|rubric},3-build,yad-review-companion,,false,epics/EP-<slug>/reviews/ | code PR | epics/EP-<slug>/learning/ (local-only),pair session (platform) learning-records.json learning/<member>--review-<pr>.md
|
|
8
8
|
SDLC Workflow,yad-architecture,Author Architecture,AA,"Shape step 3: with the architect author architecture.md and the locked contract.md; hash-lock the contract surface. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,architecture.md contract.md contract-lock.json state.json
|
|
9
9
|
SDLC Workflow,yad-ui,Author UI Design,AU,"Shape step 5: with the ux-designer author ui-design.md and DESIGN.md, driving Impeccable slash-commands when installed. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,ui-design.md DESIGN.md state.json
|
|
10
10
|
SDLC Workflow,yad-stories,Author Stories,AS,"Shape step 7: with the pm break the epic into repo-tagged stories with stable EP-<slug>-S0N IDs, one file each under stories/. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/stories/,stories/EP-<slug>-S0N.md state.json
|
|
11
11
|
SDLC Workflow,yad-test-cases,Author Test Cases,TC,"Shape step 9 (PARALLEL, non-blocking): opens when the stories gate passes — the epic is already ready-for-build, so Build can start at the same time the tester works here. With the test architect author test-cases.md covering the approved stories, and — when a testing tool is connected (.sdlc/testing.json) — generate/link the actual automation tests in it, recording test-links.json; otherwise produce the test-case artifact only. Its review never moves currentStep off ready-for-build. Never auto-advances.",,{epic: EP-<slug>},1-front,yad-review-gate,yad-review-gate,true,epics/EP-<slug>/,test-cases.md test-links.json state.json
|
|
12
|
-
SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the Product so the Shape/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. yad repo refresh --push publishes the refreshed code-maps + registry to the Product default branch as a chore(
|
|
12
|
+
SDLC Workflow,yad-connect-repos,Connect Code Repos,CR,"Setup/maintenance: connect code repos to the Product so the Shape/brain phases are code-aware. Registers each repo (GitHub or GitLab, local-user auth, no stored tokens) in .sdlc/repos.json and caches a Repomix pack + a lightweight code-map (existing endpoints/events/data-models/modules, secret-scanned). Idempotent and refreshable; staleness tracked by HEAD sha. yad repo refresh --push publishes the refreshed code-maps + registry to the Product default branch as a chore(product): sync code-context [skip ci] audit commit. Also drafts each code repo's .sdlc/risk-map (a level per directory, no names) by reading the code, marked guessed for a person to confirm. Run at setup or any time a new repo is added. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {repo: <name>} {path: <path-or-absolute>} {git_url: <ssh-or-https>},0-setup,,yad-sync-repos,false,.sdlc/,repos.json code-context/<repo>/pack.md code-context/<repo>/code-map.md
|
|
13
13
|
SDLC Workflow,yad-sync-repos,Sync Connected Repos,SR,"Setup/maintenance: bring every connected repo up to date in one shot — switch each repo in .sdlc/repos.json to its default_branch and fast-forward it from origin (local-user git, no stored tokens). Working-tree only; never a gate and never writes the registry. A dirty repo is skipped and reported (never overwritten); a diverged branch is left for manual resolution (fast-forward only). After pulling, a repo's cached pack goes stale — points the human at yad repo refresh.",,{action: sync} {repo: <name>},0-setup,yad-connect-repos,yad-connect-design,false,,(none — working-tree only)
|
|
14
14
|
SDLC Workflow,yad-connect-design,Connect Design Tool,CD,"Setup/maintenance: connect a design tool (Figma-first, pluggable) to the Product so the UI design step can materialize the actual feature design (mobile screens / web pages) inside it, alongside ui-design.md + DESIGN.md. Records the tool + project/file references in .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,"{action: connect|refresh|list|disconnect} {tool: figma|pencil|none} {project_url: <team/project/file url>} {files: {web,mobile}}",0-setup,,yad-ui,false,.sdlc/,design.json
|
|
15
15
|
SDLC Workflow,yad-connect-testing,Connect Testing Tool,CT,"Setup/maintenance: connect a testing tool (Playwright-first, pluggable) to the Product so the test-cases step can implement the actual automation tests in it, alongside test-cases.md. Records the tool + project/suite references in .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {tool: playwright|cypress|pytest|none} {project_url: <project/config reference>} {suites: {<repo>}},0-setup,,yad-test-cases,false,.sdlc/,testing.json
|
|
@@ -17,18 +17,18 @@ SDLC Workflow,yad-connect-learning,Connect Learning Tool,CL,"Setup/maintenance:
|
|
|
17
17
|
SDLC Workflow,yad-learn,Learn (Tutor),LN,"Cross-cutting learning layer: at ANY SDLC stage a team member can ask to learn a concept and be tutored in the context of what the team is building. Routes to the connected learning tool (.sdlc/learning.json, DeepTutor-first) grounded in the project knowledge base, or degrades to harness-native tutoring (the harness model reading the artifacts). Renders a tutorial artifact and appends to a per-member learning ledger kept LOCAL-ONLY (gitignored, never committed/pushed to the Product or any code repo) so it stays a private, personal skills log (yad-status rolls up the local records). Purely opt-in — never blocks a gate, never touches epic state, approvals, or the contract lock.",,{concept: <idea>} {context: <focus>} {epic: EP-<slug>} {stage: <sdlc stage>} {member: <who>} {mode: explain|deep|quiz} {action: learn|list|complete},anytime,,,false,epics/EP-<slug>/,learning-records.json learning/<member>--<concept>.md
|
|
18
18
|
SDLC Workflow,yad-spec,Author Spec,SP,"Build Step A: for one ready-for-build story and one of its repos, run the heavy Spec Kit ceremony once (specify→clarify→plan→analyze→checklist→tasks) inside that code repo, writing specs/<story-id>/ in Spec Kit's layout (drives /speckit.* when installed, else hand-authors and records speckit: not-installed). References the locked contract; never re-invents the surface. Writes link.md back to the story. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>},3-build,yad-review-gate,,false,demo-repos/<repo>/specs/<story-id>/,spec.md research.md data-model.md contracts/ plan.md tasks.md link.md
|
|
19
19
|
SDLC Workflow,yad-implement,Implement Task,IM,"Build Step B: with the dev lens, implement ONE atomic task from a story's tasks.md as a small diff (<=3 files) on its own branch feat/<story>-<task>-<slug> in the code repo. Diff stays inside the task's declared files (flag and STOP if it grows beyond them). Commit ends with a Task: <story>-<task> trailer; add Contract-Change: yes only when the locked contract surface is touched (routes back to the architecture gate). Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <one of story.repos>} {task: T0N},3-build,yad-spec,,false,demo-repos/<repo>/,branch+commit per atomic task
|
|
20
|
-
SDLC Workflow,yad-checks,Check Gates,CK,"Build Step C: wire and run the production-safety CI gates on a code repo (and Product) — spec-link (every change links a real story/spec via its Task trailer), contract-check (a contract-surface change without Contract-Change + an updated re-locked contract FAILS and routes back to the architecture gate), build/test/lint, verified-commits (every commit carries a platform-Verified signature), and the pattern gates commit-message / pr-title / pr-template (profile-aware code|
|
|
21
|
-
SDLC Workflow,yad-pr-template,PR/MR Template,PT,"Build Step D: detect a code repo's platform and commit the matching PR/MR template (.github/pull_request_template.md or .gitlab/merge_request_templates/Default.md) with an Impact & Risk block. A high risk level (or a touched contract surface) raises the approver count — the same count yad-review-gate prints. Ships the routing helper risk-route.sh plus the pattern-gate scripts pr-title.sh and pr-template.sh (used by yad-checks). Never auto-advances.",,{repo: <one of an epic's repos |
|
|
20
|
+
SDLC Workflow,yad-checks,Check Gates,CK,"Build Step C: wire and run the production-safety CI gates on a code repo (and Product) — spec-link (every change links a real story/spec via its Task trailer), contract-check (a contract-surface change without Contract-Change + an updated re-locked contract FAILS and routes back to the architecture gate), build/test/lint, verified-commits (every commit carries a platform-Verified signature), and the pattern gates commit-message / pr-title / pr-template (profile-aware code|product). CI-agnostic bash invoked by GitHub Actions and GitLab CI. Blocking in CI; the human still owns the merge. Never auto-advances.",,{repo: <one of an epic's repos | product>} {action: wire|run} {base: target branch},3-build,yad-implement,,false,demo-repos/<repo>/,checks/*.sh .github/workflows/yad-checks.yml .gitlab/ci/yad-checks.yml
|
|
21
|
+
SDLC Workflow,yad-pr-template,PR/MR Template,PT,"Build Step D: detect a code repo's platform and commit the matching PR/MR template (.github/pull_request_template.md or .gitlab/merge_request_templates/Default.md) with an Impact & Risk block. A high risk level (or a touched contract surface) raises the approver count — the same count yad-review-gate prints. Ships the routing helper risk-route.sh plus the pattern-gate scripts pr-title.sh and pr-template.sh (used by yad-checks). Never auto-advances.",,{repo: <one of an epic's repos | product>} {action: wire|route} {body: PR description file},3-build,yad-checks,,false,demo-repos/<repo>/,.github/pull_request_template.md .gitlab/merge_request_templates/Default.md checks/risk-route.sh checks/pr-title.sh checks/pr-template.sh
|
|
22
22
|
SDLC Workflow,yad-commit,Commit by Convention,CM,"Build helper: commit ONE staged atomic change by the conventions — a Conventional-Commits subject, the fixed trailer block (Task -> Contract-Change -> Co-Authored-By), and the <=3-file atomic guard. The human git author owns the commit; an assisting AI is recorded only as a Co-Authored-By footer chosen per-commit with --ai (claude|copilot|cursor|coderabbit|none, default none). Drives the yad commit CLI. Never auto-advances.",,{type: feat|fix|...} {message: subject} {ai: <tool|none>} {task: <id>} {contract-change: true|false},3-build,yad-implement,,false,<repo>/,one commit
|
|
23
|
-
SDLC Workflow,yad-open-pr,Open PR/MR,OP,"Build helper: open a code-repo task PR/MR from the committed platform template — detect GitHub/GitLab, push the task branch, create the PR/MR with the body prefilled (Summary / Story-task / Impact & Risk) and the title defaulting to the commit subject. Assigns the logged-in gh/glab login and requests no reviewers; high risk / contract surface raises the approver count (risk-route.sh prints it). Bases the PR on the repo's RESOLVED default branch (--base, else repos.json default_branch, else hub.json default_branch for a PR on the Product itself, else the platform's own default, else origin/HEAD, else main) instead of a hardcoded main, and warns when the base is not the platform default — that loses the AI first pass irreversibly. Drives the yad open-pr CLI. Never merges; never auto-advances.",,{repo: <name>} {risk: low|medium|high} {contract-change: true|false} {base: <branch>},3-build,yad-commit,,false,<repo>/,one PR/MR
|
|
23
|
+
SDLC Workflow,yad-open-pr,Open PR/MR,OP,"Build helper: open a code-repo task PR/MR from the committed platform template — detect GitHub/GitLab, push the task branch, create the PR/MR with the body prefilled (Summary / Story-task / Impact & Risk) and the title defaulting to the commit subject. Assigns the logged-in gh/glab login and requests no reviewers; high risk / contract surface raises the approver count (risk-route.sh prints it). Bases the PR on the repo's RESOLVED default branch (--base, else repos.json default_branch, else product.json (hub.json on an older Product) default_branch for a PR on the Product itself, else the platform's own default, else origin/HEAD, else main) instead of a hardcoded main, and warns when the base is not the platform default — that loses the AI first pass irreversibly. Drives the yad open-pr CLI. Never merges; never auto-advances.",,{repo: <name>} {risk: low|medium|high} {contract-change: true|false} {base: <branch>},3-build,yad-commit,,false,<repo>/,one PR/MR
|
|
24
24
|
SDLC Workflow,yad-ship,Commit + Open PR/MR,SP2,"Build helper: commit AND open the task PR/MR in one step — a thin orchestration over yad-commit then yad-open-pr. Commits the staged atomic change by the conventions, then pushes the branch and opens the PR/MR from the committed template, assigned to the logged-in gh/glab login with no reviewers requested. The PR step runs ONLY if the commit lands (a failed commit, tripped guard, or --dry-run stops before pushing). Drives the yad ship CLI. Never merges; never auto-advances.",,{type: feat|fix|...} {message: subject} {ai: <tool|none>} {repo: <name>} {risk: low|medium|high} {contract-change: true|false} {base: <branch>},3-build,yad-pr-template,,false,<repo>/,one commit + one PR/MR
|
|
25
|
-
SDLC Workflow,yad-
|
|
25
|
+
SDLC Workflow,yad-product-bridge,Product Review Bridge,PB,"The templated PR/MR bridge for the Shape review gate: when the Product has a platform (.sdlc/product.json), open a review PR/MR on the Product for an authored artifact, apply the touched-domain labels (no reviewers are requested), and provide the read-only gh/glab recipes yad-review-gate's sync uses to pull platform comments + approvals into the file ledger. Local-user auth, no stored tokens; file ledger stays the source of truth; degrades to local when no platform/CLI. Never auto-advances.",,{epic: EP-<slug>} {artifact: epic.md|architecture.md|ui-design.md|stories/} {action: open|route},1-front,yad-review-gate,yad-review-gate,false,epics/EP-<slug>/.sdlc/,product-prs.json
|
|
26
26
|
SDLC Workflow,yad-engineer-review,Engineer Review & Merge,ER,"Build Step E: wire an advisory AI first-pass (CodeRabbit) on the PR, record the human engineer review with the same advance-human discipline as the Shape gates (1 approval from someone other than the author; high risk / contract / auth / payments add an advisory risk step), and on merge record the ship in epics/<epic>/.sdlc/build-log.json and update the story state. AI review is advisory, never the authority; the human owns the merge. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {task: T0N} {repo: <repo>} {action: ai-review|approve|ship},3-build,yad-ship,,false,epics/EP-<slug>/.sdlc/,build-log.json story-status
|
|
27
27
|
SDLC Workflow,yad-backfill,Backfill Specs,BF,"Build Step G: generate specs for already-built features in an existing repo. Confirm Repomix (npx repomix CLI), pack ONE feature (compress + git logs, secret-scan), feed to AI with a 'describe what exists, do not invent' prompt, write a DRAFT spec marked verified: false. Human approval (reuse yad-review-gate) makes it real. Boundary auto-proposed and human-confirmed. A change is blocked only until the features it touches have approved specs. The promote action flips a brownfield stub epic (yad-stub) to a real, verified feature epic once its backfill spec is approved. Never auto-advances.",,{repo: <repo>} {feature: <name + globs>} {action: pack|draft|approve|gate|promote} {epic: EP-<slug> (promote)},3-build,,,false,demo-repos/<repo>/specs/backfill/<feature>/,spec.md backfill-check.sh
|
|
28
28
|
SDLC Workflow,yad-run,Run (Automation),RN,"Phase 4 orchestrator: drive a story's Build loop (spec→tasks→implement→checks) in one code repo, reading each step's advance dial through the engine (yad dial --json) — on auto it advances on its own, on human it stops for a human. Records every run in the trust log, the run record yad dial shows as advice. A clean checks pass on auto advances to the engineer review; any FAIL / scope overrun / contract touch HALTS. set-dial runs yad dial (no trust threshold since E34; a gate is refused); kill/unkill run yad kill / yad unkill, recorded in .sdlc/automation.json. Never drives a Shape step; the human merge gate never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <repo>} {action: run|set-dial|kill|unkill} {step: <back-step>} {to: human|auto},4-automate,yad-spec,yad-engineer-review,false,epics/EP-<slug>/.sdlc/,build-state/<story-id>.json trust-log.json
|
|
29
29
|
SDLC Workflow,yad-status,SDLC Status,SS,"Read-only: print the full Shape step chain, per-step dials, contract lock, story repo tags, and pending approvals at the active gate. For stories in Build, also print each Build step's automation dial and status, the run record (runs / % approved-unchanged, as advice), the Shape author dials and the kill switch from .sdlc/automation.json.",,{epic: EP-<slug>},anytime,,,false,,
|
|
30
30
|
SDLC Workflow,yad-report,Report Issue,RP,"Self issue reporter: when a yad flow breaks, file a well-formed bug in the upstream yadflow repo with AUTO-SCRUBBED diagnostics (yadflow/node/os version, tool present+authenticated booleans, Product platform enum, the YadError code/hint, a path-scrubbed message, and the failing command + flag NAMES only) — never absolute paths, hostnames, git URLs, repo names, logins/emails, epic/story IDs, branch names, or flag values. Searches open issues first to avoid duplicates, previews the exact payload, and asks before posting to the public repo; files via an authenticated gh (the upstream repo is on GitHub) or a prefilled issues/new URL fallback. Also offered automatically after an unexpected failure (interactive only; YAD_NO_REPORT=1 disables). Never a gate, never touches epic state.",,{message: one-line summary},,,,false,,
|
|
31
|
-
SDLC Workflow,yad-connect-docs,Connect Docs Target,DX,"Setup/maintenance: connect a docs/Pages publishing target so the interactive-docs steps can deploy the generated site (not just commit its source). Auto-detects the platform from .sdlc/
|
|
31
|
+
SDLC Workflow,yad-connect-docs,Connect Docs Target,DX,"Setup/maintenance: connect a docs/Pages publishing target so the interactive-docs steps can deploy the generated site (not just commit its source). Auto-detects the platform from .sdlc/product.json (github->github-pages, gitlab->gitlab-pages, null->build-only), resolves the Vite base path, and records the target in .sdlc/docs.json (local-user auth, no stored tokens). Degrades to build-only when gh/glab is absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {target: github-pages|gitlab-pages|none} {scope: product|<repo>|dedicated} {base_path: <override>},0-setup,,yad-docs,false,.sdlc/,docs.json
|
|
32
32
|
SDLC Workflow,yad-docs,Author Docs Site,DS,"Generate the per-epic interactive documentation SPA (animated flow canvas + role-based stakeholder doc pages) from the epic's approved artifacts (epic, architecture, locked contract, ui-design, stories, code-context, test-cases), themed by the connected design system, into epics/EP-<slug>/docs-site/. Copies the vendored React/Vite/Tailwind shell verbatim and generates src/data/*.ts deterministically; drives the yad docs build/deploy CLI to publish to Pages (or build-only). An OUTPUT ENRICHMENT — never a gate; never mutates state.json, approvals, or the contract lock.",,{epic: EP-<slug>} {action: generate|refresh|deploy} {login_gate: true|false},4-automate,yad-review-gate,,false,epics/EP-<slug>/docs-site/,docs-site/ docs-build.json
|
|
33
33
|
SDLC Workflow,yad-docs-overview,Docs Overview Site,DO,"Generate the project SDLC-overview interactive site (docs/sdlc-site/) — every stage from setup to ship modeled as flow paths, system components, and stakeholder roles — reusing the same vendored shell, themed with yadflow's brand palette. Reads config.yaml + module-help.csv + the overview diagram as the pipeline source. Folds the hand-maintained docs/index.html report into the site as report.html (linked from the nav). Not a gate — a project-level enrichment that regenerates whenever the skill set / pipeline changes.",,{action: generate|deploy},4-automate,,,false,docs/sdlc-site/,sdlc-site/ .docs-build.json
|
|
34
34
|
SDLC Workflow,yad-docs-sync,Docs Sync,DY,"Maintenance/CI: keep the generated doc sites fresh. Detect staleness (a content hash of the approved artifacts + the connected repos' HEAD shas + for a per-epic site the doc-shell version, vs each site's build manifest), report which sites drifted and why, regenerate + redeploy the stale ones, and wire a CI job that rebuilds on push (carrying [skip ci] + a concurrency group to prevent deploy loops). Generalizes the rule that feature work must hand-update docs/index.html + diagrams + skill counts. Refresh is always a human/CI decision; never a gate.",,{action: check|refresh|wire} {epic: EP-<slug>},4-automate,,,false,epics/EP-<slug>/.sdlc/,docs-build.json yad-docs.yml
|
|
@@ -15,7 +15,9 @@ This step is **optional**. When it runs, it is the entry point: it assigns the I
|
|
|
15
15
|
analyst shaping inline and seeds the **10-step** chain — no behaviour change for teams that skip it.
|
|
16
16
|
|
|
17
17
|
This skill enforces the build plan's core rules: all state lives in files; IDs are generated by the
|
|
18
|
-
engine (never typed by hand); Shape
|
|
18
|
+
engine (never typed by hand); every Shape step starts at `advance: human`. A review gate is always
|
|
19
|
+
human; only the team can set a Shape author step to auto (`yad dial`, recorded in
|
|
20
|
+
`.sdlc/automation.json`; nothing acts on it yet).
|
|
19
21
|
|
|
20
22
|
## Conventions
|
|
21
23
|
|
|
@@ -131,7 +133,7 @@ yad epic new EP-<slug> --profile spike --type <feature|chore> # a timeboxed in
|
|
|
131
133
|
|
|
132
134
|
That writes `{project-root}/epics/EP-<slug>/.sdlc/state.json`, the empty `approvals.json` and
|
|
133
135
|
`comments.json`, and the `reviews/` directory. With `--profile analysis-first` the chain is the full
|
|
134
|
-
**12-step** route (`analysis` before `epic`). Every step
|
|
136
|
+
**12-step** route (`analysis` before `epic`). Every step starts at `advance: human`; `analysis` is
|
|
135
137
|
open and the rest are `todo`. The chain comes from the step catalogue and the lifecycle profile in
|
|
136
138
|
the engine (`../yad-epic/references/state-schema.md`), so there is one definition of it and no copy
|
|
137
139
|
here to drift from it.
|
|
@@ -177,7 +179,7 @@ Notes:
|
|
|
177
179
|
*(**Always run this**, on both entry modes. The chain is seeded by now either way — by `yad epic new`
|
|
178
180
|
in Step 6, or by `yad epic new` before this skill was invoked — and `analysis.md` is written. This is
|
|
179
181
|
the step that closes the authoring step and opens its gate.)*
|
|
180
|
-
**Check the mode first — the two modes have opposite instructions here.** Read `.sdlc/hub.json
|
|
182
|
+
**Check the mode first — the two modes have opposite instructions here.** Read `.sdlc/product.json` (`.sdlc/hub.json` on an older Product that has only that name):
|
|
181
183
|
**verified mode** is `platform` set AND `ledger: "verified"` — or, on a project that has not run
|
|
182
184
|
`yad migrate` yet, `bridge_enabled` (or legacy `bridge`) `true`. `ledger` wins whenever it is present.
|
|
183
185
|
|
|
@@ -211,7 +213,7 @@ Report: epic ID, the path to `analysis.md`, and that the next action is **review
|
|
|
211
213
|
`yad-review-gate` (base rule: 1 distinct approver, who should not be the author). **Never mark the analysis-review step approved
|
|
212
214
|
here** — only real reviewers do that through the gate. Shape steps do not auto-advance. When the
|
|
213
215
|
analysis gate passes, control moves to `yad-epic`, which reads `analysis.md` as input. When the
|
|
214
|
-
Product has a platform, the gate opens a review PR on the Product (via `yad-
|
|
216
|
+
Product has a platform, the gate opens a review PR on the Product (via `yad-product-bridge`) and
|
|
215
217
|
`yad-review-gate action: sync` pulls platform approvals/comments into the ledger; otherwise the review
|
|
216
218
|
is recorded local.
|
|
217
219
|
|
|
@@ -12,7 +12,8 @@ When both artifacts are drafted, control passes to `yad-review-gate`. The archit
|
|
|
12
12
|
`risk_tags: ["contract"]` by default, which raises the review's (advisory) approver count.
|
|
13
13
|
|
|
14
14
|
This skill enforces the build plan's core rules: all state lives in files; the contract holds only the
|
|
15
|
-
shared cross-repo surface at charter altitude; Shape
|
|
15
|
+
shared cross-repo surface at charter altitude; every Shape step starts at `advance: human` (a review gate
|
|
16
|
+
is always human; the team sets an author step's dial with `yad dial`).
|
|
16
17
|
|
|
17
18
|
## Conventions
|
|
18
19
|
|
|
@@ -179,7 +180,7 @@ awk '/CONTRACT-SURFACE:BEGIN/{f=1;next} /CONTRACT-SURFACE:END/{f=0} f' \
|
|
|
179
180
|
> of hash-binding.
|
|
180
181
|
|
|
181
182
|
### Step 6 — Advance the authoring step (NOT the gate)
|
|
182
|
-
**Check the mode first — the two modes have opposite instructions here.** Read `.sdlc/hub.json
|
|
183
|
+
**Check the mode first — the two modes have opposite instructions here.** Read `.sdlc/product.json` (`.sdlc/hub.json` on an older Product that has only that name):
|
|
183
184
|
**verified mode** is `platform` set AND `ledger: "verified"` — or, on a project that has not run `yad migrate` yet, `bridge_enabled` (or legacy `bridge`) `true`. `ledger` wins whenever it is present.
|
|
184
185
|
|
|
185
186
|
**verified mode — do NOT write `state.json`.** The ledger is CI-owned: the `ledger-guard` check rejects
|
|
@@ -212,7 +213,7 @@ the count at the active people less one, floor 1, and prints it (E72) — with 2
|
|
|
212
213
|
ask is 1, with 3 it is 2, with 4 or more all 3 — and a shortfall never holds the gate. When the people
|
|
213
214
|
cannot be counted (as in Product CI with connected repos), no cap is shown. The review PR requests no reviewers; the team asks them on the PR itself.
|
|
214
215
|
**Never record approval here.** Shape steps do not auto-advance. When the Product has a platform, the gate
|
|
215
|
-
opens a review PR on the Product (via `yad-
|
|
216
|
+
opens a review PR on the Product (via `yad-product-bridge`, labelled per touched repo) and
|
|
216
217
|
`yad-review-gate action: sync` pulls platform approvals/comments into the ledger; a contract re-lock
|
|
217
218
|
invalidates prior platform approvals too. Otherwise the review is recorded local.
|
|
218
219
|
|
|
@@ -57,17 +57,19 @@ feature: <feature>
|
|
|
57
57
|
repo: <repo>
|
|
58
58
|
artifact: backfill-spec
|
|
59
59
|
status: draft
|
|
60
|
-
verified: false
|
|
60
|
+
verified: false
|
|
61
61
|
source: repomix # or "repomix: unavailable" when degraded
|
|
62
62
|
generated: <YYYY-MM-DD>
|
|
63
63
|
---
|
|
64
64
|
```
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
`verified: false` means the spec is not real until a human approves it. Keep that line free of a `#`
|
|
66
|
+
comment: the gate reads the whole line, so `verified: true # approved` would not count. Mark every
|
|
67
|
+
uncertain item explicitly (`<!-- unverified: ... -->`); do not fill gaps with invented behaviour.
|
|
67
68
|
|
|
68
69
|
### Step 4 — `approve` (human approval — reuse the gate)
|
|
69
70
|
A human reads the draft against the real code and approves it with the same `advance: human` discipline
|
|
70
|
-
as `yad-review-gate` (at least 1 approver who is not the author). On approval set the frontmatter `verified: true`
|
|
71
|
+
as `yad-review-gate` (at least 1 approver who is not the author). On approval set the frontmatter line to exactly `verified: true` (lowercase, nothing after it — the
|
|
72
|
+
gate reads it from the base branch, so the approval merges before the change it allows) and record
|
|
71
73
|
the approver(s) + date. Only a `verified: true` backfill spec counts as real.
|
|
72
74
|
|
|
73
75
|
### Step 5 — `gate` (block changes per touched feature)
|
|
@@ -75,7 +77,11 @@ the approver(s) + date. Only a `verified: true` backfill spec counts as real.
|
|
|
75
77
|
that feature's spec is `verified: true`. `<base>` is optional and resolves like every other gate's —
|
|
76
78
|
see "Resolving `<base>`" in `../yad-checks/references/check-gates.md`. It is **per touched feature** —
|
|
77
79
|
a change touching feature A is not blocked by an unverified feature B. Forward-spec'd features (those with their own `specs/<story>/`)
|
|
78
|
-
are not this gate's concern.
|
|
80
|
+
are not this gate's concern. The spec and its `verified: true` are read **from the base**, so an
|
|
81
|
+
approval counts once it is merged — approve in its own PR, before the change (E116). A symlink or
|
|
82
|
+
submodule at `src`, at `src/<feature>` or inside a feature being backfilled fails every PR until one PR
|
|
83
|
+
removes it; so do two spellings of that folder at once (`src/Billing/` beside `src/billing/`) until one
|
|
84
|
+
PR removes one of them.
|
|
79
85
|
|
|
80
86
|
### Step 6 — `promote` (flip a stub epic → real, once its spec is approved)
|
|
81
87
|
When a brownfield feature was anchored with a **stub genesis epic** (`yad-stub`) so that defects could
|
|
@@ -64,10 +64,40 @@ A change is blocked **only until the features it touches** have approved specs
|
|
|
64
64
|
- A feature with **no** `specs/backfill/<feature>/` is not this gate's concern (it is either
|
|
65
65
|
forward-spec'd via `yad-spec`, or not yet being backfilled).
|
|
66
66
|
- Fails closed on an unresolvable base ref, like the other gates.
|
|
67
|
-
-
|
|
68
|
-
|
|
67
|
+
- Only a frontmatter that closes counts: a spec that opens `---` and never closes it is unapproved,
|
|
68
|
+
whatever its body says. A repeated `verified:` key is read as YAML reads it: the last one wins — and any spelling YAML
|
|
69
|
+
may take for the key (`"verified":`, `verified :`, another case) counts as a later line. The last one
|
|
70
|
+
must be exactly `verified: true`, the key in lowercase.
|
|
71
|
+
- A **moved** file counts at both ends (E114). The changed list is `git diff --no-renames --raw
|
|
72
|
+
-z`, each path paired with its header NUL by NUL (so a newline in one name cannot shift the rest), so `git mv src/<feature>/x.js lib/x.js` still touches `<feature>`; without `--no-renames` git
|
|
69
73
|
named the move by its new path only and the feature was never checked. `-z` and `LC_ALL=C` keep an
|
|
70
74
|
odd file name (a `"`, a tab, bytes that are not UTF-8) from hiding a path.
|
|
75
|
+
- **The spec is read from the base** (E116), never from the PR: which features are being backfilled,
|
|
76
|
+
and whether each spec says `verified: true`. Read from the PR, a PR could approve itself (set
|
|
77
|
+
`verified: true` in the same PR) or delete the spec and read as "not being backfilled". So an approval
|
|
78
|
+
must merge before the change it allows. A spec the PR adds counts once merged. Names are compared as
|
|
79
|
+
macOS and Windows compare them: ASCII case, plus the 13 characters a Mac folds into ASCII alone
|
|
80
|
+
(U+00DF ß and U+1E9E ẞ as `ss`, U+017F ſ as `s`, U+212A Kelvin sign as `k`, the ligatures U+FB00–U+FB06
|
|
81
|
+
as their letters, U+037E and U+1FEF as `;` and `` ` ``). Other non-ASCII case (`CAFÉ`/`café`) and
|
|
82
|
+
NFC/NFD twins are not folded — a stated limit, as in contract-check: `specs/backfill/Billing/spec.md` is
|
|
83
|
+
`billing`'s spec.
|
|
84
|
+
- **No link where a backfilled feature lives** (E116). The gate reads paths, so a symlink or submodule
|
|
85
|
+
at `src`, at `src/<feature>` or inside `src/<feature>/` let the code live where no path under `src/`
|
|
86
|
+
names it, and every later edit to the link's target passed. The tree at HEAD is read on every PR,
|
|
87
|
+
before the "no src/<feature> changes" PASS, and such a link is refused by name — only for a feature
|
|
88
|
+
the base is backfilling (a code repo may hold real links elsewhere in `src/`). So is a second spelling
|
|
89
|
+
of that folder at the same time (`Src/billing/` or `src/Billing/` beside `src/billing/`), which is one
|
|
90
|
+
folder on macOS and Windows but two on Linux CI; a lone `Src/` clashes with nothing and is read as
|
|
91
|
+
`src/`. **Not a dead end:** a PR that only deletes a link, or every file of one spelling while
|
|
92
|
+
another spelling stays, is not a change to the feature, so it passes. (Deleting both spellings deletes
|
|
93
|
+
the feature, and is a change to it.) A link where the spec lives on the base (`specs/backfill/<feature>` or its
|
|
94
|
+
`spec.md`) makes that feature unapproved; one at `specs` or `specs/backfill` fails every feature change
|
|
95
|
+
until a PR puts the real folder back, and meanwhile every folder under `src/` is checked for links —
|
|
96
|
+
so that PR must also remove any link under `src/` (it can come back after); the message says so.
|
|
97
|
+
**A repo that keeps `specs` as a symlink** is refused this way on every feature change until it holds
|
|
98
|
+
real files. A path that IS `src/<feature>` (a link, a submodule, or a file
|
|
99
|
+
where the folder goes) counts as that feature; a top-level `src/*.js` file still does not.
|
|
71
100
|
- This script is never wired by `yad check`; each copy is placed by hand. `yad doctor` warns
|
|
72
101
|
`checks:rename-blind` when a copy at `checks/backfill-check.sh` (in the Product or a connected repo)
|
|
73
|
-
is an older one that lists a rename by one path
|
|
102
|
+
is an older one that lists a rename by one path, and `checks:backfill-blind` when it is older than
|
|
103
|
+
E116 (it reads the approval from the PR, and does not read the tree for links).
|
|
@@ -3,7 +3,33 @@
|
|
|
3
3
|
# until that feature's backfill spec is human-approved (verified: true). Gated PER touched feature, not
|
|
4
4
|
# the whole repo: touching feature A is never blocked by an unverified feature B. Features that are
|
|
5
5
|
# forward-spec'd (their own specs/<story>/) or not yet being backfilled are not this gate's concern.
|
|
6
|
+
# The spec and its approval are read from the BASE, and a link where a backfilled feature lives is
|
|
7
|
+
# refused (E116).
|
|
6
8
|
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
# --- shared Product settings file (byte-identical across the gates; they are standalone by design, so
|
|
11
|
+
# --- it is duplicated, not sourced) ---
|
|
12
|
+
# The Product's settings live under two names until v5: `.sdlc/product.json`, read first from 4.0, and
|
|
13
|
+
# `.sdlc/hub.json`, which an older yadflow wrote and its gates read. The environment may name the file
|
|
14
|
+
# instead: SDLC_PRODUCT_CONFIG, or the older SDLC_HUB_CONFIG. Two names that say different things are
|
|
15
|
+
# never settled by picking one — the CLI refuses the same way (YAD-STATE-008) — so the gate FAILS and
|
|
16
|
+
# names both. Said on stderr and returned as a failure: this runs inside `$(...)`, where an `exit` would
|
|
17
|
+
# only leave the subshell. `cmp -s` is byte for byte, like the CLI's own comparison.
|
|
18
|
+
product_config() {
|
|
19
|
+
if [ -n "${SDLC_PRODUCT_CONFIG:-}" ] && [ -n "${SDLC_HUB_CONFIG:-}" ] \
|
|
20
|
+
&& [ "$SDLC_PRODUCT_CONFIG" != "$SDLC_HUB_CONFIG" ] && ! cmp -s "$SDLC_PRODUCT_CONFIG" "$SDLC_HUB_CONFIG"; then
|
|
21
|
+
echo "FAIL [product-settings]: SDLC_PRODUCT_CONFIG (${SDLC_PRODUCT_CONFIG}) and SDLC_HUB_CONFIG (${SDLC_HUB_CONFIG}) name files that say different things. Set only SDLC_PRODUCT_CONFIG." >&2
|
|
22
|
+
return 1
|
|
23
|
+
fi
|
|
24
|
+
if [ -n "${SDLC_PRODUCT_CONFIG:-}" ]; then printf '%s' "$SDLC_PRODUCT_CONFIG"; return 0; fi
|
|
25
|
+
if [ -n "${SDLC_HUB_CONFIG:-}" ]; then printf '%s' "$SDLC_HUB_CONFIG"; return 0; fi
|
|
26
|
+
if [ -f .sdlc/product.json ] && [ -f .sdlc/hub.json ] && ! cmp -s .sdlc/product.json .sdlc/hub.json; then
|
|
27
|
+
echo "FAIL [product-settings]: .sdlc/product.json and .sdlc/hub.json say different things — they are one file under two names until v5. Run \`yad migrate\` in the Product to choose the copy to keep, and commit both." >&2
|
|
28
|
+
return 1
|
|
29
|
+
fi
|
|
30
|
+
if [ -f .sdlc/product.json ]; then printf '%s' .sdlc/product.json; else printf '%s' .sdlc/hub.json; fi
|
|
31
|
+
}
|
|
32
|
+
PRODUCT_CONFIG="$(product_config)" || exit 1
|
|
7
33
|
# Bytes, not characters (E114). The changed list below carries raw path bytes, and a path that is not
|
|
8
34
|
# valid UTF-8 read under a UTF-8 locale is one GNU grep and sed may skip or leave unmatched — one such
|
|
9
35
|
# file in a diff would hide every other path from the patterns (Linux CI). On macOS, `tr` and `sed` stop
|
|
@@ -14,7 +40,7 @@ export LC_ALL=C
|
|
|
14
40
|
# --- is duplicated, not sourced) ---
|
|
15
41
|
# With no explicit base, RESOLVE the trunk instead of assuming a hardcoded `origin/main` — on a repo
|
|
16
42
|
# whose trunk is `develop`/`master` that guess either fails closed or, where a stale `main` still
|
|
17
|
-
# exists, silently diffs the WRONG range (issue #161). Mirrors the CLI's own order (cli/
|
|
43
|
+
# exists, silently diffs the WRONG range (issue #161). Mirrors the CLI's own order (cli/productcommit.mjs,
|
|
18
44
|
# cli/repo.mjs): the CONFIGURED default_branch first, then the remote's published default
|
|
19
45
|
# (origin/HEAD), then origin/main. Each candidate must actually resolve before it is used, so a
|
|
20
46
|
# DANGLING origin/HEAD (trunk renamed, the old remote-tracking ref pruned) falls through to the next
|
|
@@ -23,7 +49,7 @@ export LC_ALL=C
|
|
|
23
49
|
# command substitution in an assignment aborts the script.
|
|
24
50
|
resolve_base() {
|
|
25
51
|
# tr first: a key and its value may legally sit on separate lines, which a per-line match misses.
|
|
26
|
-
_cfg="$(tr -d '\n' < "$
|
|
52
|
+
_cfg="$({ tr -d '\n' < "$PRODUCT_CONFIG"; } 2>/dev/null | sed -nE 's/.*"default_branch"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p')" || _cfg=""
|
|
27
53
|
_head="$(git symbolic-ref --short --quiet refs/remotes/origin/HEAD 2>/dev/null)" || _head=""
|
|
28
54
|
for _c in "origin/${_cfg}" "${_head}" origin/main; do
|
|
29
55
|
case "$_c" in ''|origin/) continue ;; esac
|
|
@@ -39,14 +65,144 @@ if ! git rev-parse --verify --quiet "${BASE}^{commit}" >/dev/null; then
|
|
|
39
65
|
exit 1
|
|
40
66
|
fi
|
|
41
67
|
|
|
68
|
+
|
|
69
|
+
# Folded as macOS and Windows fold a name (E116, as E115): `Src/` IS `src/` there, and `src/Billing/` IS
|
|
70
|
+
# `src/billing/`, while git and Linux CI read exact bytes. `tolower` under LC_ALL=C folds ASCII only, so
|
|
71
|
+
# every character APFS folds (full case folding, then NFD) into ASCII alone is mapped by hand, as bytes —
|
|
72
|
+
# all 13 of them, from Unicode's own tables (reviews 4 and 5): ß and ẞ into `ss`, ſ into `s`, the
|
|
73
|
+
# Kelvin sign into `k`, the ligatures ff fi fl ffi ffl ſt st into their letters, and the Greek question mark and
|
|
74
|
+
# varia into `;` and `` ` ``. So `src/claß/` IS `src/class/` on a Mac, and is read so here. Not folded,
|
|
75
|
+
# and stated: characters that fold into other non-ASCII ones (`CAFÉ` is `café` on a Mac) and NFC/NFD
|
|
76
|
+
# twins; awk cannot fold them, and refusing every non-ASCII feature name would refuse real repos (E115
|
|
77
|
+
# made the same call).
|
|
78
|
+
FOLD='function fold(x) { x = tolower(x)
|
|
79
|
+
gsub(/\303\237|\341\272\236/, "ss", x); gsub(/\305\277/, "s", x); gsub(/\342\204\252/, "k", x)
|
|
80
|
+
gsub(/\357\254\200/, "ff", x); gsub(/\357\254\201/, "fi", x); gsub(/\357\254\202/, "fl", x)
|
|
81
|
+
gsub(/\357\254\203/, "ffi", x); gsub(/\357\254\204/, "ffl", x); gsub(/\357\254\205|\357\254\206/, "st", x)
|
|
82
|
+
gsub(/\315\276/, ";", x); gsub(/\341\277\257/, "`", x); return x }'
|
|
83
|
+
|
|
84
|
+
# Which features are being backfilled is read from the BASE, never from the PR (E116). Read from the
|
|
85
|
+
# PR, the gate let a PR approve itself: it could set `verified: true` in the same PR, or delete the spec
|
|
86
|
+
# so the feature read as "not being backfilled". So a spec, and its approval, count once they are merged.
|
|
87
|
+
# The whole tree, folded: on a Mac checkout `Specs/backfill/Billing/spec.md` is the spec of `billing`.
|
|
88
|
+
# A symlink or submodule where the specs live hides what the spec says: at `specs/backfill/<f>` or its
|
|
89
|
+
# spec.md, <f> counts as being backfilled and unapproved; at `specs` or `specs/backfill`, the gate cannot
|
|
90
|
+
# tell which features are, so every src/<feature> change fails until one PR puts real files back (that PR
|
|
91
|
+
# changes no feature, so it passes).
|
|
92
|
+
# `tr '\n\0' '?\n'`: one record per line, and a newline inside a path becomes `?` — split on newlines
|
|
93
|
+
# alone, a file named `notes<newline>100644 blob 0<TAB>src/Billing/y` added a record nobody made (review 2).
|
|
94
|
+
base_tree="$(git ls-tree -r -z --full-tree "$BASE" | tr '\n\0' '?\n')" || {
|
|
95
|
+
echo "FAIL [backfill]: git could not read the tree of '${BASE}' — the gate cannot tell which features are being backfilled."
|
|
96
|
+
exit 1
|
|
97
|
+
}
|
|
98
|
+
# One line each: `S <oid> <path>` a spec to read, `U - <path>` a link where a spec goes, `A - <path>` a
|
|
99
|
+
# link above. A spec is read by its object id, never by its path again: with a newline written as `?`,
|
|
100
|
+
# `x?y/spec.md` and `x<newline>y/spec.md` are one path here, and reading `<base>:x?y/spec.md` twice let
|
|
101
|
+
# an approved spec stand in for an unapproved one (review 3).
|
|
102
|
+
specs="$(printf '%s\n' "$base_tree" | awk "$FOLD"'
|
|
103
|
+
{ t = index($0, "\t"); if (!t) next; p = substr($0, t + 1); m = substr($0, 1, 6); lp = fold(p); split(substr($0, 1, t - 1), h, " ") }
|
|
104
|
+
lp !~ /^specs(\/backfill(\/[^\/]+(\/spec\.md)?)?)?$/ { next }
|
|
105
|
+
m == "120000" || m == "160000" { print (lp ~ /^specs\/backfill\/[^\/]+/ ? "U - " : "A - ") p; next }
|
|
106
|
+
lp ~ /^specs\/backfill\/[^\/]+\/spec\.md$/ { print "S " h[3] " " p }
|
|
107
|
+
')"
|
|
108
|
+
blind="$(printf '%s\n' "$specs" | sed -n 's/^A - //p')"
|
|
109
|
+
# The folded names of the features being backfilled, one per line.
|
|
110
|
+
bf="$(printf '%s\n' "$specs" | sed -n 's/^[SU] [^ ]* //p' | awk "$FOLD"'{ split($0, c, "/"); print fold(c[3]) }' | sort -u)"
|
|
111
|
+
|
|
112
|
+
# No symlink, no submodule and no second spelling where a feature being backfilled lives (E116). The
|
|
113
|
+
# gate reads PATHS: with a link at `src/<feature>` (or `src`, or inside the feature) the code lives
|
|
114
|
+
# where no path under src/ names it, so every later edit to the link's target passed. Read from the tree
|
|
115
|
+
# at HEAD, on EVERY PR and before the "no src/<feature> changes" PASS — an edit to the target changes no
|
|
116
|
+
# path under src/ at all. Links elsewhere in src/ are not this gate's concern: a code repo may hold real
|
|
117
|
+
# ones. 120000 is a symlink, 160000 a submodule; `-r` lists `src` itself when `src` is the link. When
|
|
118
|
+
# the base hides its specs behind a link (`blind`), every folder under src/ is taken as backfilled here.
|
|
119
|
+
# A second spelling is two spellings of one folder at HEAD at once (`Src/billing/` beside `src/billing/`,
|
|
120
|
+
# or `src/Billing/` beside it): one folder on macOS and Windows, two on Linux CI. A lone `Src/` clashes
|
|
121
|
+
# with nothing, and is read as `src/`.
|
|
122
|
+
# Lists go to awk IN the stream, each line tagged, never through the environment: a list of every
|
|
123
|
+
# src/ folder passed as a variable hit the size limit ("Argument list too long") in a large repo and
|
|
124
|
+
# failed every PR there (review 2). `tagged T "$list"` prints `T<TAB><line>` for each line that is not
|
|
125
|
+
# empty.
|
|
126
|
+
tagged() { printf '%s\n' "$2" | awk -v t="$1" 'length($0) { print t "\t" $0 }'; }
|
|
127
|
+
head_tree="$(git ls-tree -r -z --full-tree HEAD | tr '\n\0' '?\n')" || {
|
|
128
|
+
echo "FAIL [backfill]: git could not read the tree at HEAD — the gate cannot check src/ for links."
|
|
129
|
+
exit 1
|
|
130
|
+
}
|
|
131
|
+
if [ -n "$bf" ] || [ -n "$blind" ]; then
|
|
132
|
+
bad="$({ tagged B "$bf"; printf '%s\n' "$head_tree"; } | ALL="${blind:+1}" awk "$FOLD"'
|
|
133
|
+
function once(k, s) { if (!(k in done)) { done[k] = 1; print " " s } }
|
|
134
|
+
BEGIN { all = (ENVIRON["ALL"] != "") }
|
|
135
|
+
/^B\t/ { bf[substr($0, 3)] = 1; next }
|
|
136
|
+
{ t = index($0, "\t"); if (!t) next; p = substr($0, t + 1); m = substr($0, 1, 6); lp = fold(p); ln = (m == "120000" || m == "160000") }
|
|
137
|
+
lp == "src" && ln { print " " p (m == "120000" ? " (a symlink" : " (a submodule") ", where the src/ folder goes)"; next }
|
|
138
|
+
lp !~ /^src\// { next }
|
|
139
|
+
{ split(p, c, "/"); f = fold(c[2]) }
|
|
140
|
+
!all && !(f in bf) { next }
|
|
141
|
+
{ s = c[1] "/" c[2] }
|
|
142
|
+
(f in sp) && sp[f] != s { once("f/" f "/" s, sp[f] " and " s " (one name on macOS and Windows)") }
|
|
143
|
+
!(f in sp) { sp[f] = s }
|
|
144
|
+
m == "120000" { print " " p " (symlink)"; next }
|
|
145
|
+
m == "160000" { print " " p " (submodule)"; next }
|
|
146
|
+
')"
|
|
147
|
+
if [ -n "$bad" ]; then
|
|
148
|
+
echo "FAIL [backfill]: src/ holds a symlink, a submodule or a second spelling where a feature being backfilled lives — this gate cannot see what changes behind it:"
|
|
149
|
+
printf '%s\n' "$bad"
|
|
150
|
+
echo " -> put the real files in place of each link, and keep one spelling of each folder. A PR that only"
|
|
151
|
+
echo " removes a link, or every file of one spelling, passes; until then every PR in this repo fails,"
|
|
152
|
+
echo " because an edit to the link's target changes no path under src/."
|
|
153
|
+
if [ -n "$blind" ]; then
|
|
154
|
+
echo " On the base, $(printf '%s' "$blind" | head -n 1) is a symlink or submodule, so the gate cannot tell which"
|
|
155
|
+
echo " features are being backfilled and checks EVERY folder under src/ — a link above may belong to no"
|
|
156
|
+
echo " backfilled feature. Remove it in the PR that puts the real specs back; add it back after."
|
|
157
|
+
fi
|
|
158
|
+
exit 1
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
|
|
42
162
|
# --no-renames (E114): a rename is listed by BOTH paths. Without it git names a rename by its NEW path
|
|
43
163
|
# only, so `git mv src/<feature>/x.js lib/x.js` took the file out of a feature being backfilled and the
|
|
44
164
|
# gate never saw the feature. -z | tr: NUL-separated, so git never quotes a path — a quoted one (any
|
|
45
|
-
# non-ASCII byte by default; a `"` or a tab always) never matched src/<feature>/ below.
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
|
|
165
|
+
# non-ASCII byte by default; a `"` or a tab always) never matched src/<feature>/ below. --raw gives each
|
|
166
|
+
# path after a record with its modes and status; with --no-renames every change has exactly one path.
|
|
167
|
+
# The two are paired here, NUL by NUL, into one line — `<modes and status><TAB><path>` — and a newline
|
|
168
|
+
# inside a path becomes `?`: split into lines first, one file named `a<newline>b` put every later header
|
|
169
|
+
# where its path should be, and every src/<feature> change after it was never seen (review 1).
|
|
170
|
+
changed="$(git diff --no-renames --raw -z "${BASE}..HEAD" | while IFS= read -r -d '' h && IFS= read -r -d '' p; do
|
|
171
|
+
printf '%s\t%s\n' "$h" "${p//$'\n'/?}"
|
|
172
|
+
done)" || {
|
|
173
|
+
echo "FAIL [backfill]: git could not list the changes between '${BASE}' and HEAD."
|
|
174
|
+
exit 1
|
|
175
|
+
}
|
|
176
|
+
# The spellings of each src/<feature> folder, on the base and at HEAD: `<top>/<feature>`, once each.
|
|
177
|
+
spellings() {
|
|
178
|
+
awk "$FOLD"'{ t = index($0, "\t"); if (!t) next; p = substr($0, t + 1); if (fold(p) !~ /^src\/[^\/]+\//) next
|
|
179
|
+
split(p, c, "/"); s = c[1] "/" c[2]; if (!(s in seen)) { seen[s] = 1; print s } }'
|
|
180
|
+
}
|
|
181
|
+
base_sp="$(printf '%s\n' "$base_tree" | spellings)"
|
|
182
|
+
head_sp="$(printf '%s\n' "$head_tree" | spellings)"
|
|
183
|
+
# Feature = a directory under src/ (src/<feature>/...), folded (E116). A path that IS `src/<feature>` — a
|
|
184
|
+
# link, a submodule or a file put where the folder goes — is that feature too. Top-level src/*.js files
|
|
185
|
+
# are deliberately NOT gated here (they belong to no single feature), so a path that IS `src/<name>`
|
|
186
|
+
# counts only as a link or a submodule (either side), or when <name> is being backfilled. A deleted
|
|
187
|
+
# symlink or submodule is not a change to the feature: removing the link is how a repo gets out of the
|
|
188
|
+
# refusal above, and the spec's approval may itself be waiting on that PR. For the same reason a file
|
|
189
|
+
# deleted from one spelling of a folder the base held in two, when that spelling is gone at HEAD, is not
|
|
190
|
+
# a change either (review 1): without it, neither removing the twin nor approving the spec could pass.
|
|
191
|
+
# Only while ANOTHER spelling of that folder is still there at HEAD (review 2): deleting both spellings
|
|
192
|
+
# deletes the feature, and that is a change to it. (A diff header starts with `:`, so the tags below
|
|
193
|
+
# cannot be mistaken for one.)
|
|
194
|
+
feats="$({ tagged B "$bf"; tagged S "$base_sp"; tagged H "$head_sp"; printf '%s\n' "$changed"; } | awk "$FOLD"'
|
|
195
|
+
/^B\t/ { bf[substr($0, 3)] = 1; next }
|
|
196
|
+
/^S\t/ { cnt[fold(substr($0, 3))]++; next }
|
|
197
|
+
/^H\t/ { here[substr($0, 3)] = 1; still[fold(substr($0, 3))] = 1; next }
|
|
198
|
+
{ t = index($0, "\t"); if (!t) next; h = substr($0, 1, t - 1); p = substr($0, t + 1); lp = fold(p) }
|
|
199
|
+
lp !~ /^src\/./ { next }
|
|
200
|
+
{ split(h, w, " "); om = substr(w[1], 2); nm = w[2]; ln = (om ~ /^1[26]0000$/ || nm ~ /^1[26]0000$/) }
|
|
201
|
+
w[5] == "D" && ln { next }
|
|
202
|
+
{ k = split(p, c, "/"); f = fold(c[2]); s = c[1] "/" c[2] }
|
|
203
|
+
w[5] == "D" && k > 2 && cnt[fold(s)] > 1 && !(s in here) && (fold(s) in still) { next }
|
|
204
|
+
k > 2 || ln || (f in bf) { print f }
|
|
205
|
+
' | sort -u)"
|
|
50
206
|
|
|
51
207
|
if [ -z "$feats" ]; then
|
|
52
208
|
echo "PASS [backfill]: no src/<feature> changes."
|
|
@@ -56,18 +212,45 @@ fi
|
|
|
56
212
|
rc=0
|
|
57
213
|
while IFS= read -r f; do
|
|
58
214
|
[ -z "$f" ] && continue
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# contains "verified: true" cannot false-pass the gate.
|
|
63
|
-
fm="$(awk 'NR==1 && /^---[[:space:]]*$/ {f=1; next} f && /^---[[:space:]]*$/ {exit} f {print}' "$spec")"
|
|
64
|
-
if printf '%s\n' "$fm" | grep -qiE '^verified:[[:space:]]*true[[:space:]]*$'; then
|
|
65
|
-
echo "PASS [backfill]: ${f} has an approved (verified) backfill spec."
|
|
66
|
-
else
|
|
67
|
-
echo "FAIL [backfill]: ${f} is being backfilled but its spec is not yet human-approved (verified: true)."
|
|
68
|
-
echo " -> run yad-backfill approve for ${spec} before changing this feature."
|
|
215
|
+
if [ -n "$blind" ]; then
|
|
216
|
+
echo "FAIL [backfill]: cannot tell whether ${f} is being backfilled — on the base, $(printf '%s' "$blind" | head -n 1) is a symlink or submodule, so the specs behind it are not read."
|
|
217
|
+
echo " -> put the real specs/backfill/ folder back in its own PR first."
|
|
69
218
|
rc=1
|
|
219
|
+
continue
|
|
70
220
|
fi
|
|
221
|
+
mine="$(printf '%s\n' "$specs" | F="$f" awk "$FOLD"'/^[SU] / { r = substr($0, 3); split(substr(r, index(r, " ") + 1), c, "/"); if (fold(c[3]) == ENVIRON["F"]) print }')"
|
|
222
|
+
if [ -z "$mine" ]; then
|
|
223
|
+
echo "note [backfill]: ${f} is not being backfilled (no specs/backfill/${f}/spec.md on the base) — skipped."
|
|
224
|
+
continue
|
|
225
|
+
fi
|
|
226
|
+
ok=1
|
|
227
|
+
while IFS= read -r l; do
|
|
228
|
+
rest="${l#? }"; oid="${rest%% *}"; spec="${rest#* }"
|
|
229
|
+
case "$l" in
|
|
230
|
+
U\ *) echo "FAIL [backfill]: ${f} is being backfilled, but on the base ${spec} is a symlink or submodule — its spec is not read."
|
|
231
|
+
echo " -> put the real spec there in its own PR first."
|
|
232
|
+
ok=0; continue ;;
|
|
233
|
+
esac
|
|
234
|
+
# Read ONLY the YAML frontmatter (between the first two --- lines) so a prose line that merely
|
|
235
|
+
# contains "verified: true" cannot false-pass the gate — and only once it closes: a spec that opens
|
|
236
|
+
# `---` and never closes it has no frontmatter, or a body line would count (review 5). Read to the end,
|
|
237
|
+
# never stopped early: under pipefail a closed pipe would read a long approved spec as unapproved.
|
|
238
|
+
fm="$(git cat-file blob "$oid" 2>/dev/null | awk 'NR==1 && /^---[[:space:]]*$/ {f=1; next} f==1 && /^---[[:space:]]*$/ {f=2; next} f==1 {b = b $0 "\n"} END {if (f == 2) printf "%s", b}')" || fm=""
|
|
239
|
+
# The LAST line that could set the key, as YAML reads a repeated key (review 6): `true` then `false`
|
|
240
|
+
# is false. Any spelling YAML might take for it — quoted, a space before the colon, another case —
|
|
241
|
+
# is found (review 7), and the last one must be exactly `verified: true` (the key in lowercase, as
|
|
242
|
+
# YAML keys are case-sensitive; the value as YAML 1.1 reads it). Anything else is not approved.
|
|
243
|
+
last="$(printf '%s\n' "$fm" | grep -iE "^[\"']?verified[\"']?[[:space:]]*:" | tail -n 1)" || last=""
|
|
244
|
+
if ! printf '%s\n' "$last" | grep -qE '^verified:[[:space:]]*(true|True|TRUE)[[:space:]]*$'; then
|
|
245
|
+
echo "FAIL [backfill]: ${f} is being backfilled but its spec is not yet human-approved (verified: true) on the base."
|
|
246
|
+
echo " -> run yad-backfill approve for ${spec} and merge that first: the gate reads the spec"
|
|
247
|
+
echo " as it stands on the base, so an approval in this same PR does not count."
|
|
248
|
+
ok=0
|
|
249
|
+
fi
|
|
250
|
+
done <<EOF
|
|
251
|
+
$mine
|
|
252
|
+
EOF
|
|
253
|
+
if [ "$ok" = 1 ]; then echo "PASS [backfill]: ${f} has an approved (verified) backfill spec."; else rc=1; fi
|
|
71
254
|
done <<EOF
|
|
72
255
|
$feats
|
|
73
256
|
EOF
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-change
|
|
3
|
-
description: 'Phase 6 post-lock change management — the change-request/defect INTAKE + TRIAGE step of a feature thread. After the contract locks and code ships, a change must NOT mutate a locked artifact; it becomes a NEW epic threaded to its parent.
|
|
3
|
+
description: 'Phase 6 post-lock change management — the change-request/defect INTAKE + TRIAGE step of a feature thread. After the contract locks and code ships, a change must NOT mutate a locked artifact; it becomes a NEW epic threaded to its parent. Classifies the change DEPTH (defect-fix / behavioral-no-surface / contract-surface / new-capability), seeds that EP-<slug> change-epic (lineage frontmatter kind+type/parent/thread/inherits/supersedes; `yad epic new --parent` writes a state.json that carries inherited steps by reference and runs only the changed ones, plus a pointer-lock contract-lock.json when architecture is inherited), and records the intake in change.json (escape_stage + root_cause for defects). For hotfixes it records the ship-first exception and opens reconcile-debt.json. Never auto-advances — hands off to the authoring skills + the team review gate. Use when the user says "log a change request", "file a defect", "thread a change off EP-…", "open a hotfix", or after a shipped feature needs a fix.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Change/Defect Intake + Triage (Phase 6, the entry of a feature thread)
|