Conversation
…ocation (#340) Moves block-wip-register-before-execute.sh beside its .py in wip/resources/ and makes it fail open when either python3 or the implementation is absent - the split pair had been hard-blocking every Edit and Write. Repoints three guards' hangul-patterns.regex lookup at hook-kit/data/, and corrects three cleanup-guard registrations in hooks.json that still pointed at pre-move hook-kit paths (a missing script exits 127, which reads as 'no objection'). Adds a bats case asserting every registered command resolves on disk - those same three paths had already been corrected three times and reverted twice.
…ivergence) Add a Don't/Do row for the git merge-tree --write-tree stage-numbering gotcha (1=merge-base, 2=ours, 3=theirs -- easy to invert) and a new section walking through how to tell base staleness apart from genuine parallel divergence before assuming a base switch will fix a conflict, including the git cherry -v straggler check for when a conflicting commit's content is already partially superseded by a separate merged commit.
…ed deletion The Completed-item RAG sync + delete procedure conditioned deletion only on RAG-sync success, with zero awareness of Plane as canonical backlog. This left the deletion direction of the canonical-medium principle unguarded -- only the completion-flip direction (run.md Step 0) and the creation direction (fix-plan/add.md, added after a prior data-loss recurrence) were covered. Add Step 1.5 to the delete procedure: for any Plane-indexed item, verify/complete the Plane issue (or register via intake if missing) before its local text is removed, since RAG-sync preserves search but not canonical-record completeness.
fix(git-repo): document conflict root-cause diagnosis (staleness vs divergence)
`cwd_match` tokens are written as path fragments ("ghq/github.com/<org>"),
but detect_workspace compared each token against single path components. No
multi-segment token could ever match, so every workspace resolved to
"default" — handing consumers the placeholder endpoints (localhost qdrant,
empty plane host, collection "wiki") instead of the configured ones. The
failure was silent: "default" is a legitimate return value, so nothing
surfaced the mismatch.
Split tokens on "/" and compare segment sequences, which handles bare
components and path fragments alike. Segments are still compared whole, so a
token cannot match a longer component that merely contains it — a regression
test pins that property alongside the fix.
Verified against the live config: the previously-misresolved workspace now
returns its real endpoint and collections.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Following the marketplace symlink lands inside a working checkout, which in this workspace routinely holds several git worktrees. Each carries its own copy of every skill, so one skill name matches N+1 directories and `head -1` picks arbitrarily among them - `cleanup` alone matched seven. The consequence is silent: the hook still emits a valid path, so nothing looks wrong, but it can name a worktree's in-progress copy instead of the installed one. Observed in a single session pointing five times at a cleanup/run.md that was two lines behind the live file. Prunes .worktrees / worktrees / .git from the search rather than trying to rank matches - a worktree is an in-progress branch by definition and is never the installed copy, so there is no case where preferring one is correct.
…-guard Editing a plan or research file fires two PostToolUse skill-triggers at once, this skill's and vibe-coding's, and both demand the same undecided-item scan: find unresolved markers, turn each into a question, write the answers back. Nothing said they were the same obligation, so honouring one read as skipping the other. Names them as one axis with two entry points - satisfying either is enough, and invoking both doubles the skill load per edit for no extra check.
fix(hook-kit): stop topic dispatch from resolving into nested worktrees
…k to v1
The v2 config names roles ("rag", "backlog", "wiki", "checklist") and carries
the vendor in a `kind` field, so swapping a vendor becomes a one-line config
edit. Consumers of this module still read the v1 flat vendor-named keys, so v2
is translated down to them on load rather than pushed onto every caller.
v2 wins when present; v1 stays readable so an unmigrated machine keeps working
unchanged during the migration window.
Two translation details are load-bearing:
- `kind: "none"` means the role is not configured for that workspace, and must
surface as an empty value. Letting it fall through to DEFAULT_PROFILE would
make "unconfigured" and "configured, pointing at localhost" indistinguishable
downstream — the exact ambiguity the role schema exists to remove.
- `workspace_name` is carried explicitly. DEFAULT_PROFILE already holds
"default" for that key, so get_profile's setdefault cannot correct it; v1
configs hid this by naming workspace_name in every profile. Without it the
report claims "default" while serving a matched profile's real endpoints.
Verified against the live config: both workspaces resolve to their own name,
endpoints and collections, and tracker_root now comes from the configured
checklist path instead of a filesystem guess.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…check on receiver presence Add workspace-config.sh, a single resolver every hook can source to learn which receivers (checklist/backlog/rag/wiki) the current workspace is wired to. Roles carry a `kind` discriminator so a vendor swap is a one-line config change, and `kind: "none"` makes "not configured" a first-class state. It reads the v2 role-shaped config, falls back to the v1 flat config, and degrades to all-"none" (never blocks) on missing file, malformed JSON, or no usable interpreter. Wire check-session-rag.sh to it: a workspace with no RAG receiver now skips the store demand instead of blocking Stop — resolving a false block observed on a session whose workspace had no RAG MCP server connected at all. The literal override phrases stay for muscle memory; a config-derived "skip <kind> store" phrase is added so the escape hatch survives a vendor change. Cover the shim with test-workspace-config.sh (19 cases: profile match, role->kind indirection, collection flatten, multi-segment token match, default fallback, missing-config degrade).
… v2 config load_user_config() reads CONFIG_FILE_V2 (~/.config/agent-workspace/config.json) before CONFIG_FILE, but the isolated_workspace fixture only patched CONFIG_FILE. On a machine that has a real v2 config the fixture was shadowed, the throwaway workspace resolved to the wrong (default) profile, and test_resolve_profile_reaches_workspace_config failed with an empty plane_host. Neutralise CONFIG_FILE_V2 in the fixture so the loader falls through to the fixture config, restoring per-workspace isolation for the whole test module.
…obe interpreter in RAG guard Review follow-up (PR #345 — CodeRabbit + internal review). 1. v2 was detected per-profile via a `roles` key. A v2 profile that defines only `match` and relies on top-level `defaults` carries no `roles`, so the whole config was read as v1 and `v1_to_roles` overwrote its configured checklist (and, in workspace_profile.py, every default receiver). Detect v2 by the top-level `version == 2` (keeping the roles heuristic as a fallback for version-less configs) and translate every v2 profile, no-roles included. Applied symmetrically to workspace_profile.py and workspace-config.sh. 2. check-session-rag.sh ran its transcript scan with a bare `python3`, skipping the Windows-safe interpreter probe the shim already uses. On the MS Store stub (exit 49) the scan yielded empty metrics and the guard silently no-oped. Reuse the probe; no usable interpreter -> skip (fail-safe), as before. 3. Regression tests: a no-roles v2 profile keeps its top-level default receivers (bash T20-T22, python T16-T17). Both fail on the old detection.
Three hook commands in hooks.json still pointed at skills/hook-kit/resources/ for scripts that were relocated to skills/cleanup/resources/ during the PR #330 migration, causing them to fail with exit 127 (ghost registration): block-cleanup-without-rag.sh, block-cleanup-without-claudify.sh, and block-cleanup-option-below-context-gate.sh. This is hooks.json's first commit to git (previously untracked at HEAD on main). Full 66-entry audit recorded in .agents/docs/generated/research-claude-hooks-audit.md.
…session-end RAG receiver gate Phase 1: v2 role-shaped config + workspace-config.sh resolver + check-session-rag.sh receiver-presence gate. Review findings 1·2·3 fixed (13042e6); 4·5 deferred (Minor polish).
…e and inject WAF-safe User-Agent
Cloudflare 403s the default Python-urllib User-Agent on plane.es6.kr;
adopt the browser-like UA already proven by plane_create_comment.py in
plane_create_issue.py (REST path) and plane_client.py (all API calls).
The embedded Django-shell f-string moves into build_k3s_py_script(),
keeping json.dumps parameter injection and doubled braces (the fix that
previously landed only in this copy), correcting a mismatched </h{level}>
closing tag to </h{indent}>, resolving the kubectl namespace dynamically
(profile k3s_namespace -> plane-ce, replacing the stale -n plane
hardcode), and skipping the fallback gracefully when kubectl is absent.
…acklog copy and inject User-Agent into plane_sync The fix-plan copy still carried the K3s fallback f-string defect (ValueError: Invalid format specifier on single braces) that the plane-backlog copy had already fixed — reconcile makes the two copies byte-identical again, adopting the repaired template plus the shared UA/namespace changes. plane_sync.py gains the same WAF-safe User-Agent on all Plane REST calls.
Covers the two defect classes end to end: outbound User-Agent capture for plane_create_issue (both copies), plane_sync and plane_client; K3s fallback template build+parse with hostile quotes/braces; namespace resolution (profile override -> plane-ce default); graceful skip without kubectl; and a byte-identity guard against dual-copy drift.
fix: repair plane script defects — WAF-safe User-Agent + K3s fallback template
A refactor commit is a behaviour-preserving restructure (e.g. the claude-session -> session skill rename) and drives no release bump, so it needs no same-skill feat/fix rider context. Accept it as a primary tag on both next-feat and next-fix instead of rejecting it as an unsupported tag.
The hooks.json registration for this script landed in an earlier commit of this PR, but the script itself was never committed — the hooks.json registration-integrity test rightly fails on a reference that would exit 127 at runtime. Adds the authored script (PreToolUse gate denying squash-merge recommendations for multi-commit es6kr/skills PRs).
fix(hooks): correct ghost path for block-wip-register-before-execute.sh
…eue, not to done An audit_status: approved_by_* transition means the audit STAGE is complete but the ITEM is not — implementation remains. The relocation HARD STOP now targets the tracker's implementation-queue section (e.g. TODO) instead of a priority/execution section, forbids flipping the item to [x] or harvesting it to Completed at audit time, and completion-criteria carries a cross-note exempting audit-stage items from the generic deliverable-exists -> done rule.
…up decision A below-threshold reading taken earlier in a long tool-call chain is stale-LOW — a full /fix flow can grow usage tens of points past it. ask-gates gains the stale-LOW direction (re-measure immediately before deciding and before composing the ask); suggestion-patterns states the per-model live thresholds (Fable/Mythos 55, Opus 50, others 45 as the generic fallback) and makes the cleanup option REQUIRED as the Recommended #1 when a fresh reading is at/above the threshold.
…next call Step 4's mandatory Skill(next) call now re-measures live context usage first — if the fresh reading is at/above the session model's threshold (or a CLEANUP-GATE directive fired), the turn-final ask leads with a Recommended cleanup option citing the live percentage instead of composing next-action candidates on a stale-LOW number.
…when findings were fixed (#348) Step 8's finding-first ordering already sequences finding-handling before the merge ask, but it stopped at "fix -> reflected in branch". When the fix lands this session, the Step 7 AI Review Summary still lists those findings as open/Valid, so recommending merge on top of it is self-contradictory — the reader sees unresolved findings while the option says merge, and the merge-attestation URL points at a pre-fix record. The PATCH-after-fix rule existed only in post.md's "Single Summary preservation guard" (a duplicate- comment guard), invisible at the Step 8 decision point. Add a HARD STOP gate to next.md Step 8: after a finding-handling answer that applies fixes, PATCH the existing Summary to mark them Resolved (+ fixing SHA) per post.md's procedure BEFORE composing the merge ask. Don't/Do table + self-check included.
…#350) Pruning worktrees was necessary but not sufficient. Several marketplaces can each ship a skill of the same name (consolidate, github-flow, code-quality), and every one of those is a legitimate install — no prune rule separates them. `head -1` then picked by directory-walk order, which answered Skill("es6kr:consolidate") with dgs-plugins/.claude/skills/consolidate/post.md and Skill("es6kr:github-flow") with dgs-plugins/skills/github-flow/merge.md, even though both calls named their plugin. The prefix was already in hand and being discarded. A plugin name is declared by exactly one marketplace manifest, so resolving the prefix back to that marketplace narrows the search to the right tree. Without a prefix there is nothing to narrow by and the search stays global — but then, when more than one candidate survives, the reminder now lists them instead of presenting an arbitrary pick as the answer. A wrong path here is silent precisely because it still resolves to a real SKILL.md. Verified against the base (which already carries the worktree prune), so the delta below is this change alone: es6kr:consolidate dgs-plugins/.claude/... -> es6kr-skills/skills/... es6kr:github-flow dgs-plugins/skills/... -> es6kr-skills/skills/... es6kr:cleanup / :next / :wip unchanged (already correct) Fail-open boundaries unchanged: unknown skill, non-Skill tool, and missing jq all still exit 0. Prefix-less calls still resolve, now with the ambiguity note. Uses head/grep rather than mapfile so it keeps working where `env bash` is 3.2.
…#351) A workflow_run trigger's `branches:` filter matches the HEAD branch of the run that fired, not the base of its PR. A PR into next-feat/next-fix runs its checks on the feature branch (fix/…, feat/…), so filtering on [next-feat, next-fix] never matched the PRs this workflow exists to serve. It matched only promotion PRs (head next-*, base main) — the ones it should leave alone. All four historical runs were that misfire: PR_NUMBER 336, 346, 347. The payload does carry the base (pull_requests[0].base.ref, verified against live runs), so drop the filter and gate on base in the job. Also drop `Test` from the trigger list: it fires on `pull_request: branches: [main]`, so it never runs for a PR into next-feat/next-fix, and waiting on it meant waiting for something that could not arrive. The token limit is left as-is but no longer fails the run. GITHUB_TOKEN cannot perform markPullRequestReadyForReview even with pull-requests: write, and a red X on this workflow reads as "the checks failed", which is not what happened. It now warns and explains instead.
…h and claude-code rewind
fix(fix-plan,plane-backlog): add fix_plan P0-P3 <-> Plane native priority mapping
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ry POST
The AI Review Summary is the audit trail a merge is read against, but a
consolidate run can write specifics it never verified: a `commit <sha>` that
does not exist, or "Copilot: N findings" inflated above the reviewer's actual
comment count. A reader (or a future session) then approves a merge on a
fabricated basis.
Add a PreToolUse guard that, before any consolidate-provenance comment POST
(receiving-code-review / requesting-code-review / consolidate:verified marker),
verifies both mechanically:
1. every cited `commit <sha>` resolves (local git cat-file first, else gh api
commits; only a definite 404/422 marks it fabricated)
2. any "Copilot: N findings" claim reconciles with the PR's actual Copilot
review-comment count (gh api pulls/<N>/comments)
Both fail open on ambiguity (no gh / offline / unreadable body / a SHA on an
unfetched branch), with ALLOW_SUMMARY_FABRICATED_CLAIMS=1 as the per-command
override. Registered in hooks.json beside block-noncompliant-review-comment.
Document the same discipline as a HARD STOP in consolidate/post.md, and add
tests/test_consolidate_sha_guard.bats (9 cases, offline via a gh mock).
…fter cleanup The cleanup-adjacent silent-skip in Step 0.66 is a single, one-time use right after /cleanup. Clarify that every subsequent Stop-hook re-fire (including during a polling wait for external/user action) falls back to the minimal single-item confirmation Exception -- a bare status report with no AskUserQuestion call is never correct there, regardless of how many times the hook has already fired.
…cation debug logging - Update check-completed-bloat.js with ^-\s regex anchor to ignore sub-bullets - Add debug trace logging and trailing question mark check in check-ask-bypass-keywords.sh - Update hooks.json cleanup guard paths - Add block-squash-recommend-multi-commit.sh guard script
hooks.json invokes block-manual-handoff-web-task.sh and block-taskoutput-long-block.sh by direct path (not `bash <path>`), so Claude Code exec's them relying on the shebang + executable bit. Both were committed as mode 100644, causing permission-denied failures at hook time. Also ignore stray *.debug.log scratch output under hook-kit/resources/.
…guard - Require matching clickable PR URL for each distinct PR number referenced in AskUserQuestion payloads - Update conversation-id.md documentation to reflect PR-URL gate validation
Reuse-first was written as an unconditional obligation, but what reuse buys back is a dependency install / build cache — not the worktree directory. In a repo with no dependency manifest a replacement worktree is a plain checkout, so following the rule there produces the wrong answer: it recommends renaming a branch whose name encodes planned intent in order to save a second. Add a cost gate to the decision tree — manifest/lockfile presence classifies the repo as heavy or lightweight — and scope the reuse-first obligation, the AskUserQuestion ordering mandate, and the inventory requirement to heavy repos. In a lightweight repo new-create is the recommended option, and a finished worktree may be offered for removal at any completion stage (push, PR open, or merge) rather than held as a reuse candidate. Also correct the count-limit matrix rationale, which cited worktree creation mechanics rather than the dependency cost that actually motivates reuse, and split the matrix by repo weight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ports scripts/verify-hooks-json.py + its test suite from es6kr/claude-plugins (PR #23, merged: es6kr/claude-plugins#23) — that repo hit the exact class this repo independently hit earlier this session (hooks.json#L43: a hook relocation commit updated the script's new location but left the old registration path in place, so the guard stayed listed while silently enforcing nothing). Neither failure mode has any other signal — a ghost hook dies with exit 127, which the harness can't distinguish from "ran, raised no objection". - scripts/verify-hooks-json.py: detects (1) duplicate (event, matcher, script-basename) registrations and (2) ${CLAUDE_PLUGIN_ROOT}-relative paths with no file behind them. Handles quoted/unquoted, braced/unbraced, and interpreter-prefixed command shapes. - tests/test_verify_hooks_json.py: 13 tests (ported + translated from Korean — this repo is PUBLIC/English-only), covering both failure classes, path extraction edge cases (spaces, shell metacharacters), and the duplicate+ghost co-occurrence case. - .github/workflows/test.yml: new `hooks-json-lint` job. Verified locally against the real hooks/hooks.json (66 registrations, 0 skipped, 0 errors) — confirms this session's earlier ghost-path fix (commit 7724739) held. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… runtime artifacts
… Step 0 The Step 0 recency check unconditionally re-asked the scope question on every invocation whenever a recent-completion marker existed, even when the user had already answered the same ask earlier the same day. Adds a "Recency-ask answer reuse" HARD STOP: reuse a same-day answer's pattern instead of re-asking; treat an explicit role-flagged re-invocation after a completed same-role run as the scope answer itself (skip duplicated steps, report, proceed to the role's remaining actionable work); re-ask only on plausibly-moved external state or a conflicting explicit scope argument.
check-session-rag.sh counted only MCP tool calls (mcp__*__*-store/-find) while its DENY guidance also named only the MCP medium — a session whose MCP binding was absent got told the store was impossible even though the vendor script route (qdrant-import.py / qdrant-search.py) works without MCP and edit-guard.sh already accepts it. Adds script_store_re / script_find_re Bash-command counting (mirroring edit-guard's vendor_pat) and extends the DENY text to name the script route per the tool-priority rule (skill script -> CLI -> HTTP -> MCP).
…e schema validation
…served in cleanup
fix: accumulate 16 patch-level bug fixes and guard enhancements across skills
…tent titles (#361) rewind-session.py had two nitpicks surfaced while reviewing the --claude-code addition: - main()'s antigravity branch called rewind_antigravity_db() then sys.exit(0) unconditionally, discarding its True/False return, so a failed rewind (e.g. a missing DB) reported success. Capture the return and exit 0/1, matching the --claude-code branch's own success propagation. - list_claude_sessions() read a user message's content as a string; when it is a content-block list the slice raised AttributeError (caught, leaving "(No Title)"). Join the blocks' text so array-content sessions get a title. Verified: antigravity rewind against a missing DB now exits 1; --list-sessions claude-code extracts titles from both string and array content.
…e, not hardcoded main (#362) Step 6's Branch guard asserted "the es6kr/skills repo flow is feat/fix branch -> PR -> main", baking one workspace's release convention into a shared skill. The PR base is a per-workspace customization: works-config v0.2.0 models it as the `staging` role (kind "branch" with next_fix/next_feat/main, or "none"). Rewrite the guard to resolve the base from that role instead of a fixed branch: two-tier staging routes fix/* -> next_fix and feat/* -> next_feat (promoted to the release branch separately); no staging layer routes feat/fix to the default integration branch. Reference the schema concept only -- the WSCFG_STAGING_* resolver export is a later works-config phase, so no shell binding is asserted.
- .githooks/pre-push: skip delete refs with `continue` (not `exit 0`) so a mixed delete + 'local' push still reaches the local-branch guard; heavy CI is skipped only for a delete-only push (HAS_REAL_PUSH).
- skills/fix/scripts/detect-agent-env.sh: header now lists every emitted value (antigravity-agent / antigravity-ide / vscode were undocumented).
- tests/test_structure.bats: duplicate-basename scan also globs *.js (hooks.json registers .js hooks).
- consolidate block-summary-fabricated-claims.sh + post.md: Copilot count uses a case-insensitive contains-match test("copilot";"i") — robust across the inline `Copilot` login and the `copilot-pull-request-reviewer[bot]` review author.
Replaces the hardcoded personal fix_plan path and internal Plane workspace URL with the shared workspace_profile abstraction (plane_host / plane_token_env / tracker_root), deriving the workspace slug from the first Plane issue URL in fix_plan.md (--workspace-slug overrides). Imports the P0-P3 priority mapping from the sibling plane_sync.py instead of keeping a third independent copy (plane-backlog mapping policy), and merges repeated [IDENT-seq] index lines incrementally so a later line no longer drops fields already collected (priority/date/is_done). PR #363 review findings: Copilot inline 1 = CodeRabbit M19, M21, M22. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
INDEX_LINE_RE only matched the Unicode arrow, while test_matches_ascii_arrow_delimiter (added on this branch's base) asserts the documented ASCII "->" form also parses -- the test failed against the shipped regex but was invisible to CI, whose pytest job collects tests/ only. Adds the (?:UNICODE|ASCII) alternation; the scripts-dir suite now passes by direct execution. PR #363 review finding: CodeRabbit M20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The checker iterated only the outer entries of the installed nested
schema ({event: [{matcher, hooks: [{type, command}]}]}), found no
command key there, and silently skipped every registered hook; it also
resolved the first token of interpreter-prefixed commands, so
"python3 /path/hook.sh" existence-checked the interpreter instead of
the script. Adds iter_hook_commands (both flat and nested schemas) and
resolve_script_operand (shlex tokenization, skipping interpreters,
env-assignment prefixes and flags), skips unresolvable ${VAR} paths,
and adds a CI-visible fixture test under tests/.
PR #363 review finding: CodeRabbit M2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "Decide foreground vs background BEFORE spawning" HARD STOP block (guidance + Don't/Do table + self-check + footnote) was committed twice verbatim, back to back. Keeps a single copy so future edits cannot drift between two identical-looking blocks. PR #363 review finding: CodeRabbit M17. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix: resolve PR #363 audit Pending findings (plane_bulk_update profile, hook checker schema, regex, dedup)
fix: promote next-fix batch (consolidate fabrication guard, session rewind, config-driven PR base)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Brings
next-featup to date withmain(30 commits behind, 3 ahead).Why
next-feathas drifted far enough frommainthat it can no longer serve as a usable base. Measured on this branch: cherry-picking a 12-commitfeatbundle onto it conflicts at the 3rd commit, because the files those commits were authored against are ~30 commits newer than the copies onnext-feat.The repo's own rule forbids pushing to
next-featdirectly, so the sync has to arrive as a PR. This is that PR — it carries no new work of its own.Scope
main's existing commits. No new commits authored for this PR.next-feat(Agent Plugins Specification v1.0.0 adoption + conformance linter, and a marketplace.json source-directory check) are preserved by the merge.Follow-up
Once this merges, the
feathalf of the accumulated-work backlog gets its own PR on top of the refreshed base. Thefixhalf is already open at #366.Test plan
git rev-list --count origin/next-feat..origin/mainreports 0